From b691c43c44ff180d10e7d4a9afc83b98551ff586 Mon Sep 17 00:00:00 2001 From: daoge_cmd <3523206925@qq.com> Date: Sun, 1 Mar 2026 12:16:08 +0800 Subject: Initial commit --- Minecraft.World/AABB.cpp | 342 ++ Minecraft.World/AABB.h | 65 + Minecraft.World/Abilities.cpp | 77 + Minecraft.World/Abilities.h | 32 + Minecraft.World/AbstractContainerMenu.cpp | 537 +++ Minecraft.World/AbstractContainerMenu.h | 88 + Minecraft.World/Achievement.cpp | 82 + Minecraft.World/Achievement.h | 38 + Minecraft.World/Achievements.cpp | 185 + Minecraft.World/Achievements.h | 92 + Minecraft.World/AddEntityPacket.cpp | 111 + Minecraft.World/AddEntityPacket.h | 58 + Minecraft.World/AddExperienceOrbPacket.cpp | 51 + Minecraft.World/AddExperienceOrbPacket.h | 24 + Minecraft.World/AddGlobalEntityPacket.cpp | 65 + Minecraft.World/AddGlobalEntityPacket.h | 25 + Minecraft.World/AddIslandLayer.cpp | 64 + Minecraft.World/AddIslandLayer.h | 11 + Minecraft.World/AddMobPacket.cpp | 140 + Minecraft.World/AddMobPacket.h | 37 + Minecraft.World/AddMushroomIslandLayer.cpp | 41 + Minecraft.World/AddMushroomIslandLayer.h | 9 + Minecraft.World/AddPaintingPacket.cpp | 58 + Minecraft.World/AddPaintingPacket.h | 27 + Minecraft.World/AddPlayerPacket.cpp | 142 + Minecraft.World/AddPlayerPacket.h | 43 + Minecraft.World/AddSnowLayer.cpp | 39 + Minecraft.World/AddSnowLayer.h | 9 + Minecraft.World/AdminLogCommand.h | 13 + Minecraft.World/AgableMob.cpp | 131 + Minecraft.World/AgableMob.h | 34 + Minecraft.World/AirTile.cpp | 6 + Minecraft.World/AirTile.h | 11 + Minecraft.World/Animal.cpp | 458 ++ Minecraft.World/Animal.h | 80 + Minecraft.World/AnimatePacket.cpp | 42 + Minecraft.World/AnimatePacket.h | 31 + Minecraft.World/AnvilTile.cpp | 117 + Minecraft.World/AnvilTile.h | 50 + Minecraft.World/AnvilTileItem.cpp | 18 + Minecraft.World/AnvilTileItem.h | 13 + Minecraft.World/ArmorDyeRecipe.cpp | 240 + Minecraft.World/ArmorDyeRecipe.h | 23 + Minecraft.World/ArmorItem.cpp | 229 + Minecraft.World/ArmorItem.h | 90 + Minecraft.World/ArmorRecipes.cpp | 158 + Minecraft.World/ArmorRecipes.h | 34 + Minecraft.World/ArmorSlot.cpp | 59 + Minecraft.World/ArmorSlot.h | 24 + Minecraft.World/ArrayWithLength.h | 115 + Minecraft.World/Arrays.h | 23 + Minecraft.World/Arrow.cpp | 531 +++ Minecraft.World/Arrow.h | 78 + Minecraft.World/ArrowAttackGoal.cpp | 91 + Minecraft.World/ArrowAttackGoal.h | 33 + Minecraft.World/ArrowDamageEnchantment.cpp | 22 + Minecraft.World/ArrowDamageEnchantment.h | 13 + Minecraft.World/ArrowFireEnchantment.cpp | 22 + Minecraft.World/ArrowFireEnchantment.h | 13 + Minecraft.World/ArrowInfiniteEnchantment.cpp | 22 + Minecraft.World/ArrowInfiniteEnchantment.h | 13 + Minecraft.World/ArrowKnockbackEnchantment.cpp | 22 + Minecraft.World/ArrowKnockbackEnchantment.h | 13 + Minecraft.World/AuxDataTileItem.cpp | 21 + Minecraft.World/AuxDataTileItem.h | 17 + Minecraft.World/AvoidPlayerGoal.cpp | 85 + Minecraft.World/AvoidPlayerGoal.h | 29 + Minecraft.World/AwardStatPacket.cpp | 89 + Minecraft.World/AwardStatPacket.h | 34 + Minecraft.World/BasicTree.cpp | 565 +++ Minecraft.World/BasicTree.h | 70 + Minecraft.World/BasicTypeContainers.cpp | 20 + Minecraft.World/BasicTypeContainers.h | 75 + Minecraft.World/BeachBiome.cpp | 18 + Minecraft.World/BeachBiome.h | 9 + Minecraft.World/BedItem.cpp | 59 + Minecraft.World/BedItem.h | 15 + Minecraft.World/BedTile.cpp | 332 ++ Minecraft.World/BedTile.h | 53 + Minecraft.World/BegGoal.cpp | 60 + Minecraft.World/BegGoal.h | 31 + Minecraft.World/BinaryHeap.cpp | 188 + Minecraft.World/BinaryHeap.h | 36 + Minecraft.World/Biome.cpp | 308 ++ Minecraft.World/Biome.h | 152 + Minecraft.World/BiomeCache.cpp | 165 + Minecraft.World/BiomeCache.h | 52 + Minecraft.World/BiomeDecorator.cpp | 328 ++ Minecraft.World/BiomeDecorator.h | 75 + Minecraft.World/BiomeInitLayer.cpp | 79 + Minecraft.World/BiomeInitLayer.h | 16 + Minecraft.World/BiomeOverrideLayer.cpp | 81 + Minecraft.World/BiomeOverrideLayer.h | 18 + Minecraft.World/BiomeSource.cpp | 638 +++ Minecraft.World/BiomeSource.h | 106 + Minecraft.World/BirchFeature.cpp | 81 + Minecraft.World/BirchFeature.h | 11 + Minecraft.World/Blaze.cpp | 227 + Minecraft.World/Blaze.h | 51 + Minecraft.World/BlockDestructionProgress.cpp | 57 + Minecraft.World/BlockDestructionProgress.h | 24 + Minecraft.World/BlockGenMethods.cpp | 224 + Minecraft.World/BlockGenMethods.h | 12 + Minecraft.World/BlockRegionUpdatePacket.cpp | 157 + Minecraft.World/BlockRegionUpdatePacket.h | 33 + Minecraft.World/BlockReplacements.cpp | 26 + Minecraft.World/BlockReplacements.h | 13 + Minecraft.World/Boat.cpp | 519 +++ Minecraft.World/Boat.h | 83 + Minecraft.World/BoatItem.cpp | 129 + Minecraft.World/BoatItem.h | 24 + Minecraft.World/BodyControl.cpp | 54 + Minecraft.World/BodyControl.h | 20 + Minecraft.World/BonusChestFeature.cpp | 88 + Minecraft.World/BonusChestFeature.h | 19 + Minecraft.World/BookItem.cpp | 17 + Minecraft.World/BookItem.h | 12 + Minecraft.World/BookshelfTile.cpp | 24 + Minecraft.World/BookshelfTile.h | 15 + Minecraft.World/BossMob.cpp | 36 + Minecraft.World/BossMob.h | 22 + Minecraft.World/BossMobPart.cpp | 43 + Minecraft.World/BossMobPart.h | 27 + Minecraft.World/BottleItem.cpp | 99 + Minecraft.World/BottleItem.h | 20 + Minecraft.World/BoundingBox.cpp | 166 + Minecraft.World/BoundingBox.h | 31 + Minecraft.World/BowItem.cpp | 109 + Minecraft.World/BowItem.h | 31 + Minecraft.World/BowlFoodItem.cpp | 16 + Minecraft.World/BowlFoodItem.h | 14 + Minecraft.World/BreakDoorGoal.cpp | 65 + Minecraft.World/BreakDoorGoal.h | 22 + Minecraft.World/BreedGoal.cpp | 116 + Minecraft.World/BreedGoal.h | 32 + Minecraft.World/BrewingStandMenu.cpp | 239 + Minecraft.World/BrewingStandMenu.h | 65 + Minecraft.World/BrewingStandTile.cpp | 136 + Minecraft.World/BrewingStandTile.h | 32 + Minecraft.World/BrewingStandTileEntity.cpp | 433 ++ Minecraft.World/BrewingStandTileEntity.h | 51 + Minecraft.World/BucketItem.cpp | 261 ++ Minecraft.World/BucketItem.h | 31 + Minecraft.World/Buffer.cpp | 73 + Minecraft.World/Buffer.h | 34 + Minecraft.World/BufferedOutputStream.cpp | 87 + Minecraft.World/BufferedOutputStream.h | 23 + Minecraft.World/BufferedReader.cpp | 173 + Minecraft.World/BufferedReader.h | 27 + Minecraft.World/Bush.cpp | 87 + Minecraft.World/Bush.h | 42 + Minecraft.World/ButtonTile.cpp | 366 ++ Minecraft.World/ButtonTile.h | 66 + Minecraft.World/ByteArrayInputStream.cpp | 118 + Minecraft.World/ByteArrayInputStream.h | 26 + Minecraft.World/ByteArrayOutputStream.cpp | 80 + Minecraft.World/ByteArrayOutputStream.h | 29 + Minecraft.World/ByteArrayTag.h | 53 + Minecraft.World/ByteBuffer.cpp | 478 ++ Minecraft.World/ByteBuffer.h | 57 + Minecraft.World/ByteTag.h | 36 + Minecraft.World/C4JThread.cpp | 1100 +++++ Minecraft.World/C4JThread.h | 225 + Minecraft.World/CactusFeature.cpp | 27 + Minecraft.World/CactusFeature.h | 9 + Minecraft.World/CactusTile.cpp | 118 + Minecraft.World/CactusTile.h | 39 + Minecraft.World/CakeTile.cpp | 151 + Minecraft.World/CakeTile.h | 44 + Minecraft.World/CanyonFeature.cpp | 185 + Minecraft.World/CanyonFeature.h | 14 + Minecraft.World/CarrotOnAStickItem.cpp | 47 + Minecraft.World/CarrotOnAStickItem.h | 13 + Minecraft.World/CarrotTile.cpp | 42 + Minecraft.World/CarrotTile.h | 21 + Minecraft.World/CauldronTile.cpp | 177 + Minecraft.World/CauldronTile.h | 33 + Minecraft.World/CaveFeature.cpp | 92 + Minecraft.World/CaveFeature.h | 10 + Minecraft.World/CaveSpider.cpp | 64 + Minecraft.World/CaveSpider.h | 18 + Minecraft.World/ChatAutoCompletePacket.h | 56 + Minecraft.World/ChatPacket.cpp | 87 + Minecraft.World/ChatPacket.h | 98 + Minecraft.World/ChestTile.cpp | 316 ++ Minecraft.World/ChestTile.h | 38 + Minecraft.World/ChestTileEntity.cpp | 289 ++ Minecraft.World/ChestTileEntity.h | 63 + Minecraft.World/Chicken.cpp | 150 + Minecraft.World/Chicken.h | 42 + Minecraft.World/ChunkPos.cpp | 75 + Minecraft.World/ChunkPos.h | 40 + Minecraft.World/ChunkSource.h | 67 + Minecraft.World/ChunkStorage.h | 15 + Minecraft.World/ChunkStorageProfileDecorator.cpp | 71 + Minecraft.World/ChunkStorageProfileDecorator.h | 25 + Minecraft.World/ChunkTilesUpdatePacket.cpp | 173 + Minecraft.World/ChunkTilesUpdatePacket.h | 31 + Minecraft.World/ChunkVisibilityAreaPacket.cpp | 51 + Minecraft.World/ChunkVisibilityAreaPacket.h | 30 + Minecraft.World/ChunkVisibilityPacket.cpp | 47 + Minecraft.World/ChunkVisibilityPacket.h | 29 + Minecraft.World/Class.cpp | 5 + Minecraft.World/Class.h | 162 + Minecraft.World/ClayFeature.cpp | 38 + Minecraft.World/ClayFeature.h | 17 + Minecraft.World/ClayTile.cpp | 17 + Minecraft.World/ClayTile.h | 13 + Minecraft.World/ClientCommandPacket.cpp | 33 + Minecraft.World/ClientCommandPacket.h | 24 + Minecraft.World/ClientInformationPacket.h | 94 + Minecraft.World/ClientProtocolPacket.h | 74 + Minecraft.World/ClientSideMerchant.cpp | 64 + Minecraft.World/ClientSideMerchant.h | 30 + Minecraft.World/ClockItem.cpp | 41 + Minecraft.World/ClockItem.h | 19 + Minecraft.World/ClothDyeRecipes.cpp | 107 + Minecraft.World/ClothDyeRecipes.h | 8 + Minecraft.World/ClothTile.cpp | 38 + Minecraft.World/ClothTile.h | 22 + Minecraft.World/ClothTileItem.cpp | 68 + Minecraft.World/ClothTileItem.h | 17 + Minecraft.World/CoalItem.cpp | 22 + Minecraft.World/CoalItem.h | 17 + Minecraft.World/CocoaTile.cpp | 176 + Minecraft.World/CocoaTile.h | 38 + Minecraft.World/Color.cpp | 97 + Minecraft.World/Color.h | 15 + Minecraft.World/ColoredTileItem.cpp | 60 + Minecraft.World/ColoredTileItem.h | 25 + Minecraft.World/Command.cpp | 45 + Minecraft.World/Command.h | 28 + Minecraft.World/CommandDispatcher.cpp | 34 + Minecraft.World/CommandDispatcher.h | 19 + Minecraft.World/CommandSender.h | 12 + Minecraft.World/CommandsEnum.h | 15 + Minecraft.World/CommonStats.cpp | 298 ++ Minecraft.World/CommonStats.h | 80 + Minecraft.World/CompassItem.cpp | 42 + Minecraft.World/CompassItem.h | 19 + Minecraft.World/ComplexItem.cpp | 19 + Minecraft.World/ComplexItem.h | 18 + Minecraft.World/ComplexItemDataPacket.cpp | 57 + Minecraft.World/ComplexItemDataPacket.h | 27 + Minecraft.World/CompoundContainer.cpp | 75 + Minecraft.World/CompoundContainer.h | 36 + Minecraft.World/CompoundTag.h | 292 ++ Minecraft.World/CompressedTileStorage.cpp | 1361 ++++++ Minecraft.World/CompressedTileStorage.h | 111 + Minecraft.World/Connection.cpp | 675 +++ Minecraft.World/Connection.h | 145 + Minecraft.World/ConsoleSaveFile.h | 57 + Minecraft.World/ConsoleSaveFileConverter.cpp | 294 ++ Minecraft.World/ConsoleSaveFileConverter.h | 16 + Minecraft.World/ConsoleSaveFileIO.h | 7 + Minecraft.World/ConsoleSaveFileInputStream.cpp | 134 + Minecraft.World/ConsoleSaveFileInputStream.h | 26 + Minecraft.World/ConsoleSaveFileOriginal.cpp | 1059 +++++ Minecraft.World/ConsoleSaveFileOriginal.h | 94 + Minecraft.World/ConsoleSaveFileOutputStream.cpp | 130 + Minecraft.World/ConsoleSaveFileOutputStream.h | 25 + Minecraft.World/ConsoleSaveFileSplit.cpp | 1711 +++++++ Minecraft.World/ConsoleSaveFileSplit.h | 143 + Minecraft.World/ConsoleSavePath.h | 15 + Minecraft.World/Container.cpp | 2 + Minecraft.World/Container.h | 23 + Minecraft.World/ContainerAckPacket.cpp | 45 + Minecraft.World/ContainerAckPacket.h | 30 + Minecraft.World/ContainerButtonClickPacket.cpp | 40 + Minecraft.World/ContainerButtonClickPacket.h | 22 + Minecraft.World/ContainerClickPacket.cpp | 65 + Minecraft.World/ContainerClickPacket.h | 30 + Minecraft.World/ContainerClosePacket.cpp | 37 + Minecraft.World/ContainerClosePacket.h | 24 + Minecraft.World/ContainerMenu.cpp | 120 + Minecraft.World/ContainerMenu.h | 22 + Minecraft.World/ContainerOpenPacket.cpp | 48 + Minecraft.World/ContainerOpenPacket.h | 37 + Minecraft.World/ContainerSetContentPacket.cpp | 60 + Minecraft.World/ContainerSetContentPacket.h | 26 + Minecraft.World/ContainerSetDataPacket.cpp | 45 + Minecraft.World/ContainerSetDataPacket.h | 24 + Minecraft.World/ContainerSetSlotPacket.cpp | 53 + Minecraft.World/ContainerSetSlotPacket.h | 30 + Minecraft.World/Control.h | 9 + Minecraft.World/ControlledByPlayerGoal.cpp | 152 + Minecraft.World/ControlledByPlayerGoal.h | 32 + Minecraft.World/Coord.h | 12 + Minecraft.World/CoralTile.cpp | 18 + Minecraft.World/CoralTile.h | 11 + Minecraft.World/Cow.cpp | 130 + Minecraft.World/Cow.h | 32 + Minecraft.World/CraftItemPacket.cpp | 46 + Minecraft.World/CraftItemPacket.h | 27 + Minecraft.World/CraftingContainer.cpp | 100 + Minecraft.World/CraftingContainer.h | 32 + Minecraft.World/CraftingMenu.cpp | 138 + Minecraft.World/CraftingMenu.h | 35 + Minecraft.World/Creature.cpp | 3 + Minecraft.World/Creature.h | 10 + Minecraft.World/Creeper.cpp | 169 + Minecraft.World/Creeper.h | 59 + Minecraft.World/CropTile.cpp | 169 + Minecraft.World/CropTile.h | 44 + Minecraft.World/CustomLevelSource.cpp | 640 +++ Minecraft.World/CustomLevelSource.h | 79 + Minecraft.World/CustomPayloadPacket.cpp | 75 + Minecraft.World/CustomPayloadPacket.h | 35 + Minecraft.World/DamageEnchantment.cpp | 62 + Minecraft.World/DamageEnchantment.h | 30 + Minecraft.World/DamageSource.cpp | 180 + Minecraft.World/DamageSource.h | 90 + Minecraft.World/DataInput.h | 22 + Minecraft.World/DataInputStream.cpp | 546 +++ Minecraft.World/DataInputStream.h | 35 + Minecraft.World/DataLayer.cpp | 62 + Minecraft.World/DataLayer.h | 22 + Minecraft.World/DataOutput.h | 20 + Minecraft.World/DataOutputStream.cpp | 268 ++ Minecraft.World/DataOutputStream.h | 37 + Minecraft.World/DeadBushFeature.cpp | 33 + Minecraft.World/DeadBushFeature.h | 15 + Minecraft.World/DeadBushTile.cpp | 46 + Minecraft.World/DeadBushTile.h | 16 + Minecraft.World/DebugOptionsPacket.cpp | 42 + Minecraft.World/DebugOptionsPacket.h | 26 + Minecraft.World/DecorationMaterial.h | 12 + Minecraft.World/DefaultGameModeCommand.cpp | 26 + Minecraft.World/DefaultGameModeCommand.h | 15 + Minecraft.World/DefendVillageTargetGoal.cpp | 24 + Minecraft.World/DefendVillageTargetGoal.h | 18 + Minecraft.World/Definitions.h | 40 + Minecraft.World/DelayedRelease.cpp | 46 + Minecraft.World/DelayedRelease.h | 30 + Minecraft.World/DerivedLevelData.cpp | 209 + Minecraft.World/DerivedLevelData.h | 60 + Minecraft.World/DescFormatter.h | 8 + Minecraft.World/DesertBiome.cpp | 33 + Minecraft.World/DesertBiome.h | 10 + Minecraft.World/DesertWellFeature.cpp | 90 + Minecraft.World/DesertWellFeature.h | 9 + Minecraft.World/DetectorRailTile.cpp | 115 + Minecraft.World/DetectorRailTile.h | 31 + Minecraft.World/Difficulty.h | 10 + Minecraft.World/DigDurabilityEnchantment.cpp | 36 + Minecraft.World/DigDurabilityEnchantment.h | 15 + Minecraft.World/DiggerItem.cpp | 64 + Minecraft.World/DiggerItem.h | 31 + Minecraft.World/DiggingEnchantment.cpp | 29 + Minecraft.World/DiggingEnchantment.h | 14 + Minecraft.World/Dimension.cpp | 243 + Minecraft.World/Dimension.h | 62 + Minecraft.World/DiodeTile.cpp | 299 ++ Minecraft.World/DiodeTile.h | 50 + Minecraft.World/Direction.cpp | 62 + Minecraft.World/Direction.h | 32 + Minecraft.World/DirectionalTile.cpp | 16 + Minecraft.World/DirectionalTile.h | 17 + Minecraft.World/DirectoryLevelStorage.cpp | 824 ++++ Minecraft.World/DirectoryLevelStorage.h | 144 + Minecraft.World/DirectoryLevelStorageSource.cpp | 139 + Minecraft.World/DirectoryLevelStorageSource.h | 34 + Minecraft.World/DirtTile.cpp | 6 + Minecraft.World/DirtTile.h | 9 + Minecraft.World/DisconnectPacket.cpp | 48 + Minecraft.World/DisconnectPacket.h | 73 + Minecraft.World/DispenserTile.cpp | 573 +++ Minecraft.World/DispenserTile.h | 58 + Minecraft.World/DispenserTileEntity.cpp | 224 + Minecraft.World/DispenserTileEntity.h | 52 + Minecraft.World/Distort.cpp | 13 + Minecraft.World/Distort.h | 14 + Minecraft.World/DoorInfo.cpp | 69 + Minecraft.World/DoorInfo.h | 29 + Minecraft.World/DoorInteractGoal.cpp | 73 + Minecraft.World/DoorInteractGoal.h | 29 + Minecraft.World/DoorItem.cpp | 75 + Minecraft.World/DoorItem.h | 20 + Minecraft.World/DoorTile.cpp | 341 ++ Minecraft.World/DoorTile.h | 60 + Minecraft.World/DoubleTag.h | 37 + Minecraft.World/DownfallLayer.cpp | 21 + Minecraft.World/DownfallLayer.h | 10 + Minecraft.World/DownfallMixerLayer.cpp | 24 + Minecraft.World/DownfallMixerLayer.h | 14 + Minecraft.World/DragonFireball.cpp | 86 + Minecraft.World/DragonFireball.h | 37 + Minecraft.World/DungeonFeature.cpp | 191 + Minecraft.World/DungeonFeature.h | 12 + Minecraft.World/DurangoStats.cpp | 1221 +++++ Minecraft.World/DurangoStats.h | 311 ++ Minecraft.World/DyePowderItem.cpp | 317 ++ Minecraft.World/DyePowderItem.h | 50 + Minecraft.World/EatTileGoal.cpp | 73 + Minecraft.World/EatTileGoal.h | 28 + Minecraft.World/EggItem.cpp | 31 + Minecraft.World/EggItem.h | 15 + Minecraft.World/EggTile.cpp | 173 + Minecraft.World/EggTile.h | 29 + Minecraft.World/Emboss.cpp | 12 + Minecraft.World/Emboss.h | 13 + Minecraft.World/EmptyLevelChunk.cpp | 222 + Minecraft.World/EmptyLevelChunk.h | 54 + Minecraft.World/EnchantItemCommand.cpp | 85 + Minecraft.World/EnchantItemCommand.h | 15 + Minecraft.World/EnchantedBookItem.cpp | 144 + Minecraft.World/EnchantedBookItem.h | 25 + Minecraft.World/Enchantment.cpp | 202 + Minecraft.World/Enchantment.h | 87 + Minecraft.World/EnchantmentCategory.cpp | 42 + Minecraft.World/EnchantmentCategory.h | 19 + Minecraft.World/EnchantmentContainer.cpp | 18 + Minecraft.World/EnchantmentContainer.h | 18 + Minecraft.World/EnchantmentHelper.cpp | 477 ++ Minecraft.World/EnchantmentHelper.h | 110 + Minecraft.World/EnchantmentInstance.cpp | 17 + Minecraft.World/EnchantmentInstance.h | 16 + Minecraft.World/EnchantmentMenu.cpp | 299 ++ Minecraft.World/EnchantmentMenu.h | 41 + Minecraft.World/EnchantmentSlot.h | 16 + Minecraft.World/EnchantmentTableEntity.cpp | 109 + Minecraft.World/EnchantmentTableEntity.h | 25 + Minecraft.World/EnchantmentTableTile.cpp | 92 + Minecraft.World/EnchantmentTableTile.h | 29 + Minecraft.World/EndPodiumFeature.cpp | 86 + Minecraft.World/EndPodiumFeature.h | 14 + Minecraft.World/EndTag.h | 25 + Minecraft.World/EnderChestTile.cpp | 117 + Minecraft.World/EnderChestTile.h | 29 + Minecraft.World/EnderChestTileEntity.cpp | 99 + Minecraft.World/EnderChestTileEntity.h | 30 + Minecraft.World/EnderCrystal.cpp | 128 + Minecraft.World/EnderCrystal.h | 42 + Minecraft.World/EnderDragon.cpp | 1941 ++++++++ Minecraft.World/EnderDragon.h | 177 + Minecraft.World/EnderEyeItem.cpp | 238 + Minecraft.World/EnderEyeItem.h | 13 + Minecraft.World/EnderMan.cpp | 411 ++ Minecraft.World/EnderMan.h | 64 + Minecraft.World/EnderpearlItem.cpp | 34 + Minecraft.World/EnderpearlItem.h | 13 + Minecraft.World/Enemy.cpp | 10 + Minecraft.World/Enemy.h | 14 + Minecraft.World/Entity.cpp | 1956 ++++++++ Minecraft.World/Entity.h | 397 ++ Minecraft.World/EntityActionAtPositionPacket.cpp | 56 + Minecraft.World/EntityActionAtPositionPacket.h | 23 + Minecraft.World/EntityDamageSource.cpp | 38 + Minecraft.World/EntityDamageSource.h | 26 + Minecraft.World/EntityEvent.h | 29 + Minecraft.World/EntityEventPacket.cpp | 41 + Minecraft.World/EntityEventPacket.h | 25 + Minecraft.World/EntityIO.cpp | 264 ++ Minecraft.World/EntityIO.h | 61 + Minecraft.World/EntityPos.cpp | 62 + Minecraft.World/EntityPos.h | 15 + Minecraft.World/EntityTile.cpp | 32 + Minecraft.World/EntityTile.h | 15 + Minecraft.World/Exceptions.h | 29 + Minecraft.World/ExperienceCommand.cpp | 41 + Minecraft.World/ExperienceCommand.h | 15 + Minecraft.World/ExperienceItem.cpp | 32 + Minecraft.World/ExperienceItem.h | 15 + Minecraft.World/ExperienceOrb.cpp | 330 ++ Minecraft.World/ExperienceOrb.h | 60 + Minecraft.World/ExplodePacket.cpp | 136 + Minecraft.World/ExplodePacket.h | 37 + Minecraft.World/Explosion.cpp | 265 ++ Minecraft.World/Explosion.h | 42 + Minecraft.World/ExtremeHillsBiome.cpp | 30 + Minecraft.World/ExtremeHillsBiome.h | 16 + Minecraft.World/EyeOfEnderSignal.cpp | 206 + Minecraft.World/EyeOfEnderSignal.h | 41 + Minecraft.World/Facing.cpp | 22 + Minecraft.World/Facing.h | 17 + Minecraft.World/FallingTile.cpp | 240 + Minecraft.World/FallingTile.h | 49 + Minecraft.World/FarmTile.cpp | 147 + Minecraft.World/FarmTile.h | 36 + Minecraft.World/FastNoise.cpp | 114 + Minecraft.World/FastNoise.h | 17 + Minecraft.World/Feature.cpp | 31 + Minecraft.World/Feature.h | 20 + Minecraft.World/FenceGateTile.cpp | 147 + Minecraft.World/FenceGateTile.h | 26 + Minecraft.World/FenceTile.cpp | 132 + Minecraft.World/FenceTile.h | 23 + Minecraft.World/File.cpp | 702 +++ Minecraft.World/File.h | 56 + Minecraft.World/FileFilter.h | 10 + Minecraft.World/FileHeader.cpp | 681 +++ Minecraft.World/FileHeader.h | 203 + Minecraft.World/FileInputStream.cpp | 204 + Minecraft.World/FileInputStream.h | 20 + Minecraft.World/FileOutputStream.cpp | 153 + Minecraft.World/FileOutputStream.h | 19 + Minecraft.World/FilenameFilter.h | 13 + Minecraft.World/FireAspectEnchantment.cpp | 22 + Minecraft.World/FireAspectEnchantment.h | 13 + Minecraft.World/FireChargeItem.cpp | 67 + Minecraft.World/FireChargeItem.h | 20 + Minecraft.World/FireTile.cpp | 407 ++ Minecraft.World/FireTile.h | 63 + Minecraft.World/Fireball.cpp | 415 ++ Minecraft.World/Fireball.h | 73 + Minecraft.World/FishingHook.cpp | 450 ++ Minecraft.World/FishingHook.h | 63 + Minecraft.World/FishingRodItem.cpp | 67 + Minecraft.World/FishingRodItem.h | 27 + Minecraft.World/FixedBiomeSource.cpp | 153 + Minecraft.World/FixedBiomeSource.h | 37 + Minecraft.World/FlatLayer.cpp | 20 + Minecraft.World/FlatLayer.h | 13 + Minecraft.World/FlatLevelSource.cpp | 153 + Minecraft.World/FlatLevelSource.h | 44 + Minecraft.World/FleeSunGoal.cpp | 52 + Minecraft.World/FleeSunGoal.h | 26 + Minecraft.World/FlintAndSteelItem.cpp | 72 + Minecraft.World/FlintAndSteelItem.h | 14 + Minecraft.World/FlippedIcon.cpp | 88 + Minecraft.World/FlippedIcon.h | 31 + Minecraft.World/FloatBuffer.cpp | 66 + Minecraft.World/FloatBuffer.h | 18 + Minecraft.World/FloatGoal.cpp | 22 + Minecraft.World/FloatGoal.h | 17 + Minecraft.World/FloatTag.h | 37 + Minecraft.World/FlowerFeature.cpp | 40 + Minecraft.World/FlowerFeature.h | 14 + Minecraft.World/FlowerPotTile.cpp | 192 + Minecraft.World/FlowerPotTile.h | 37 + Minecraft.World/FlyingMob.cpp | 81 + Minecraft.World/FlyingMob.h | 18 + Minecraft.World/FoliageColor.cpp | 40 + Minecraft.World/FoliageColor.h | 17 + Minecraft.World/FollowOwnerGoal.cpp | 85 + Minecraft.World/FollowOwnerGoal.h | 34 + Minecraft.World/FollowParentGoal.cpp | 65 + Minecraft.World/FollowParentGoal.h | 23 + Minecraft.World/FoodConstants.cpp | 35 + Minecraft.World/FoodConstants.h | 37 + Minecraft.World/FoodData.cpp | 159 + Minecraft.World/FoodData.h | 34 + Minecraft.World/FoodItem.cpp | 120 + Minecraft.World/FoodItem.h | 49 + Minecraft.World/FoodRecipies.cpp | 122 + Minecraft.World/FoodRecipies.h | 7 + Minecraft.World/ForestBiome.cpp | 26 + Minecraft.World/ForestBiome.h | 10 + Minecraft.World/FurnaceMenu.cpp | 178 + Minecraft.World/FurnaceMenu.h | 38 + Minecraft.World/FurnaceRecipes.cpp | 82 + Minecraft.World/FurnaceRecipes.h | 30 + Minecraft.World/FurnaceResultSlot.cpp | 94 + Minecraft.World/FurnaceResultSlot.h | 23 + Minecraft.World/FurnaceTile.cpp | 208 + Minecraft.World/FurnaceTile.h | 39 + Minecraft.World/FurnaceTileEntity.cpp | 350 ++ Minecraft.World/FurnaceTileEntity.h | 78 + Minecraft.World/FuzzyZoomLayer.cpp | 72 + Minecraft.World/FuzzyZoomLayer.h | 17 + Minecraft.World/GZIPInputStream.h | 17 + Minecraft.World/GZIPOutputStream.h | 17 + Minecraft.World/GameCommandPacket.cpp | 72 + Minecraft.World/GameCommandPacket.h | 26 + Minecraft.World/GameEventPacket.cpp | 59 + Minecraft.World/GameEventPacket.h | 45 + Minecraft.World/GameModeCommand.cpp | 53 + Minecraft.World/GameModeCommand.h | 16 + Minecraft.World/GasMaterial.h | 12 + Minecraft.World/GeneralStat.cpp | 18 + Minecraft.World/GeneralStat.h | 12 + Minecraft.World/GenericStats.cpp | 1284 +++++ Minecraft.World/GenericStats.h | 348 ++ Minecraft.World/GetInfoPacket.cpp | 22 + Minecraft.World/GetInfoPacket.h | 16 + Minecraft.World/Ghast.cpp | 249 + Minecraft.World/Ghast.h | 65 + Minecraft.World/Giant.cpp | 32 + Minecraft.World/Giant.h | 18 + Minecraft.World/GiveItemCommand.cpp | 50 + Minecraft.World/GiveItemCommand.h | 15 + Minecraft.World/GlassTile.cpp | 31 + Minecraft.World/GlassTile.h | 17 + Minecraft.World/GlobalEntity.cpp | 3 + Minecraft.World/GlobalEntity.h | 11 + Minecraft.World/Goal.cpp | 39 + Minecraft.World/Goal.h | 23 + Minecraft.World/GoalSelector.cpp | 147 + Minecraft.World/GoalSelector.h | 45 + Minecraft.World/GoldenAppleItem.cpp | 53 + Minecraft.World/GoldenAppleItem.h | 21 + Minecraft.World/Golem.cpp | 39 + Minecraft.World/Golem.h | 25 + Minecraft.World/GrassColor.cpp | 21 + Minecraft.World/GrassColor.h | 12 + Minecraft.World/GrassTile.cpp | 146 + Minecraft.World/GrassTile.h | 33 + Minecraft.World/GravelTile.cpp | 13 + Minecraft.World/GravelTile.h | 11 + Minecraft.World/GroundBushFeature.cpp | 43 + Minecraft.World/GroundBushFeature.h | 15 + Minecraft.World/GrowMushroomIslandLayer.cpp | 42 + Minecraft.World/GrowMushroomIslandLayer.h | 9 + Minecraft.World/HalfSlabTile.cpp | 143 + Minecraft.World/HalfSlabTile.h | 33 + Minecraft.World/HalfTransparentTile.cpp | 32 + Minecraft.World/HalfTransparentTile.h | 19 + Minecraft.World/HangingEntity.cpp | 292 ++ Minecraft.World/HangingEntity.h | 42 + Minecraft.World/HangingEntityItem.cpp | 88 + Minecraft.World/HangingEntityItem.h | 21 + Minecraft.World/HashExtension.h | 20 + Minecraft.World/Hasher.cpp | 28 + Minecraft.World/Hasher.h | 12 + Minecraft.World/HatchetItem.cpp | 33 + Minecraft.World/HatchetItem.h | 15 + Minecraft.World/HeavyTile.cpp | 91 + Minecraft.World/HeavyTile.h | 26 + Minecraft.World/HellBiome.cpp | 16 + Minecraft.World/HellBiome.h | 8 + Minecraft.World/HellDimension.cpp | 89 + Minecraft.World/HellDimension.h | 23 + Minecraft.World/HellFireFeature.cpp | 19 + Minecraft.World/HellFireFeature.h | 10 + Minecraft.World/HellFlatLevelSource.cpp | 224 + Minecraft.World/HellFlatLevelSource.h | 51 + Minecraft.World/HellPortalFeature.cpp | 37 + Minecraft.World/HellPortalFeature.h | 8 + Minecraft.World/HellRandomLevelSource.cpp | 544 +++ Minecraft.World/HellRandomLevelSource.h | 72 + Minecraft.World/HellSandTile.cpp | 21 + Minecraft.World/HellSandTile.h | 11 + Minecraft.World/HellSpringFeature.cpp | 42 + Minecraft.World/HellSpringFeature.h | 14 + Minecraft.World/HellStoneTile.cpp | 6 + Minecraft.World/HellStoneTile.h | 8 + Minecraft.World/HitResult.cpp | 33 + Minecraft.World/HitResult.h | 22 + Minecraft.World/HoeItem.cpp | 51 + Minecraft.World/HoeItem.h | 19 + Minecraft.World/HouseFeature.cpp | 193 + Minecraft.World/HouseFeature.h | 9 + Minecraft.World/HugeMushroomFeature.cpp | 111 + Minecraft.World/HugeMushroomFeature.h | 13 + Minecraft.World/HugeMushroomTile.cpp | 71 + Minecraft.World/HugeMushroomTile.h | 26 + Minecraft.World/HurtByTargetGoal.cpp | 45 + Minecraft.World/HurtByTargetGoal.h | 17 + Minecraft.World/I18n.cpp | 22 + Minecraft.World/I18n.h | 15 + Minecraft.World/IceBiome.cpp | 6 + Minecraft.World/IceBiome.h | 8 + Minecraft.World/IceTile.cpp | 83 + Minecraft.World/IceTile.h | 19 + Minecraft.World/Icon.h | 34 + Minecraft.World/IconRegister.h | 12 + Minecraft.World/ImprovedNoise.cpp | 216 + Minecraft.World/ImprovedNoise.h | 29 + Minecraft.World/IndirectEntityDamageSource.cpp | 48 + Minecraft.World/IndirectEntityDamageSource.h | 25 + Minecraft.World/InputOutputStream.h | 20 + Minecraft.World/InputStream.cpp | 9 + Minecraft.World/InputStream.h | 17 + Minecraft.World/InputStreamReader.cpp | 52 + Minecraft.World/InputStreamReader.h | 18 + Minecraft.World/InstantenousMobEffect.cpp | 16 + Minecraft.World/InstantenousMobEffect.h | 11 + Minecraft.World/IntArrayTag.h | 66 + Minecraft.World/IntBuffer.cpp | 113 + Minecraft.World/IntBuffer.h | 21 + Minecraft.World/IntCache.cpp | 164 + Minecraft.World/IntCache.h | 34 + Minecraft.World/IntTag.h | 36 + Minecraft.World/InteractGoal.cpp | 13 + Minecraft.World/InteractGoal.h | 10 + Minecraft.World/InteractPacket.cpp | 48 + Minecraft.World/InteractPacket.h | 25 + Minecraft.World/Inventory.cpp | 746 +++ Minecraft.World/Inventory.h | 136 + Minecraft.World/InventoryMenu.cpp | 247 + Minecraft.World/InventoryMenu.h | 44 + Minecraft.World/IslandLayer.cpp | 25 + Minecraft.World/IslandLayer.h | 11 + Minecraft.World/Item.cpp | 1110 +++++ Minecraft.World/Item.h | 718 +++ Minecraft.World/ItemEntity.cpp | 306 ++ Minecraft.World/ItemEntity.h | 74 + Minecraft.World/ItemFrame.cpp | 145 + Minecraft.World/ItemFrame.h | 41 + Minecraft.World/ItemInstance.cpp | 727 +++ Minecraft.World/ItemInstance.h | 154 + Minecraft.World/ItemStat.cpp | 11 + Minecraft.World/ItemStat.h | 14 + Minecraft.World/JavaIntHash.h | 77 + Minecraft.World/JavaMath.cpp | 76 + Minecraft.World/JavaMath.h | 19 + Minecraft.World/JumpControl.cpp | 21 + Minecraft.World/JumpControl.h | 18 + Minecraft.World/JungleBiome.cpp | 63 + Minecraft.World/JungleBiome.h | 14 + Minecraft.World/KeepAlivePacket.cpp | 52 + Minecraft.World/KeepAlivePacket.h | 25 + Minecraft.World/KickPlayerPacket.cpp | 37 + Minecraft.World/KickPlayerPacket.h | 22 + Minecraft.World/KillCommand.cpp | 19 + Minecraft.World/KillCommand.h | 10 + Minecraft.World/KnockbackEnchantment.cpp | 22 + Minecraft.World/KnockbackEnchantment.h | 13 + Minecraft.World/LadderTile.cpp | 112 + Minecraft.World/LadderTile.h | 26 + Minecraft.World/LakeFeature.cpp | 175 + Minecraft.World/LakeFeature.h | 14 + Minecraft.World/Language.cpp | 50 + Minecraft.World/Language.h | 14 + Minecraft.World/LargeCaveFeature.cpp | 196 + Minecraft.World/LargeCaveFeature.h | 11 + Minecraft.World/LargeFeature.cpp | 37 + Minecraft.World/LargeFeature.h | 23 + Minecraft.World/LargeHellCaveFeature.cpp | 184 + Minecraft.World/LargeHellCaveFeature.h | 11 + Minecraft.World/LavaSlime.cpp | 144 + Minecraft.World/LavaSlime.h | 46 + Minecraft.World/Layer.cpp | 199 + Minecraft.World/Layer.h | 36 + Minecraft.World/LeafTile.cpp | 337 ++ Minecraft.World/LeafTile.h | 76 + Minecraft.World/LeafTileItem.cpp | 47 + Minecraft.World/LeafTileItem.h | 17 + Minecraft.World/LeapAtTargetGoal.cpp | 40 + Minecraft.World/LeapAtTargetGoal.h | 18 + Minecraft.World/Level.cpp | 4754 +++++++++++++++++++ Minecraft.World/Level.h | 537 +++ Minecraft.World/LevelChunk.cpp | 2463 ++++++++++ Minecraft.World/LevelChunk.h | 260 ++ Minecraft.World/LevelConflictException.cpp | 6 + Minecraft.World/LevelConflictException.h | 13 + Minecraft.World/LevelData.cpp | 589 +++ Minecraft.World/LevelData.h | 133 + Minecraft.World/LevelEvent.h | 41 + Minecraft.World/LevelEventPacket.cpp | 54 + Minecraft.World/LevelEventPacket.h | 24 + Minecraft.World/LevelListener.h | 40 + Minecraft.World/LevelObjectInputStream.h | 34 + Minecraft.World/LevelSettings.cpp | 185 + Minecraft.World/LevelSettings.h | 67 + Minecraft.World/LevelSoundPacket.cpp | 96 + Minecraft.World/LevelSoundPacket.h | 38 + Minecraft.World/LevelSource.h | 29 + Minecraft.World/LevelStorage.cpp | 6 + Minecraft.World/LevelStorage.h | 37 + Minecraft.World/LevelStorageProfilerDecorator.cpp | 57 + Minecraft.World/LevelStorageProfilerDecorator.h | 27 + Minecraft.World/LevelStorageSource.h | 37 + Minecraft.World/LevelSummary.cpp | 69 + Minecraft.World/LevelSummary.h | 28 + Minecraft.World/LevelType.cpp | 118 + Minecraft.World/LevelType.h | 39 + Minecraft.World/LeverTile.cpp | 309 ++ Minecraft.World/LeverTile.h | 31 + Minecraft.World/LightGemFeature.cpp | 37 + Minecraft.World/LightGemFeature.h | 8 + Minecraft.World/LightGemTile.cpp | 22 + Minecraft.World/LightGemTile.h | 13 + Minecraft.World/LightLayer.h | 14 + Minecraft.World/LightningBolt.cpp | 143 + Minecraft.World/LightningBolt.h | 36 + Minecraft.World/LiquidMaterial.h | 12 + Minecraft.World/LiquidTile.cpp | 418 ++ Minecraft.World/LiquidTile.h | 62 + Minecraft.World/LiquidTileDynamic.cpp | 342 ++ Minecraft.World/LiquidTileDynamic.h | 54 + Minecraft.World/LiquidTileStatic.cpp | 85 + Minecraft.World/LiquidTileStatic.h | 20 + Minecraft.World/ListTag.h | 144 + Minecraft.World/LockedChestTile.cpp | 22 + Minecraft.World/LockedChestTile.h | 14 + Minecraft.World/LoginPacket.cpp | 183 + Minecraft.World/LoginPacket.h | 45 + Minecraft.World/LongTag.h | 36 + Minecraft.World/LookAtPlayerGoal.cpp | 57 + Minecraft.World/LookAtPlayerGoal.h | 32 + Minecraft.World/LookAtTradingPlayerGoal.cpp | 19 + Minecraft.World/LookAtTradingPlayerGoal.h | 16 + Minecraft.World/LookControl.cpp | 117 + Minecraft.World/LookControl.h | 33 + Minecraft.World/LootBonusEnchantment.cpp | 31 + Minecraft.World/LootBonusEnchantment.h | 14 + Minecraft.World/MakeLoveGoal.cpp | 103 + Minecraft.World/MakeLoveGoal.h | 33 + Minecraft.World/MapItem.cpp | 347 ++ Minecraft.World/MapItem.h | 23 + Minecraft.World/MapItemSavedData.cpp | 573 +++ Minecraft.World/MapItemSavedData.h | 89 + Minecraft.World/Material.cpp | 192 + Minecraft.World/Material.h | 92 + Minecraft.World/MaterialColor.cpp | 46 + Minecraft.World/MaterialColor.h | 31 + Minecraft.World/McRegionChunkStorage.cpp | 460 ++ Minecraft.World/McRegionChunkStorage.h | 43 + Minecraft.World/McRegionLevelStorage.cpp | 103 + Minecraft.World/McRegionLevelStorage.h | 22 + Minecraft.World/McRegionLevelStorageSource.cpp | 365 ++ Minecraft.World/McRegionLevelStorageSource.h | 68 + Minecraft.World/MegaTreeFeature.cpp | 201 + Minecraft.World/MegaTreeFeature.h | 19 + Minecraft.World/MeleeAttackGoal.cpp | 96 + Minecraft.World/MeleeAttackGoal.h | 38 + Minecraft.World/MelonTile.cpp | 45 + Minecraft.World/MelonTile.h | 24 + Minecraft.World/MemoryChunkStorage.cpp | 25 + Minecraft.World/MemoryChunkStorage.h | 14 + Minecraft.World/MemoryLevelStorage.cpp | 63 + Minecraft.World/MemoryLevelStorage.h | 31 + Minecraft.World/MemoryLevelStorageSource.cpp | 61 + Minecraft.World/MemoryLevelStorageSource.h | 21 + Minecraft.World/MenuBackup.cpp | 47 + Minecraft.World/MenuBackup.h | 21 + Minecraft.World/Merchant.h | 17 + Minecraft.World/MerchantContainer.cpp | 183 + Minecraft.World/MerchantContainer.h | 42 + Minecraft.World/MerchantMenu.cpp | 150 + Minecraft.World/MerchantMenu.h | 46 + Minecraft.World/MerchantRecipe.cpp | 146 + Minecraft.World/MerchantRecipe.h | 35 + Minecraft.World/MerchantRecipeList.cpp | 195 + Minecraft.World/MerchantRecipeList.h | 35 + Minecraft.World/MerchantResultSlot.cpp | 94 + Minecraft.World/MerchantResultSlot.h | 33 + Minecraft.World/MetalTile.cpp | 6 + Minecraft.World/MetalTile.h | 8 + Minecraft.World/MilkBucketItem.cpp | 41 + Minecraft.World/MilkBucketItem.h | 17 + Minecraft.World/MineShaftFeature.cpp | 23 + Minecraft.World/MineShaftFeature.h | 10 + Minecraft.World/MineShaftPieces.cpp | 697 +++ Minecraft.World/MineShaftPieces.h | 96 + Minecraft.World/MineShaftStart.cpp | 12 + Minecraft.World/MineShaftStart.h | 9 + Minecraft.World/Minecart.cpp | 1211 +++++ Minecraft.World/Minecart.h | 114 + Minecraft.World/MinecartItem.cpp | 33 + Minecraft.World/MinecartItem.h | 14 + Minecraft.World/Minecraft.World.cpp | 104 + Minecraft.World/Minecraft.World.h | 3 + Minecraft.World/Minecraft.World.vcxproj | 3969 ++++++++++++++++ Minecraft.World/Minecraft.World.vcxproj.filters | 4927 ++++++++++++++++++++ Minecraft.World/Minecraft.World.vcxproj.user | 3 + Minecraft.World/Minecraft.World.vcxproj.vspscc | 10 + Minecraft.World/Mob.cpp | 1943 ++++++++ Minecraft.World/Mob.h | 369 ++ Minecraft.World/MobCategory.cpp | 71 + Minecraft.World/MobCategory.h | 82 + Minecraft.World/MobEffect.cpp | 270 ++ Minecraft.World/MobEffect.h | 114 + Minecraft.World/MobEffectInstance.cpp | 139 + Minecraft.World/MobEffectInstance.h | 42 + Minecraft.World/MobSpawner.cpp | 651 +++ Minecraft.World/MobSpawner.h | 46 + Minecraft.World/MobSpawnerTile.cpp | 49 + Minecraft.World/MobSpawnerTile.h | 19 + Minecraft.World/MobSpawnerTileEntity.cpp | 233 + Minecraft.World/MobSpawnerTileEntity.h | 59 + Minecraft.World/MobType.h | 9 + Minecraft.World/MockedLevelStorage.cpp | 49 + Minecraft.World/MockedLevelStorage.h | 22 + Minecraft.World/Monster.cpp | 159 + Minecraft.World/Monster.h | 48 + Minecraft.World/MonsterPlacerItem.cpp | 291 ++ Minecraft.World/MonsterPlacerItem.h | 42 + Minecraft.World/MonsterRoomFeature.cpp | 140 + Minecraft.World/MonsterRoomFeature.h | 16 + Minecraft.World/MouseInventoryClickHandler.h | 84 + Minecraft.World/MoveControl.cpp | 75 + Minecraft.World/MoveControl.h | 34 + Minecraft.World/MoveEntityPacket.cpp | 168 + Minecraft.World/MoveEntityPacket.h | 78 + Minecraft.World/MoveEntityPacketSmall.cpp | 191 + Minecraft.World/MoveEntityPacketSmall.h | 79 + Minecraft.World/MoveIndoorsGoal.cpp | 65 + Minecraft.World/MoveIndoorsGoal.h | 22 + Minecraft.World/MovePlayerPacket.cpp | 188 + Minecraft.World/MovePlayerPacket.h | 78 + Minecraft.World/MoveThroughVillageGoal.cpp | 127 + Minecraft.World/MoveThroughVillageGoal.h | 32 + Minecraft.World/MoveTowardsRestrictionGoal.cpp | 38 + Minecraft.World/MoveTowardsRestrictionGoal.h | 18 + Minecraft.World/MoveTowardsTargetGoal.cpp | 43 + Minecraft.World/MoveTowardsTargetGoal.h | 20 + Minecraft.World/Mth.cpp | 172 + Minecraft.World/Mth.h | 42 + Minecraft.World/MultiTextureTileItem.cpp | 46 + Minecraft.World/MultiTextureTileItem.h | 22 + Minecraft.World/Mushroom.cpp | 107 + Minecraft.World/Mushroom.h | 23 + Minecraft.World/MushroomCow.cpp | 84 + Minecraft.World/MushroomCow.h | 17 + Minecraft.World/MushroomIslandBiome.cpp | 25 + Minecraft.World/MushroomIslandBiome.h | 8 + Minecraft.World/MusicTile.cpp | 84 + Minecraft.World/MusicTile.h | 16 + Minecraft.World/MusicTileEntity.cpp | 64 + Minecraft.World/MusicTileEntity.h | 26 + Minecraft.World/MycelTile.cpp | 74 + Minecraft.World/MycelTile.h | 23 + Minecraft.World/NbtIo.cpp | 65 + Minecraft.World/NbtIo.h | 17 + Minecraft.World/NbtSlotFile.cpp | 250 + Minecraft.World/NbtSlotFile.h | 44 + Minecraft.World/NearestAttackableTargetGoal.cpp | 71 + Minecraft.World/NearestAttackableTargetGoal.h | 36 + Minecraft.World/NetherBridgeFeature.cpp | 119 + Minecraft.World/NetherBridgeFeature.h | 30 + Minecraft.World/NetherBridgePieces.cpp | 1453 ++++++ Minecraft.World/NetherBridgePieces.h | 343 ++ Minecraft.World/NetherSphere.cpp | 26 + Minecraft.World/NetherSphere.h | 16 + Minecraft.World/NetherStalkTile.cpp | 122 + Minecraft.World/NetherStalkTile.h | 33 + Minecraft.World/Node.cpp | 75 + Minecraft.World/Node.h | 38 + Minecraft.World/NonTameRandomTargetGoal.cpp | 14 + Minecraft.World/NonTameRandomTargetGoal.h | 16 + Minecraft.World/NormalDimension.h | 6 + Minecraft.World/NotGateTile.cpp | 247 + Minecraft.World/NotGateTile.h | 67 + Minecraft.World/Npc.cpp | 4 + Minecraft.World/Npc.h | 11 + Minecraft.World/NumberFormaters.h | 35 + Minecraft.World/ObsidianTile.cpp | 16 + Minecraft.World/ObsidianTile.h | 12 + Minecraft.World/OceanBiome.h | 14 + Minecraft.World/OcelotSitOnTileGoal.cpp | 127 + Minecraft.World/OcelotSitOnTileGoal.h | 37 + Minecraft.World/OfferFlowerGoal.cpp | 44 + Minecraft.World/OfferFlowerGoal.h | 25 + Minecraft.World/OldChunkStorage.cpp | 594 +++ Minecraft.World/OldChunkStorage.h | 57 + Minecraft.World/OpenDoorGoal.cpp | 35 + Minecraft.World/OpenDoorGoal.h | 18 + Minecraft.World/OreFeature.cpp | 127 + Minecraft.World/OreFeature.h | 19 + Minecraft.World/OreRecipies.cpp | 75 + Minecraft.World/OreRecipies.h | 17 + Minecraft.World/OreTile.cpp | 77 + Minecraft.World/OreTile.h | 17 + Minecraft.World/OutputStream.h | 14 + Minecraft.World/OwnerHurtByTargetGoal.cpp | 25 + Minecraft.World/OwnerHurtByTargetGoal.h | 18 + Minecraft.World/OwnerHurtTargetGoal.cpp | 25 + Minecraft.World/OwnerHurtTargetGoal.h | 18 + Minecraft.World/OxygenEnchantment.cpp | 22 + Minecraft.World/OxygenEnchantment.h | 13 + Minecraft.World/Ozelot.cpp | 333 ++ Minecraft.World/Ozelot.h | 76 + Minecraft.World/OzelotAttackGoal.cpp | 61 + Minecraft.World/OzelotAttackGoal.h | 25 + Minecraft.World/Packet.cpp | 596 +++ Minecraft.World/Packet.h | 113 + Minecraft.World/PacketListener.cpp | 451 ++ Minecraft.World/PacketListener.h | 211 + Minecraft.World/Painting.cpp | 148 + Minecraft.World/Painting.h | 117 + Minecraft.World/PanicGoal.cpp | 35 + Minecraft.World/PanicGoal.h | 20 + Minecraft.World/ParticleTypes.h | 56 + Minecraft.World/Path.cpp | 116 + Minecraft.World/Path.h | 31 + Minecraft.World/PathFinder.cpp | 271 ++ Minecraft.World/PathFinder.h | 54 + Minecraft.World/PathNavigation.cpp | 376 ++ Minecraft.World/PathNavigation.h | 67 + Minecraft.World/PathfinderMob.cpp | 264 ++ Minecraft.World/PathfinderMob.h | 49 + Minecraft.World/PerformanceTimer.cpp | 35 + Minecraft.World/PerformanceTimer.h | 13 + Minecraft.World/PerlinNoise.cpp | 98 + Minecraft.World/PerlinNoise.h | 24 + Minecraft.World/PerlinSimplexNoise.cpp | 115 + Minecraft.World/PerlinSimplexNoise.h | 23 + Minecraft.World/PickaxeItem.cpp | 62 + Minecraft.World/PickaxeItem.h | 20 + Minecraft.World/Pig.cpp | 192 + Minecraft.World/Pig.h | 58 + Minecraft.World/PigZombie.cpp | 202 + Minecraft.World/PigZombie.h | 60 + Minecraft.World/PineFeature.cpp | 102 + Minecraft.World/PineFeature.h | 8 + Minecraft.World/PistonBaseTile.cpp | 628 +++ Minecraft.World/PistonBaseTile.h | 71 + Minecraft.World/PistonExtensionTile.cpp | 210 + Minecraft.World/PistonExtensionTile.h | 31 + Minecraft.World/PistonMovingPiece.cpp | 211 + Minecraft.World/PistonMovingPiece.h | 37 + Minecraft.World/PistonPieceEntity.cpp | 215 + Minecraft.World/PistonPieceEntity.h | 40 + Minecraft.World/PistonTileItem.cpp | 14 + Minecraft.World/PistonTileItem.h | 12 + Minecraft.World/PlainsBiome.cpp | 9 + Minecraft.World/PlainsBiome.h | 10 + Minecraft.World/PlayGoal.cpp | 86 + Minecraft.World/PlayGoal.h | 22 + Minecraft.World/Player.cpp | 3002 ++++++++++++ Minecraft.World/Player.h | 551 +++ Minecraft.World/PlayerAbilitiesPacket.cpp | 137 + Minecraft.World/PlayerAbilitiesPacket.h | 50 + Minecraft.World/PlayerActionPacket.cpp | 58 + Minecraft.World/PlayerActionPacket.h | 31 + Minecraft.World/PlayerCommandPacket.cpp | 51 + Minecraft.World/PlayerCommandPacket.h | 37 + Minecraft.World/PlayerEnderChestContainer.cpp | 72 + Minecraft.World/PlayerEnderChestContainer.h | 21 + Minecraft.World/PlayerIO.h | 22 + Minecraft.World/PlayerInfoPacket.cpp | 61 + Minecraft.World/PlayerInfoPacket.h | 32 + Minecraft.World/PlayerInputPacket.cpp | 87 + Minecraft.World/PlayerInputPacket.h | 36 + Minecraft.World/PortalForcer.cpp | 381 ++ Minecraft.World/PortalForcer.h | 21 + Minecraft.World/PortalMaterial.h | 12 + Minecraft.World/PortalTile.cpp | 246 + Minecraft.World/PortalTile.h | 25 + Minecraft.World/Pos.cpp | 248 + Minecraft.World/Pos.h | 97 + Minecraft.World/PotatoTile.cpp | 60 + Minecraft.World/PotatoTile.h | 23 + Minecraft.World/PotionBrewing.cpp | 910 ++++ Minecraft.World/PotionBrewing.h | 101 + Minecraft.World/PotionItem.cpp | 372 ++ Minecraft.World/PotionItem.h | 57 + Minecraft.World/PreLoginPacket.cpp | 119 + Minecraft.World/PreLoginPacket.h | 39 + Minecraft.World/PressurePlateTile.cpp | 211 + Minecraft.World/PressurePlateTile.h | 49 + Minecraft.World/PrimedTnt.cpp | 116 + Minecraft.World/PrimedTnt.h | 37 + Minecraft.World/ProgressListener.h | 15 + Minecraft.World/ProtectionEnchantment.cpp | 94 + Minecraft.World/ProtectionEnchantment.h | 33 + Minecraft.World/PumpkinFeature.cpp | 23 + Minecraft.World/PumpkinFeature.h | 9 + Minecraft.World/PumpkinTile.cpp | 169 + Minecraft.World/PumpkinTile.h | 31 + Minecraft.World/QuartzBlockTile.cpp | 122 + Minecraft.World/QuartzBlockTile.h | 47 + Minecraft.World/RailTile.cpp | 676 +++ Minecraft.World/RailTile.h | 84 + Minecraft.World/RainforestBiome.cpp | 16 + Minecraft.World/RainforestBiome.h | 9 + Minecraft.World/Random.cpp | 106 + Minecraft.World/Random.h | 23 + Minecraft.World/RandomLevelSource.cpp | 780 ++++ Minecraft.World/RandomLevelSource.h | 92 + Minecraft.World/RandomLookAroundGoal.cpp | 37 + Minecraft.World/RandomLookAroundGoal.h | 19 + Minecraft.World/RandomPos.cpp | 88 + Minecraft.World/RandomPos.h | 17 + Minecraft.World/RandomScatteredLargeFeature.cpp | 84 + Minecraft.World/RandomScatteredLargeFeature.h | 22 + Minecraft.World/RandomStrollGoal.cpp | 60 + Minecraft.World/RandomStrollGoal.h | 20 + Minecraft.World/Rarity.cpp | 12 + Minecraft.World/Rarity.h | 15 + Minecraft.World/ReadMe.txt | 30 + Minecraft.World/ReadOnlyChunkCache.cpp | 100 + Minecraft.World/ReadOnlyChunkCache.h | 39 + Minecraft.World/Reader.h | 11 + Minecraft.World/Recipes.cpp | 1220 +++++ Minecraft.World/Recipes.h | 111 + Minecraft.World/Recipy.h | 55 + Minecraft.World/RecordPlayerTile.cpp | 100 + Minecraft.World/RecordPlayerTile.h | 68 + Minecraft.World/RecordingItem.cpp | 64 + Minecraft.World/RecordingItem.h | 23 + Minecraft.World/RedStoneDustTile.cpp | 431 ++ Minecraft.World/RedStoneDustTile.h | 61 + Minecraft.World/RedStoneItem.cpp | 40 + Minecraft.World/RedStoneItem.h | 13 + Minecraft.World/RedStoneOreTile.cpp | 128 + Minecraft.World/RedStoneOreTile.h | 34 + Minecraft.World/RedlightTile.cpp | 78 + Minecraft.World/RedlightTile.h | 19 + Minecraft.World/ReedTile.cpp | 117 + Minecraft.World/ReedTile.h | 52 + Minecraft.World/ReedsFeature.cpp | 46 + Minecraft.World/ReedsFeature.h | 9 + Minecraft.World/Reference.h | 10 + Minecraft.World/Region.cpp | 364 ++ Minecraft.World/Region.h | 51 + Minecraft.World/RegionFile.cpp | 497 ++ Minecraft.World/RegionFile.h | 97 + Minecraft.World/RegionFileCache.cpp | 122 + Minecraft.World/RegionFileCache.h | 35 + Minecraft.World/RegionHillsLayer.cpp | 78 + Minecraft.World/RegionHillsLayer.h | 11 + Minecraft.World/RemoveEntitiesPacket.cpp | 56 + Minecraft.World/RemoveEntitiesPacket.h | 27 + Minecraft.World/RemoveMobEffectPacket.cpp | 39 + Minecraft.World/RemoveMobEffectPacket.h | 23 + Minecraft.World/RepairContainer.cpp | 14 + Minecraft.World/RepairContainer.h | 15 + Minecraft.World/RepairMenu.cpp | 403 ++ Minecraft.World/RepairMenu.h | 55 + Minecraft.World/RepairResultSlot.cpp | 75 + Minecraft.World/RepairResultSlot.h | 20 + Minecraft.World/RespawnPacket.cpp | 97 + Minecraft.World/RespawnPacket.h | 34 + Minecraft.World/RestrictOpenDoorGoal.cpp | 49 + Minecraft.World/RestrictOpenDoorGoal.h | 22 + Minecraft.World/RestrictSunGoal.cpp | 25 + Minecraft.World/RestrictSunGoal.h | 16 + Minecraft.World/ResultContainer.cpp | 64 + Minecraft.World/ResultContainer.h | 26 + Minecraft.World/ResultSlot.cpp | 102 + Minecraft.World/ResultSlot.h | 26 + Minecraft.World/RiverBiome.h | 14 + Minecraft.World/RiverInitLayer.cpp | 24 + Minecraft.World/RiverInitLayer.h | 11 + Minecraft.World/RiverLayer.cpp | 40 + Minecraft.World/RiverLayer.h | 10 + Minecraft.World/RiverMixerLayer.cpp | 47 + Minecraft.World/RiverMixerLayer.h | 16 + Minecraft.World/Rotate.cpp | 15 + Minecraft.World/Rotate.h | 15 + Minecraft.World/RotateHeadPacket.cpp | 51 + Minecraft.World/RotateHeadPacket.h | 27 + Minecraft.World/SaddleItem.cpp | 31 + Minecraft.World/SaddleItem.h | 13 + Minecraft.World/SandFeature.cpp | 58 + Minecraft.World/SandFeature.h | 14 + Minecraft.World/SandStoneTile.cpp | 52 + Minecraft.World/SandStoneTile.h | 38 + Minecraft.World/Sapling.cpp | 166 + Minecraft.World/Sapling.h | 50 + Minecraft.World/SaplingTileItem.cpp | 31 + Minecraft.World/SaplingTileItem.h | 16 + Minecraft.World/SavedData.cpp | 24 + Minecraft.World/SavedData.h | 25 + Minecraft.World/SavedDataStorage.cpp | 211 + Minecraft.World/SavedDataStorage.h | 35 + Minecraft.World/Scale.cpp | 14 + Minecraft.World/Scale.h | 15 + Minecraft.World/ScatteredFeaturePieces.cpp | 537 +++ Minecraft.World/ScatteredFeaturePieces.h | 67 + Minecraft.World/SeedFoodItem.cpp | 31 + Minecraft.World/SeedFoodItem.h | 15 + Minecraft.World/SeedItem.cpp | 36 + Minecraft.World/SeedItem.h | 16 + Minecraft.World/Sensing.cpp | 35 + Minecraft.World/Sensing.h | 15 + Minecraft.World/ServerAuthDataPacket.h | 58 + Minecraft.World/ServerSettingsChangedPacket.cpp | 52 + Minecraft.World/ServerSettingsChangedPacket.h | 31 + Minecraft.World/SetCarriedItemPacket.cpp | 47 + Minecraft.World/SetCarriedItemPacket.h | 24 + Minecraft.World/SetCreativeModeSlotPacket.cpp | 42 + Minecraft.World/SetCreativeModeSlotPacket.h | 23 + Minecraft.World/SetEntityDataPacket.cpp | 64 + Minecraft.World/SetEntityDataPacket.h | 31 + Minecraft.World/SetEntityMotionPacket.cpp | 113 + Minecraft.World/SetEntityMotionPacket.h | 31 + Minecraft.World/SetEquippedItemPacket.cpp | 69 + Minecraft.World/SetEquippedItemPacket.h | 33 + Minecraft.World/SetExperiencePacket.cpp | 59 + Minecraft.World/SetExperiencePacket.h | 26 + Minecraft.World/SetHealthPacket.cpp | 66 + Minecraft.World/SetHealthPacket.h | 30 + Minecraft.World/SetRidingPacket.cpp | 53 + Minecraft.World/SetRidingPacket.h | 25 + Minecraft.World/SetSpawnPositionPacket.cpp | 60 + Minecraft.World/SetSpawnPositionPacket.h | 25 + Minecraft.World/SetTimePacket.cpp | 52 + Minecraft.World/SetTimePacket.h | 25 + Minecraft.World/ShapedRecipy.cpp | 230 + Minecraft.World/ShapedRecipy.h | 32 + Minecraft.World/ShapelessRecipy.cpp | 182 + Minecraft.World/ShapelessRecipy.h | 23 + Minecraft.World/SharedConstants.cpp | 51 + Minecraft.World/SharedConstants.h | 32 + Minecraft.World/SharedKeyPacket.h | 63 + Minecraft.World/ShearsItem.cpp | 38 + Minecraft.World/ShearsItem.h | 13 + Minecraft.World/Sheep.cpp | 337 ++ Minecraft.World/Sheep.h | 86 + Minecraft.World/ShoreLayer.cpp | 74 + Minecraft.World/ShoreLayer.h | 9 + Minecraft.World/ShortTag.h | 36 + Minecraft.World/ShovelItem.cpp | 33 + Minecraft.World/ShovelItem.h | 15 + Minecraft.World/SignItem.cpp | 59 + Minecraft.World/SignItem.h | 12 + Minecraft.World/SignTile.cpp | 129 + Minecraft.World/SignTile.h | 38 + Minecraft.World/SignTileEntity.cpp | 198 + Minecraft.World/SignTileEntity.h | 49 + Minecraft.World/SignUpdatePacket.cpp | 71 + Minecraft.World/SignUpdatePacket.h | 26 + Minecraft.World/Silverfish.cpp | 211 + Minecraft.World/Silverfish.h | 49 + Minecraft.World/SimpleContainer.cpp | 110 + Minecraft.World/SimpleContainer.h | 42 + Minecraft.World/SimplexNoise.cpp | 476 ++ Minecraft.World/SimplexNoise.h | 40 + Minecraft.World/SitGoal.cpp | 45 + Minecraft.World/SitGoal.h | 18 + Minecraft.World/Skeleton.cpp | 154 + Minecraft.World/Skeleton.h | 39 + Minecraft.World/SkullItem.cpp | 141 + Minecraft.World/SkullItem.h | 29 + Minecraft.World/SkullTile.cpp | 270 ++ Minecraft.World/SkullTile.h | 44 + Minecraft.World/SkullTileEntity.cpp | 72 + Minecraft.World/SkullTileEntity.h | 36 + Minecraft.World/SkyIslandDimension.cpp | 63 + Minecraft.World/Slime.cpp | 287 ++ Minecraft.World/Slime.h | 78 + Minecraft.World/Slot.cpp | 163 + Minecraft.World/Slot.h | 41 + Minecraft.World/SmallFireball.cpp | 80 + Minecraft.World/SmallFireball.h | 24 + Minecraft.World/SmoothFloat.cpp | 29 + Minecraft.World/SmoothFloat.h | 13 + Minecraft.World/SmoothLayer.cpp | 45 + Minecraft.World/SmoothLayer.h | 11 + Minecraft.World/SmoothStoneBrickTile.cpp | 43 + Minecraft.World/SmoothStoneBrickTile.h | 33 + Minecraft.World/SmoothStoneBrickTileItem.cpp | 32 + Minecraft.World/SmoothStoneBrickTileItem.h | 17 + Minecraft.World/SmoothZoomLayer.cpp | 61 + Minecraft.World/SmoothZoomLayer.h | 12 + Minecraft.World/SnowMan.cpp | 97 + Minecraft.World/SnowMan.h | 21 + Minecraft.World/SnowTile.cpp | 35 + Minecraft.World/SnowTile.h | 20 + Minecraft.World/Snowball.cpp | 52 + Minecraft.World/Snowball.h | 24 + Minecraft.World/SnowballItem.cpp | 23 + Minecraft.World/SnowballItem.h | 12 + Minecraft.World/Socket.cpp | 534 +++ Minecraft.World/Socket.h | 134 + Minecraft.World/SocketAddress.h | 6 + Minecraft.World/SoundTypes.h | 304 ++ Minecraft.World/SparseDataStorage.cpp | 625 +++ Minecraft.World/SparseDataStorage.h | 84 + Minecraft.World/SparseLightStorage.cpp | 642 +++ Minecraft.World/SparseLightStorage.h | 87 + Minecraft.World/Spider.cpp | 192 + Minecraft.World/Spider.h | 45 + Minecraft.World/SpikeFeature.cpp | 179 + Minecraft.World/SpikeFeature.h | 14 + Minecraft.World/Sponge.cpp | 11 + Minecraft.World/Sponge.h | 13 + Minecraft.World/SpringFeature.cpp | 51 + Minecraft.World/SpringFeature.h | 14 + Minecraft.World/SpringTile.cpp | 29 + Minecraft.World/SpringTile.h | 17 + Minecraft.World/SpruceFeature.cpp | 116 + Minecraft.World/SpruceFeature.h | 9 + Minecraft.World/Squid.cpp | 189 + Minecraft.World/Squid.h | 52 + Minecraft.World/StairTile.cpp | 567 +++ Minecraft.World/StairTile.h | 106 + Minecraft.World/Stat.cpp | 115 + Minecraft.World/Stat.h | 61 + Minecraft.World/StatFormatter.h | 8 + Minecraft.World/Stats.cpp | 557 +++ Minecraft.World/Stats.h | 94 + Minecraft.World/StemTile.cpp | 234 + Minecraft.World/StemTile.h | 48 + Minecraft.World/StoneMonsterTile.cpp | 114 + Minecraft.World/StoneMonsterTile.h | 38 + Minecraft.World/StoneMonsterTileItem.cpp | 30 + Minecraft.World/StoneMonsterTileItem.h | 16 + Minecraft.World/StoneSlabTile.cpp | 84 + Minecraft.World/StoneSlabTile.h | 39 + Minecraft.World/StoneSlabTileItem.cpp | 140 + Minecraft.World/StoneSlabTileItem.h | 24 + Minecraft.World/StoneTile.cpp | 11 + Minecraft.World/StoneTile.h | 11 + Minecraft.World/StringHelpers.cpp | 133 + Minecraft.World/StringHelpers.h | 40 + Minecraft.World/StringTag.h | 42 + Minecraft.World/StrongholdFeature.cpp | 224 + Minecraft.World/StrongholdFeature.h | 45 + Minecraft.World/StrongholdPieces.cpp | 1517 ++++++ Minecraft.World/StrongholdPieces.h | 395 ++ Minecraft.World/StructureFeature.cpp | 208 + Minecraft.World/StructureFeature.h | 58 + Minecraft.World/StructurePiece.cpp | 822 ++++ Minecraft.World/StructurePiece.h | 117 + Minecraft.World/StructureRecipies.cpp | 128 + Minecraft.World/StructureRecipies.h | 7 + Minecraft.World/StructureStart.cpp | 111 + Minecraft.World/StructureStart.h | 25 + Minecraft.World/SwampBiome.cpp | 40 + Minecraft.World/SwampBiome.h | 18 + Minecraft.World/SwampRiversLayer.cpp | 34 + Minecraft.World/SwampRiversLayer.h | 11 + Minecraft.World/SwampTreeFeature.cpp | 132 + Minecraft.World/SwampTreeFeature.h | 11 + Minecraft.World/SwellGoal.cpp | 54 + Minecraft.World/SwellGoal.h | 20 + Minecraft.World/SynchedEntityData.cpp | 558 +++ Minecraft.World/SynchedEntityData.h | 127 + Minecraft.World/Synth.cpp | 15 + Minecraft.World/Synth.h | 9 + Minecraft.World/System.h | 39 + Minecraft.World/Tag.cpp | 181 + Minecraft.World/Tag.h | 45 + Minecraft.World/TaigaBiome.cpp | 22 + Minecraft.World/TaigaBiome.h | 10 + Minecraft.World/TakeFlowerGoal.cpp | 82 + Minecraft.World/TakeFlowerGoal.h | 21 + Minecraft.World/TakeItemEntityPacket.cpp | 41 + Minecraft.World/TakeItemEntityPacket.h | 22 + Minecraft.World/TallGrass.cpp | 119 + Minecraft.World/TallGrass.h | 43 + Minecraft.World/TallGrassFeature.cpp | 33 + Minecraft.World/TallGrassFeature.h | 14 + Minecraft.World/TamableAnimal.cpp | 159 + Minecraft.World/TamableAnimal.h | 38 + Minecraft.World/TargetGoal.cpp | 113 + Minecraft.World/TargetGoal.h | 44 + Minecraft.World/TeleportEntityPacket.cpp | 91 + Minecraft.World/TeleportEntityPacket.h | 27 + Minecraft.World/TemperatureLayer.cpp | 20 + Minecraft.World/TemperatureLayer.h | 11 + Minecraft.World/TemperatureMixerLayer.cpp | 23 + Minecraft.World/TemperatureMixerLayer.h | 15 + Minecraft.World/TemptGoal.cpp | 91 + Minecraft.World/TemptGoal.h | 27 + Minecraft.World/TextureAndGeometryChangePacket.cpp | 53 + Minecraft.World/TextureAndGeometryChangePacket.h | 25 + Minecraft.World/TextureAndGeometryPacket.cpp | 189 + Minecraft.World/TextureAndGeometryPacket.h | 35 + Minecraft.World/TextureChangePacket.cpp | 46 + Minecraft.World/TextureChangePacket.h | 30 + Minecraft.World/TexturePacket.cpp | 66 + Minecraft.World/TexturePacket.h | 25 + Minecraft.World/TheEndBiome.cpp | 26 + Minecraft.World/TheEndBiome.h | 11 + Minecraft.World/TheEndBiomeDecorator.cpp | 71 + Minecraft.World/TheEndBiomeDecorator.h | 24 + Minecraft.World/TheEndDimension.cpp | 91 + Minecraft.World/TheEndDimension.h | 20 + Minecraft.World/TheEndLevelRandomLevelSource.cpp | 421 ++ Minecraft.World/TheEndLevelRandomLevelSource.h | 60 + Minecraft.World/TheEndPortal.cpp | 126 + Minecraft.World/TheEndPortal.h | 27 + Minecraft.World/TheEndPortalFrameTile.cpp | 83 + Minecraft.World/TheEndPortalFrameTile.h | 26 + Minecraft.World/TheEndPortalTileEntity.cpp | 10 + Minecraft.World/TheEndPortalTileEntity.h | 12 + Minecraft.World/ThinFenceTile.cpp | 154 + Minecraft.World/ThinFenceTile.h | 32 + Minecraft.World/ThornsEnchantment.cpp | 77 + Minecraft.World/ThornsEnchantment.h | 20 + Minecraft.World/ThreadName.cpp | 41 + Minecraft.World/ThreadName.h | 3 + Minecraft.World/Throwable.cpp | 281 ++ Minecraft.World/Throwable.h | 60 + Minecraft.World/ThrownEgg.cpp | 67 + Minecraft.World/ThrownEgg.h | 23 + Minecraft.World/ThrownEnderpearl.cpp | 62 + Minecraft.World/ThrownEnderpearl.h | 19 + Minecraft.World/ThrownExpBottle.cpp | 55 + Minecraft.World/ThrownExpBottle.h | 22 + Minecraft.World/ThrownPotion.cpp | 136 + Minecraft.World/ThrownPotion.h | 43 + Minecraft.World/TickNextTickData.cpp | 69 + Minecraft.World/TickNextTickData.h | 46 + Minecraft.World/Tile.cpp | 1632 +++++++ Minecraft.World/Tile.h | 648 +++ Minecraft.World/TileDestructionPacket.cpp | 85 + Minecraft.World/TileDestructionPacket.h | 35 + Minecraft.World/TileEntity.cpp | 211 + Minecraft.World/TileEntity.h | 74 + Minecraft.World/TileEntityDataPacket.cpp | 65 + Minecraft.World/TileEntityDataPacket.h | 36 + Minecraft.World/TileEventData.cpp | 48 + Minecraft.World/TileEventData.h | 21 + Minecraft.World/TileEventPacket.cpp | 56 + Minecraft.World/TileEventPacket.h | 22 + Minecraft.World/TileItem.cpp | 221 + Minecraft.World/TileItem.h | 44 + Minecraft.World/TilePlanterItem.cpp | 80 + Minecraft.World/TilePlanterItem.h | 15 + Minecraft.World/TilePos.cpp | 29 + Minecraft.World/TilePos.h | 27 + Minecraft.World/TileUpdatePacket.cpp | 92 + Minecraft.World/TileUpdatePacket.h | 23 + Minecraft.World/TimeCommand.cpp | 80 + Minecraft.World/TimeCommand.h | 17 + Minecraft.World/TntTile.cpp | 120 + Minecraft.World/TntTile.h | 31 + Minecraft.World/ToggleDownfallCommand.cpp | 30 + Minecraft.World/ToggleDownfallCommand.h | 17 + Minecraft.World/ToolRecipies.cpp | 128 + Minecraft.World/ToolRecipies.h | 24 + Minecraft.World/TopSnowTile.cpp | 182 + Minecraft.World/TopSnowTile.h | 66 + Minecraft.World/TorchTile.cpp | 238 + Minecraft.World/TorchTile.h | 36 + Minecraft.World/TownFeature.h | 5 + Minecraft.World/TradeItemPacket.cpp | 41 + Minecraft.World/TradeItemPacket.h | 32 + Minecraft.World/TradeWithPlayerGoal.cpp | 51 + Minecraft.World/TradeWithPlayerGoal.h | 18 + Minecraft.World/TransparentTile.cpp | 25 + Minecraft.World/TransparentTile.h | 14 + Minecraft.World/TrapDoorTile.cpp | 209 + Minecraft.World/TrapDoorTile.h | 87 + Minecraft.World/TrapMenu.cpp | 81 + Minecraft.World/TrapMenu.h | 22 + Minecraft.World/TreeFeature.cpp | 179 + Minecraft.World/TreeFeature.h | 20 + Minecraft.World/TreeTile.cpp | 141 + Minecraft.World/TreeTile.h | 55 + Minecraft.World/TreeTileItem.cpp | 31 + Minecraft.World/TreeTileItem.h | 18 + Minecraft.World/TripWireSourceTile.cpp | 359 ++ Minecraft.World/TripWireSourceTile.h | 43 + Minecraft.World/TripWireTile.cpp | 221 + Minecraft.World/TripWireTile.h | 43 + Minecraft.World/UntouchingEnchantment.cpp | 34 + Minecraft.World/UntouchingEnchantment.h | 15 + Minecraft.World/UpdateGameRuleProgressPacket.cpp | 75 + Minecraft.World/UpdateGameRuleProgressPacket.h | 26 + Minecraft.World/UpdateMobEffectPacket.cpp | 60 + Minecraft.World/UpdateMobEffectPacket.h | 28 + Minecraft.World/UpdateProgressPacket.cpp | 37 + Minecraft.World/UpdateProgressPacket.h | 25 + Minecraft.World/UseAnim.h | 10 + Minecraft.World/UseItemPacket.cpp | 112 + Minecraft.World/UseItemPacket.h | 36 + Minecraft.World/Vec3.cpp | 265 ++ Minecraft.World/Vec3.h | 59 + Minecraft.World/Village.cpp | 512 ++ Minecraft.World/Village.h | 81 + Minecraft.World/VillageFeature.cpp | 144 + Minecraft.World/VillageFeature.h | 31 + Minecraft.World/VillagePieces.cpp | 1847 ++++++++ Minecraft.World/VillagePieces.h | 353 ++ Minecraft.World/VillageSiege.cpp | 169 + Minecraft.World/VillageSiege.h | 28 + Minecraft.World/Villager.cpp | 782 ++++ Minecraft.World/Villager.h | 149 + Minecraft.World/VillagerGolem.cpp | 240 + Minecraft.World/VillagerGolem.h | 64 + Minecraft.World/Villages.cpp | 253 + Minecraft.World/Villages.h | 47 + Minecraft.World/VineTile.cpp | 387 ++ Minecraft.World/VineTile.h | 38 + Minecraft.World/VinesFeature.cpp | 39 + Minecraft.World/VinesFeature.h | 12 + Minecraft.World/VoronoiZoom.cpp | 122 + Minecraft.World/VoronoiZoom.h | 15 + Minecraft.World/WallTile.cpp | 189 + Minecraft.World/WallTile.h | 31 + Minecraft.World/WaterAnimal.cpp | 42 + Minecraft.World/WaterAnimal.h | 18 + Minecraft.World/WaterColor.cpp | 23 + Minecraft.World/WaterColor.h | 14 + Minecraft.World/WaterLevelChunk.cpp | 159 + Minecraft.World/WaterLevelChunk.h | 45 + Minecraft.World/WaterLilyTile.cpp | 76 + Minecraft.World/WaterLilyTile.h | 24 + Minecraft.World/WaterLilyTileItem.cpp | 78 + Minecraft.World/WaterLilyTileItem.h | 14 + Minecraft.World/WaterWorkerEnchantment.cpp | 22 + Minecraft.World/WaterWorkerEnchantment.h | 13 + Minecraft.World/WaterlilyFeature.cpp | 23 + Minecraft.World/WaterlilyFeature.h | 7 + Minecraft.World/WeaponItem.cpp | 87 + Minecraft.World/WeaponItem.h | 28 + Minecraft.World/WeaponRecipies.cpp | 107 + Minecraft.World/WeaponRecipies.h | 22 + Minecraft.World/WebMaterial.h | 9 + Minecraft.World/WebTile.cpp | 53 + Minecraft.World/WebTile.h | 31 + Minecraft.World/WeighedRandom.cpp | 72 + Minecraft.World/WeighedRandom.h | 28 + Minecraft.World/WeighedTreasure.cpp | 86 + Minecraft.World/WeighedTreasure.h | 19 + Minecraft.World/Wolf.cpp | 550 +++ Minecraft.World/Wolf.h | 86 + Minecraft.World/WoodSlabTile.cpp | 60 + Minecraft.World/WoodSlabTile.h | 28 + Minecraft.World/WoodTile.cpp | 56 + Minecraft.World/WoodTile.h | 26 + Minecraft.World/WoolCarpetTile.cpp | 111 + Minecraft.World/WoolCarpetTile.h | 37 + Minecraft.World/WorkbenchTile.cpp | 44 + Minecraft.World/WorkbenchTile.h | 26 + Minecraft.World/XZPacket.cpp | 52 + Minecraft.World/XZPacket.h | 30 + Minecraft.World/Zombie.cpp | 383 ++ Minecraft.World/Zombie.h | 88 + Minecraft.World/ZoneFile.cpp | 92 + Minecraft.World/ZoneFile.h | 42 + Minecraft.World/ZoneIo.cpp | 48 + Minecraft.World/ZoneIo.h | 19 + Minecraft.World/ZonedChunkStorage.cpp | 264 ++ Minecraft.World/ZonedChunkStorage.h | 53 + Minecraft.World/ZoomLayer.cpp | 92 + Minecraft.World/ZoomLayer.h | 18 + Minecraft.World/com.mojang.nbt.h | 1 + Minecraft.World/compression.cpp | 546 +++ Minecraft.World/compression.h | 86 + Minecraft.World/net.minecraft.commands.common.h | 10 + Minecraft.World/net.minecraft.commands.h | 7 + Minecraft.World/net.minecraft.h | 6 + Minecraft.World/net.minecraft.locale.h | 4 + Minecraft.World/net.minecraft.network.h | 3 + Minecraft.World/net.minecraft.network.packet.h | 102 + Minecraft.World/net.minecraft.stats.h | 10 + .../net.minecraft.world.ContainerListener.h | 18 + Minecraft.World/net.minecraft.world.damagesource.h | 5 + Minecraft.World/net.minecraft.world.effect.h | 5 + .../net.minecraft.world.entity.ai.control.h | 7 + .../net.minecraft.world.entity.ai.goal.h | 41 + .../net.minecraft.world.entity.ai.goal.target.h | 9 + .../net.minecraft.world.entity.ai.navigation.h | 3 + .../net.minecraft.world.entity.ai.sensing.h | 3 + .../net.minecraft.world.entity.ai.util.h | 3 + .../net.minecraft.world.entity.ai.village.h | 6 + .../net.minecraft.world.entity.animal.h | 20 + .../net.minecraft.world.entity.boss.enderdragon.h | 5 + Minecraft.World/net.minecraft.world.entity.boss.h | 4 + .../net.minecraft.world.entity.global.h | 4 + Minecraft.World/net.minecraft.world.entity.h | 26 + Minecraft.World/net.minecraft.world.entity.item.h | 7 + .../net.minecraft.world.entity.monster.h | 21 + Minecraft.World/net.minecraft.world.entity.npc.h | 5 + .../net.minecraft.world.entity.player.h | 5 + .../net.minecraft.world.entity.projectile.h | 18 + Minecraft.World/net.minecraft.world.food.h | 4 + Minecraft.World/net.minecraft.world.h | 13 + ...t.minecraft.world.inventory.ContainerListener.h | 21 + Minecraft.World/net.minecraft.world.inventory.h | 27 + Minecraft.World/net.minecraft.world.item.alchemy.h | 3 + .../net.minecraft.world.item.crafting.h | 15 + .../net.minecraft.world.item.enchantment.h | 24 + Minecraft.World/net.minecraft.world.item.h | 80 + Minecraft.World/net.minecraft.world.item.trading.h | 5 + Minecraft.World/net.minecraft.world.level.biome.h | 32 + Minecraft.World/net.minecraft.world.level.chunk.h | 8 + .../net.minecraft.world.level.chunk.storage.h | 13 + .../net.minecraft.world.level.dimension.h | 6 + Minecraft.World/net.minecraft.world.level.h | 25 + .../net.minecraft.world.level.levelgen.feature.h | 37 + .../net.minecraft.world.level.levelgen.h | 15 + .../net.minecraft.world.level.levelgen.structure.h | 16 + .../net.minecraft.world.level.levelgen.synth.h | 12 + .../net.minecraft.world.level.material.h | 8 + .../net.minecraft.world.level.newbiome.layer.h | 28 + .../net.minecraft.world.level.pathfinder.h | 6 + .../net.minecraft.world.level.saveddata.h | 4 + .../net.minecraft.world.level.storage.h | 16 + .../net.minecraft.world.level.tile.entity.h | 15 + Minecraft.World/net.minecraft.world.level.tile.h | 114 + .../net.minecraft.world.level.tile.piston.h | 6 + Minecraft.World/net.minecraft.world.phys.h | 3 + Minecraft.World/stdafx.cpp | 8 + Minecraft.World/stdafx.h | 247 + Minecraft.World/system.cpp | 200 + Minecraft.World/x64headers/extraX64.h | 640 +++ Minecraft.World/x64headers/qnet.h | 0 Minecraft.World/x64headers/xmcore.h | 0 Minecraft.World/x64headers/xrnm.h | 0 Minecraft.World/x64headers/xsocialpost.h | 0 Minecraft.World/x64headers/xuiapp.h | 0 Minecraft.World/x64headers/xuiresource.h | 0 1572 files changed, 173061 insertions(+) create mode 100644 Minecraft.World/AABB.cpp create mode 100644 Minecraft.World/AABB.h create mode 100644 Minecraft.World/Abilities.cpp create mode 100644 Minecraft.World/Abilities.h create mode 100644 Minecraft.World/AbstractContainerMenu.cpp create mode 100644 Minecraft.World/AbstractContainerMenu.h create mode 100644 Minecraft.World/Achievement.cpp create mode 100644 Minecraft.World/Achievement.h create mode 100644 Minecraft.World/Achievements.cpp create mode 100644 Minecraft.World/Achievements.h create mode 100644 Minecraft.World/AddEntityPacket.cpp create mode 100644 Minecraft.World/AddEntityPacket.h create mode 100644 Minecraft.World/AddExperienceOrbPacket.cpp create mode 100644 Minecraft.World/AddExperienceOrbPacket.h create mode 100644 Minecraft.World/AddGlobalEntityPacket.cpp create mode 100644 Minecraft.World/AddGlobalEntityPacket.h create mode 100644 Minecraft.World/AddIslandLayer.cpp create mode 100644 Minecraft.World/AddIslandLayer.h create mode 100644 Minecraft.World/AddMobPacket.cpp create mode 100644 Minecraft.World/AddMobPacket.h create mode 100644 Minecraft.World/AddMushroomIslandLayer.cpp create mode 100644 Minecraft.World/AddMushroomIslandLayer.h create mode 100644 Minecraft.World/AddPaintingPacket.cpp create mode 100644 Minecraft.World/AddPaintingPacket.h create mode 100644 Minecraft.World/AddPlayerPacket.cpp create mode 100644 Minecraft.World/AddPlayerPacket.h create mode 100644 Minecraft.World/AddSnowLayer.cpp create mode 100644 Minecraft.World/AddSnowLayer.h create mode 100644 Minecraft.World/AdminLogCommand.h create mode 100644 Minecraft.World/AgableMob.cpp create mode 100644 Minecraft.World/AgableMob.h create mode 100644 Minecraft.World/AirTile.cpp create mode 100644 Minecraft.World/AirTile.h create mode 100644 Minecraft.World/Animal.cpp create mode 100644 Minecraft.World/Animal.h create mode 100644 Minecraft.World/AnimatePacket.cpp create mode 100644 Minecraft.World/AnimatePacket.h create mode 100644 Minecraft.World/AnvilTile.cpp create mode 100644 Minecraft.World/AnvilTile.h create mode 100644 Minecraft.World/AnvilTileItem.cpp create mode 100644 Minecraft.World/AnvilTileItem.h create mode 100644 Minecraft.World/ArmorDyeRecipe.cpp create mode 100644 Minecraft.World/ArmorDyeRecipe.h create mode 100644 Minecraft.World/ArmorItem.cpp create mode 100644 Minecraft.World/ArmorItem.h create mode 100644 Minecraft.World/ArmorRecipes.cpp create mode 100644 Minecraft.World/ArmorRecipes.h create mode 100644 Minecraft.World/ArmorSlot.cpp create mode 100644 Minecraft.World/ArmorSlot.h create mode 100644 Minecraft.World/ArrayWithLength.h create mode 100644 Minecraft.World/Arrays.h create mode 100644 Minecraft.World/Arrow.cpp create mode 100644 Minecraft.World/Arrow.h create mode 100644 Minecraft.World/ArrowAttackGoal.cpp create mode 100644 Minecraft.World/ArrowAttackGoal.h create mode 100644 Minecraft.World/ArrowDamageEnchantment.cpp create mode 100644 Minecraft.World/ArrowDamageEnchantment.h create mode 100644 Minecraft.World/ArrowFireEnchantment.cpp create mode 100644 Minecraft.World/ArrowFireEnchantment.h create mode 100644 Minecraft.World/ArrowInfiniteEnchantment.cpp create mode 100644 Minecraft.World/ArrowInfiniteEnchantment.h create mode 100644 Minecraft.World/ArrowKnockbackEnchantment.cpp create mode 100644 Minecraft.World/ArrowKnockbackEnchantment.h create mode 100644 Minecraft.World/AuxDataTileItem.cpp create mode 100644 Minecraft.World/AuxDataTileItem.h create mode 100644 Minecraft.World/AvoidPlayerGoal.cpp create mode 100644 Minecraft.World/AvoidPlayerGoal.h create mode 100644 Minecraft.World/AwardStatPacket.cpp create mode 100644 Minecraft.World/AwardStatPacket.h create mode 100644 Minecraft.World/BasicTree.cpp create mode 100644 Minecraft.World/BasicTree.h create mode 100644 Minecraft.World/BasicTypeContainers.cpp create mode 100644 Minecraft.World/BasicTypeContainers.h create mode 100644 Minecraft.World/BeachBiome.cpp create mode 100644 Minecraft.World/BeachBiome.h create mode 100644 Minecraft.World/BedItem.cpp create mode 100644 Minecraft.World/BedItem.h create mode 100644 Minecraft.World/BedTile.cpp create mode 100644 Minecraft.World/BedTile.h create mode 100644 Minecraft.World/BegGoal.cpp create mode 100644 Minecraft.World/BegGoal.h create mode 100644 Minecraft.World/BinaryHeap.cpp create mode 100644 Minecraft.World/BinaryHeap.h create mode 100644 Minecraft.World/Biome.cpp create mode 100644 Minecraft.World/Biome.h create mode 100644 Minecraft.World/BiomeCache.cpp create mode 100644 Minecraft.World/BiomeCache.h create mode 100644 Minecraft.World/BiomeDecorator.cpp create mode 100644 Minecraft.World/BiomeDecorator.h create mode 100644 Minecraft.World/BiomeInitLayer.cpp create mode 100644 Minecraft.World/BiomeInitLayer.h create mode 100644 Minecraft.World/BiomeOverrideLayer.cpp create mode 100644 Minecraft.World/BiomeOverrideLayer.h create mode 100644 Minecraft.World/BiomeSource.cpp create mode 100644 Minecraft.World/BiomeSource.h create mode 100644 Minecraft.World/BirchFeature.cpp create mode 100644 Minecraft.World/BirchFeature.h create mode 100644 Minecraft.World/Blaze.cpp create mode 100644 Minecraft.World/Blaze.h create mode 100644 Minecraft.World/BlockDestructionProgress.cpp create mode 100644 Minecraft.World/BlockDestructionProgress.h create mode 100644 Minecraft.World/BlockGenMethods.cpp create mode 100644 Minecraft.World/BlockGenMethods.h create mode 100644 Minecraft.World/BlockRegionUpdatePacket.cpp create mode 100644 Minecraft.World/BlockRegionUpdatePacket.h create mode 100644 Minecraft.World/BlockReplacements.cpp create mode 100644 Minecraft.World/BlockReplacements.h create mode 100644 Minecraft.World/Boat.cpp create mode 100644 Minecraft.World/Boat.h create mode 100644 Minecraft.World/BoatItem.cpp create mode 100644 Minecraft.World/BoatItem.h create mode 100644 Minecraft.World/BodyControl.cpp create mode 100644 Minecraft.World/BodyControl.h create mode 100644 Minecraft.World/BonusChestFeature.cpp create mode 100644 Minecraft.World/BonusChestFeature.h create mode 100644 Minecraft.World/BookItem.cpp create mode 100644 Minecraft.World/BookItem.h create mode 100644 Minecraft.World/BookshelfTile.cpp create mode 100644 Minecraft.World/BookshelfTile.h create mode 100644 Minecraft.World/BossMob.cpp create mode 100644 Minecraft.World/BossMob.h create mode 100644 Minecraft.World/BossMobPart.cpp create mode 100644 Minecraft.World/BossMobPart.h create mode 100644 Minecraft.World/BottleItem.cpp create mode 100644 Minecraft.World/BottleItem.h create mode 100644 Minecraft.World/BoundingBox.cpp create mode 100644 Minecraft.World/BoundingBox.h create mode 100644 Minecraft.World/BowItem.cpp create mode 100644 Minecraft.World/BowItem.h create mode 100644 Minecraft.World/BowlFoodItem.cpp create mode 100644 Minecraft.World/BowlFoodItem.h create mode 100644 Minecraft.World/BreakDoorGoal.cpp create mode 100644 Minecraft.World/BreakDoorGoal.h create mode 100644 Minecraft.World/BreedGoal.cpp create mode 100644 Minecraft.World/BreedGoal.h create mode 100644 Minecraft.World/BrewingStandMenu.cpp create mode 100644 Minecraft.World/BrewingStandMenu.h create mode 100644 Minecraft.World/BrewingStandTile.cpp create mode 100644 Minecraft.World/BrewingStandTile.h create mode 100644 Minecraft.World/BrewingStandTileEntity.cpp create mode 100644 Minecraft.World/BrewingStandTileEntity.h create mode 100644 Minecraft.World/BucketItem.cpp create mode 100644 Minecraft.World/BucketItem.h create mode 100644 Minecraft.World/Buffer.cpp create mode 100644 Minecraft.World/Buffer.h create mode 100644 Minecraft.World/BufferedOutputStream.cpp create mode 100644 Minecraft.World/BufferedOutputStream.h create mode 100644 Minecraft.World/BufferedReader.cpp create mode 100644 Minecraft.World/BufferedReader.h create mode 100644 Minecraft.World/Bush.cpp create mode 100644 Minecraft.World/Bush.h create mode 100644 Minecraft.World/ButtonTile.cpp create mode 100644 Minecraft.World/ButtonTile.h create mode 100644 Minecraft.World/ByteArrayInputStream.cpp create mode 100644 Minecraft.World/ByteArrayInputStream.h create mode 100644 Minecraft.World/ByteArrayOutputStream.cpp create mode 100644 Minecraft.World/ByteArrayOutputStream.h create mode 100644 Minecraft.World/ByteArrayTag.h create mode 100644 Minecraft.World/ByteBuffer.cpp create mode 100644 Minecraft.World/ByteBuffer.h create mode 100644 Minecraft.World/ByteTag.h create mode 100644 Minecraft.World/C4JThread.cpp create mode 100644 Minecraft.World/C4JThread.h create mode 100644 Minecraft.World/CactusFeature.cpp create mode 100644 Minecraft.World/CactusFeature.h create mode 100644 Minecraft.World/CactusTile.cpp create mode 100644 Minecraft.World/CactusTile.h create mode 100644 Minecraft.World/CakeTile.cpp create mode 100644 Minecraft.World/CakeTile.h create mode 100644 Minecraft.World/CanyonFeature.cpp create mode 100644 Minecraft.World/CanyonFeature.h create mode 100644 Minecraft.World/CarrotOnAStickItem.cpp create mode 100644 Minecraft.World/CarrotOnAStickItem.h create mode 100644 Minecraft.World/CarrotTile.cpp create mode 100644 Minecraft.World/CarrotTile.h create mode 100644 Minecraft.World/CauldronTile.cpp create mode 100644 Minecraft.World/CauldronTile.h create mode 100644 Minecraft.World/CaveFeature.cpp create mode 100644 Minecraft.World/CaveFeature.h create mode 100644 Minecraft.World/CaveSpider.cpp create mode 100644 Minecraft.World/CaveSpider.h create mode 100644 Minecraft.World/ChatAutoCompletePacket.h create mode 100644 Minecraft.World/ChatPacket.cpp create mode 100644 Minecraft.World/ChatPacket.h create mode 100644 Minecraft.World/ChestTile.cpp create mode 100644 Minecraft.World/ChestTile.h create mode 100644 Minecraft.World/ChestTileEntity.cpp create mode 100644 Minecraft.World/ChestTileEntity.h create mode 100644 Minecraft.World/Chicken.cpp create mode 100644 Minecraft.World/Chicken.h create mode 100644 Minecraft.World/ChunkPos.cpp create mode 100644 Minecraft.World/ChunkPos.h create mode 100644 Minecraft.World/ChunkSource.h create mode 100644 Minecraft.World/ChunkStorage.h create mode 100644 Minecraft.World/ChunkStorageProfileDecorator.cpp create mode 100644 Minecraft.World/ChunkStorageProfileDecorator.h create mode 100644 Minecraft.World/ChunkTilesUpdatePacket.cpp create mode 100644 Minecraft.World/ChunkTilesUpdatePacket.h create mode 100644 Minecraft.World/ChunkVisibilityAreaPacket.cpp create mode 100644 Minecraft.World/ChunkVisibilityAreaPacket.h create mode 100644 Minecraft.World/ChunkVisibilityPacket.cpp create mode 100644 Minecraft.World/ChunkVisibilityPacket.h create mode 100644 Minecraft.World/Class.cpp create mode 100644 Minecraft.World/Class.h create mode 100644 Minecraft.World/ClayFeature.cpp create mode 100644 Minecraft.World/ClayFeature.h create mode 100644 Minecraft.World/ClayTile.cpp create mode 100644 Minecraft.World/ClayTile.h create mode 100644 Minecraft.World/ClientCommandPacket.cpp create mode 100644 Minecraft.World/ClientCommandPacket.h create mode 100644 Minecraft.World/ClientInformationPacket.h create mode 100644 Minecraft.World/ClientProtocolPacket.h create mode 100644 Minecraft.World/ClientSideMerchant.cpp create mode 100644 Minecraft.World/ClientSideMerchant.h create mode 100644 Minecraft.World/ClockItem.cpp create mode 100644 Minecraft.World/ClockItem.h create mode 100644 Minecraft.World/ClothDyeRecipes.cpp create mode 100644 Minecraft.World/ClothDyeRecipes.h create mode 100644 Minecraft.World/ClothTile.cpp create mode 100644 Minecraft.World/ClothTile.h create mode 100644 Minecraft.World/ClothTileItem.cpp create mode 100644 Minecraft.World/ClothTileItem.h create mode 100644 Minecraft.World/CoalItem.cpp create mode 100644 Minecraft.World/CoalItem.h create mode 100644 Minecraft.World/CocoaTile.cpp create mode 100644 Minecraft.World/CocoaTile.h create mode 100644 Minecraft.World/Color.cpp create mode 100644 Minecraft.World/Color.h create mode 100644 Minecraft.World/ColoredTileItem.cpp create mode 100644 Minecraft.World/ColoredTileItem.h create mode 100644 Minecraft.World/Command.cpp create mode 100644 Minecraft.World/Command.h create mode 100644 Minecraft.World/CommandDispatcher.cpp create mode 100644 Minecraft.World/CommandDispatcher.h create mode 100644 Minecraft.World/CommandSender.h create mode 100644 Minecraft.World/CommandsEnum.h create mode 100644 Minecraft.World/CommonStats.cpp create mode 100644 Minecraft.World/CommonStats.h create mode 100644 Minecraft.World/CompassItem.cpp create mode 100644 Minecraft.World/CompassItem.h create mode 100644 Minecraft.World/ComplexItem.cpp create mode 100644 Minecraft.World/ComplexItem.h create mode 100644 Minecraft.World/ComplexItemDataPacket.cpp create mode 100644 Minecraft.World/ComplexItemDataPacket.h create mode 100644 Minecraft.World/CompoundContainer.cpp create mode 100644 Minecraft.World/CompoundContainer.h create mode 100644 Minecraft.World/CompoundTag.h create mode 100644 Minecraft.World/CompressedTileStorage.cpp create mode 100644 Minecraft.World/CompressedTileStorage.h create mode 100644 Minecraft.World/Connection.cpp create mode 100644 Minecraft.World/Connection.h create mode 100644 Minecraft.World/ConsoleSaveFile.h create mode 100644 Minecraft.World/ConsoleSaveFileConverter.cpp create mode 100644 Minecraft.World/ConsoleSaveFileConverter.h create mode 100644 Minecraft.World/ConsoleSaveFileIO.h create mode 100644 Minecraft.World/ConsoleSaveFileInputStream.cpp create mode 100644 Minecraft.World/ConsoleSaveFileInputStream.h create mode 100644 Minecraft.World/ConsoleSaveFileOriginal.cpp create mode 100644 Minecraft.World/ConsoleSaveFileOriginal.h create mode 100644 Minecraft.World/ConsoleSaveFileOutputStream.cpp create mode 100644 Minecraft.World/ConsoleSaveFileOutputStream.h create mode 100644 Minecraft.World/ConsoleSaveFileSplit.cpp create mode 100644 Minecraft.World/ConsoleSaveFileSplit.h create mode 100644 Minecraft.World/ConsoleSavePath.h create mode 100644 Minecraft.World/Container.cpp create mode 100644 Minecraft.World/Container.h create mode 100644 Minecraft.World/ContainerAckPacket.cpp create mode 100644 Minecraft.World/ContainerAckPacket.h create mode 100644 Minecraft.World/ContainerButtonClickPacket.cpp create mode 100644 Minecraft.World/ContainerButtonClickPacket.h create mode 100644 Minecraft.World/ContainerClickPacket.cpp create mode 100644 Minecraft.World/ContainerClickPacket.h create mode 100644 Minecraft.World/ContainerClosePacket.cpp create mode 100644 Minecraft.World/ContainerClosePacket.h create mode 100644 Minecraft.World/ContainerMenu.cpp create mode 100644 Minecraft.World/ContainerMenu.h create mode 100644 Minecraft.World/ContainerOpenPacket.cpp create mode 100644 Minecraft.World/ContainerOpenPacket.h create mode 100644 Minecraft.World/ContainerSetContentPacket.cpp create mode 100644 Minecraft.World/ContainerSetContentPacket.h create mode 100644 Minecraft.World/ContainerSetDataPacket.cpp create mode 100644 Minecraft.World/ContainerSetDataPacket.h create mode 100644 Minecraft.World/ContainerSetSlotPacket.cpp create mode 100644 Minecraft.World/ContainerSetSlotPacket.h create mode 100644 Minecraft.World/Control.h create mode 100644 Minecraft.World/ControlledByPlayerGoal.cpp create mode 100644 Minecraft.World/ControlledByPlayerGoal.h create mode 100644 Minecraft.World/Coord.h create mode 100644 Minecraft.World/CoralTile.cpp create mode 100644 Minecraft.World/CoralTile.h create mode 100644 Minecraft.World/Cow.cpp create mode 100644 Minecraft.World/Cow.h create mode 100644 Minecraft.World/CraftItemPacket.cpp create mode 100644 Minecraft.World/CraftItemPacket.h create mode 100644 Minecraft.World/CraftingContainer.cpp create mode 100644 Minecraft.World/CraftingContainer.h create mode 100644 Minecraft.World/CraftingMenu.cpp create mode 100644 Minecraft.World/CraftingMenu.h create mode 100644 Minecraft.World/Creature.cpp create mode 100644 Minecraft.World/Creature.h create mode 100644 Minecraft.World/Creeper.cpp create mode 100644 Minecraft.World/Creeper.h create mode 100644 Minecraft.World/CropTile.cpp create mode 100644 Minecraft.World/CropTile.h create mode 100644 Minecraft.World/CustomLevelSource.cpp create mode 100644 Minecraft.World/CustomLevelSource.h create mode 100644 Minecraft.World/CustomPayloadPacket.cpp create mode 100644 Minecraft.World/CustomPayloadPacket.h create mode 100644 Minecraft.World/DamageEnchantment.cpp create mode 100644 Minecraft.World/DamageEnchantment.h create mode 100644 Minecraft.World/DamageSource.cpp create mode 100644 Minecraft.World/DamageSource.h create mode 100644 Minecraft.World/DataInput.h create mode 100644 Minecraft.World/DataInputStream.cpp create mode 100644 Minecraft.World/DataInputStream.h create mode 100644 Minecraft.World/DataLayer.cpp create mode 100644 Minecraft.World/DataLayer.h create mode 100644 Minecraft.World/DataOutput.h create mode 100644 Minecraft.World/DataOutputStream.cpp create mode 100644 Minecraft.World/DataOutputStream.h create mode 100644 Minecraft.World/DeadBushFeature.cpp create mode 100644 Minecraft.World/DeadBushFeature.h create mode 100644 Minecraft.World/DeadBushTile.cpp create mode 100644 Minecraft.World/DeadBushTile.h create mode 100644 Minecraft.World/DebugOptionsPacket.cpp create mode 100644 Minecraft.World/DebugOptionsPacket.h create mode 100644 Minecraft.World/DecorationMaterial.h create mode 100644 Minecraft.World/DefaultGameModeCommand.cpp create mode 100644 Minecraft.World/DefaultGameModeCommand.h create mode 100644 Minecraft.World/DefendVillageTargetGoal.cpp create mode 100644 Minecraft.World/DefendVillageTargetGoal.h create mode 100644 Minecraft.World/Definitions.h create mode 100644 Minecraft.World/DelayedRelease.cpp create mode 100644 Minecraft.World/DelayedRelease.h create mode 100644 Minecraft.World/DerivedLevelData.cpp create mode 100644 Minecraft.World/DerivedLevelData.h create mode 100644 Minecraft.World/DescFormatter.h create mode 100644 Minecraft.World/DesertBiome.cpp create mode 100644 Minecraft.World/DesertBiome.h create mode 100644 Minecraft.World/DesertWellFeature.cpp create mode 100644 Minecraft.World/DesertWellFeature.h create mode 100644 Minecraft.World/DetectorRailTile.cpp create mode 100644 Minecraft.World/DetectorRailTile.h create mode 100644 Minecraft.World/Difficulty.h create mode 100644 Minecraft.World/DigDurabilityEnchantment.cpp create mode 100644 Minecraft.World/DigDurabilityEnchantment.h create mode 100644 Minecraft.World/DiggerItem.cpp create mode 100644 Minecraft.World/DiggerItem.h create mode 100644 Minecraft.World/DiggingEnchantment.cpp create mode 100644 Minecraft.World/DiggingEnchantment.h create mode 100644 Minecraft.World/Dimension.cpp create mode 100644 Minecraft.World/Dimension.h create mode 100644 Minecraft.World/DiodeTile.cpp create mode 100644 Minecraft.World/DiodeTile.h create mode 100644 Minecraft.World/Direction.cpp create mode 100644 Minecraft.World/Direction.h create mode 100644 Minecraft.World/DirectionalTile.cpp create mode 100644 Minecraft.World/DirectionalTile.h create mode 100644 Minecraft.World/DirectoryLevelStorage.cpp create mode 100644 Minecraft.World/DirectoryLevelStorage.h create mode 100644 Minecraft.World/DirectoryLevelStorageSource.cpp create mode 100644 Minecraft.World/DirectoryLevelStorageSource.h create mode 100644 Minecraft.World/DirtTile.cpp create mode 100644 Minecraft.World/DirtTile.h create mode 100644 Minecraft.World/DisconnectPacket.cpp create mode 100644 Minecraft.World/DisconnectPacket.h create mode 100644 Minecraft.World/DispenserTile.cpp create mode 100644 Minecraft.World/DispenserTile.h create mode 100644 Minecraft.World/DispenserTileEntity.cpp create mode 100644 Minecraft.World/DispenserTileEntity.h create mode 100644 Minecraft.World/Distort.cpp create mode 100644 Minecraft.World/Distort.h create mode 100644 Minecraft.World/DoorInfo.cpp create mode 100644 Minecraft.World/DoorInfo.h create mode 100644 Minecraft.World/DoorInteractGoal.cpp create mode 100644 Minecraft.World/DoorInteractGoal.h create mode 100644 Minecraft.World/DoorItem.cpp create mode 100644 Minecraft.World/DoorItem.h create mode 100644 Minecraft.World/DoorTile.cpp create mode 100644 Minecraft.World/DoorTile.h create mode 100644 Minecraft.World/DoubleTag.h create mode 100644 Minecraft.World/DownfallLayer.cpp create mode 100644 Minecraft.World/DownfallLayer.h create mode 100644 Minecraft.World/DownfallMixerLayer.cpp create mode 100644 Minecraft.World/DownfallMixerLayer.h create mode 100644 Minecraft.World/DragonFireball.cpp create mode 100644 Minecraft.World/DragonFireball.h create mode 100644 Minecraft.World/DungeonFeature.cpp create mode 100644 Minecraft.World/DungeonFeature.h create mode 100644 Minecraft.World/DurangoStats.cpp create mode 100644 Minecraft.World/DurangoStats.h create mode 100644 Minecraft.World/DyePowderItem.cpp create mode 100644 Minecraft.World/DyePowderItem.h create mode 100644 Minecraft.World/EatTileGoal.cpp create mode 100644 Minecraft.World/EatTileGoal.h create mode 100644 Minecraft.World/EggItem.cpp create mode 100644 Minecraft.World/EggItem.h create mode 100644 Minecraft.World/EggTile.cpp create mode 100644 Minecraft.World/EggTile.h create mode 100644 Minecraft.World/Emboss.cpp create mode 100644 Minecraft.World/Emboss.h create mode 100644 Minecraft.World/EmptyLevelChunk.cpp create mode 100644 Minecraft.World/EmptyLevelChunk.h create mode 100644 Minecraft.World/EnchantItemCommand.cpp create mode 100644 Minecraft.World/EnchantItemCommand.h create mode 100644 Minecraft.World/EnchantedBookItem.cpp create mode 100644 Minecraft.World/EnchantedBookItem.h create mode 100644 Minecraft.World/Enchantment.cpp create mode 100644 Minecraft.World/Enchantment.h create mode 100644 Minecraft.World/EnchantmentCategory.cpp create mode 100644 Minecraft.World/EnchantmentCategory.h create mode 100644 Minecraft.World/EnchantmentContainer.cpp create mode 100644 Minecraft.World/EnchantmentContainer.h create mode 100644 Minecraft.World/EnchantmentHelper.cpp create mode 100644 Minecraft.World/EnchantmentHelper.h create mode 100644 Minecraft.World/EnchantmentInstance.cpp create mode 100644 Minecraft.World/EnchantmentInstance.h create mode 100644 Minecraft.World/EnchantmentMenu.cpp create mode 100644 Minecraft.World/EnchantmentMenu.h create mode 100644 Minecraft.World/EnchantmentSlot.h create mode 100644 Minecraft.World/EnchantmentTableEntity.cpp create mode 100644 Minecraft.World/EnchantmentTableEntity.h create mode 100644 Minecraft.World/EnchantmentTableTile.cpp create mode 100644 Minecraft.World/EnchantmentTableTile.h create mode 100644 Minecraft.World/EndPodiumFeature.cpp create mode 100644 Minecraft.World/EndPodiumFeature.h create mode 100644 Minecraft.World/EndTag.h create mode 100644 Minecraft.World/EnderChestTile.cpp create mode 100644 Minecraft.World/EnderChestTile.h create mode 100644 Minecraft.World/EnderChestTileEntity.cpp create mode 100644 Minecraft.World/EnderChestTileEntity.h create mode 100644 Minecraft.World/EnderCrystal.cpp create mode 100644 Minecraft.World/EnderCrystal.h create mode 100644 Minecraft.World/EnderDragon.cpp create mode 100644 Minecraft.World/EnderDragon.h create mode 100644 Minecraft.World/EnderEyeItem.cpp create mode 100644 Minecraft.World/EnderEyeItem.h create mode 100644 Minecraft.World/EnderMan.cpp create mode 100644 Minecraft.World/EnderMan.h create mode 100644 Minecraft.World/EnderpearlItem.cpp create mode 100644 Minecraft.World/EnderpearlItem.h create mode 100644 Minecraft.World/Enemy.cpp create mode 100644 Minecraft.World/Enemy.h create mode 100644 Minecraft.World/Entity.cpp create mode 100644 Minecraft.World/Entity.h create mode 100644 Minecraft.World/EntityActionAtPositionPacket.cpp create mode 100644 Minecraft.World/EntityActionAtPositionPacket.h create mode 100644 Minecraft.World/EntityDamageSource.cpp create mode 100644 Minecraft.World/EntityDamageSource.h create mode 100644 Minecraft.World/EntityEvent.h create mode 100644 Minecraft.World/EntityEventPacket.cpp create mode 100644 Minecraft.World/EntityEventPacket.h create mode 100644 Minecraft.World/EntityIO.cpp create mode 100644 Minecraft.World/EntityIO.h create mode 100644 Minecraft.World/EntityPos.cpp create mode 100644 Minecraft.World/EntityPos.h create mode 100644 Minecraft.World/EntityTile.cpp create mode 100644 Minecraft.World/EntityTile.h create mode 100644 Minecraft.World/Exceptions.h create mode 100644 Minecraft.World/ExperienceCommand.cpp create mode 100644 Minecraft.World/ExperienceCommand.h create mode 100644 Minecraft.World/ExperienceItem.cpp create mode 100644 Minecraft.World/ExperienceItem.h create mode 100644 Minecraft.World/ExperienceOrb.cpp create mode 100644 Minecraft.World/ExperienceOrb.h create mode 100644 Minecraft.World/ExplodePacket.cpp create mode 100644 Minecraft.World/ExplodePacket.h create mode 100644 Minecraft.World/Explosion.cpp create mode 100644 Minecraft.World/Explosion.h create mode 100644 Minecraft.World/ExtremeHillsBiome.cpp create mode 100644 Minecraft.World/ExtremeHillsBiome.h create mode 100644 Minecraft.World/EyeOfEnderSignal.cpp create mode 100644 Minecraft.World/EyeOfEnderSignal.h create mode 100644 Minecraft.World/Facing.cpp create mode 100644 Minecraft.World/Facing.h create mode 100644 Minecraft.World/FallingTile.cpp create mode 100644 Minecraft.World/FallingTile.h create mode 100644 Minecraft.World/FarmTile.cpp create mode 100644 Minecraft.World/FarmTile.h create mode 100644 Minecraft.World/FastNoise.cpp create mode 100644 Minecraft.World/FastNoise.h create mode 100644 Minecraft.World/Feature.cpp create mode 100644 Minecraft.World/Feature.h create mode 100644 Minecraft.World/FenceGateTile.cpp create mode 100644 Minecraft.World/FenceGateTile.h create mode 100644 Minecraft.World/FenceTile.cpp create mode 100644 Minecraft.World/FenceTile.h create mode 100644 Minecraft.World/File.cpp create mode 100644 Minecraft.World/File.h create mode 100644 Minecraft.World/FileFilter.h create mode 100644 Minecraft.World/FileHeader.cpp create mode 100644 Minecraft.World/FileHeader.h create mode 100644 Minecraft.World/FileInputStream.cpp create mode 100644 Minecraft.World/FileInputStream.h create mode 100644 Minecraft.World/FileOutputStream.cpp create mode 100644 Minecraft.World/FileOutputStream.h create mode 100644 Minecraft.World/FilenameFilter.h create mode 100644 Minecraft.World/FireAspectEnchantment.cpp create mode 100644 Minecraft.World/FireAspectEnchantment.h create mode 100644 Minecraft.World/FireChargeItem.cpp create mode 100644 Minecraft.World/FireChargeItem.h create mode 100644 Minecraft.World/FireTile.cpp create mode 100644 Minecraft.World/FireTile.h create mode 100644 Minecraft.World/Fireball.cpp create mode 100644 Minecraft.World/Fireball.h create mode 100644 Minecraft.World/FishingHook.cpp create mode 100644 Minecraft.World/FishingHook.h create mode 100644 Minecraft.World/FishingRodItem.cpp create mode 100644 Minecraft.World/FishingRodItem.h create mode 100644 Minecraft.World/FixedBiomeSource.cpp create mode 100644 Minecraft.World/FixedBiomeSource.h create mode 100644 Minecraft.World/FlatLayer.cpp create mode 100644 Minecraft.World/FlatLayer.h create mode 100644 Minecraft.World/FlatLevelSource.cpp create mode 100644 Minecraft.World/FlatLevelSource.h create mode 100644 Minecraft.World/FleeSunGoal.cpp create mode 100644 Minecraft.World/FleeSunGoal.h create mode 100644 Minecraft.World/FlintAndSteelItem.cpp create mode 100644 Minecraft.World/FlintAndSteelItem.h create mode 100644 Minecraft.World/FlippedIcon.cpp create mode 100644 Minecraft.World/FlippedIcon.h create mode 100644 Minecraft.World/FloatBuffer.cpp create mode 100644 Minecraft.World/FloatBuffer.h create mode 100644 Minecraft.World/FloatGoal.cpp create mode 100644 Minecraft.World/FloatGoal.h create mode 100644 Minecraft.World/FloatTag.h create mode 100644 Minecraft.World/FlowerFeature.cpp create mode 100644 Minecraft.World/FlowerFeature.h create mode 100644 Minecraft.World/FlowerPotTile.cpp create mode 100644 Minecraft.World/FlowerPotTile.h create mode 100644 Minecraft.World/FlyingMob.cpp create mode 100644 Minecraft.World/FlyingMob.h create mode 100644 Minecraft.World/FoliageColor.cpp create mode 100644 Minecraft.World/FoliageColor.h create mode 100644 Minecraft.World/FollowOwnerGoal.cpp create mode 100644 Minecraft.World/FollowOwnerGoal.h create mode 100644 Minecraft.World/FollowParentGoal.cpp create mode 100644 Minecraft.World/FollowParentGoal.h create mode 100644 Minecraft.World/FoodConstants.cpp create mode 100644 Minecraft.World/FoodConstants.h create mode 100644 Minecraft.World/FoodData.cpp create mode 100644 Minecraft.World/FoodData.h create mode 100644 Minecraft.World/FoodItem.cpp create mode 100644 Minecraft.World/FoodItem.h create mode 100644 Minecraft.World/FoodRecipies.cpp create mode 100644 Minecraft.World/FoodRecipies.h create mode 100644 Minecraft.World/ForestBiome.cpp create mode 100644 Minecraft.World/ForestBiome.h create mode 100644 Minecraft.World/FurnaceMenu.cpp create mode 100644 Minecraft.World/FurnaceMenu.h create mode 100644 Minecraft.World/FurnaceRecipes.cpp create mode 100644 Minecraft.World/FurnaceRecipes.h create mode 100644 Minecraft.World/FurnaceResultSlot.cpp create mode 100644 Minecraft.World/FurnaceResultSlot.h create mode 100644 Minecraft.World/FurnaceTile.cpp create mode 100644 Minecraft.World/FurnaceTile.h create mode 100644 Minecraft.World/FurnaceTileEntity.cpp create mode 100644 Minecraft.World/FurnaceTileEntity.h create mode 100644 Minecraft.World/FuzzyZoomLayer.cpp create mode 100644 Minecraft.World/FuzzyZoomLayer.h create mode 100644 Minecraft.World/GZIPInputStream.h create mode 100644 Minecraft.World/GZIPOutputStream.h create mode 100644 Minecraft.World/GameCommandPacket.cpp create mode 100644 Minecraft.World/GameCommandPacket.h create mode 100644 Minecraft.World/GameEventPacket.cpp create mode 100644 Minecraft.World/GameEventPacket.h create mode 100644 Minecraft.World/GameModeCommand.cpp create mode 100644 Minecraft.World/GameModeCommand.h create mode 100644 Minecraft.World/GasMaterial.h create mode 100644 Minecraft.World/GeneralStat.cpp create mode 100644 Minecraft.World/GeneralStat.h create mode 100644 Minecraft.World/GenericStats.cpp create mode 100644 Minecraft.World/GenericStats.h create mode 100644 Minecraft.World/GetInfoPacket.cpp create mode 100644 Minecraft.World/GetInfoPacket.h create mode 100644 Minecraft.World/Ghast.cpp create mode 100644 Minecraft.World/Ghast.h create mode 100644 Minecraft.World/Giant.cpp create mode 100644 Minecraft.World/Giant.h create mode 100644 Minecraft.World/GiveItemCommand.cpp create mode 100644 Minecraft.World/GiveItemCommand.h create mode 100644 Minecraft.World/GlassTile.cpp create mode 100644 Minecraft.World/GlassTile.h create mode 100644 Minecraft.World/GlobalEntity.cpp create mode 100644 Minecraft.World/GlobalEntity.h create mode 100644 Minecraft.World/Goal.cpp create mode 100644 Minecraft.World/Goal.h create mode 100644 Minecraft.World/GoalSelector.cpp create mode 100644 Minecraft.World/GoalSelector.h create mode 100644 Minecraft.World/GoldenAppleItem.cpp create mode 100644 Minecraft.World/GoldenAppleItem.h create mode 100644 Minecraft.World/Golem.cpp create mode 100644 Minecraft.World/Golem.h create mode 100644 Minecraft.World/GrassColor.cpp create mode 100644 Minecraft.World/GrassColor.h create mode 100644 Minecraft.World/GrassTile.cpp create mode 100644 Minecraft.World/GrassTile.h create mode 100644 Minecraft.World/GravelTile.cpp create mode 100644 Minecraft.World/GravelTile.h create mode 100644 Minecraft.World/GroundBushFeature.cpp create mode 100644 Minecraft.World/GroundBushFeature.h create mode 100644 Minecraft.World/GrowMushroomIslandLayer.cpp create mode 100644 Minecraft.World/GrowMushroomIslandLayer.h create mode 100644 Minecraft.World/HalfSlabTile.cpp create mode 100644 Minecraft.World/HalfSlabTile.h create mode 100644 Minecraft.World/HalfTransparentTile.cpp create mode 100644 Minecraft.World/HalfTransparentTile.h create mode 100644 Minecraft.World/HangingEntity.cpp create mode 100644 Minecraft.World/HangingEntity.h create mode 100644 Minecraft.World/HangingEntityItem.cpp create mode 100644 Minecraft.World/HangingEntityItem.h create mode 100644 Minecraft.World/HashExtension.h create mode 100644 Minecraft.World/Hasher.cpp create mode 100644 Minecraft.World/Hasher.h create mode 100644 Minecraft.World/HatchetItem.cpp create mode 100644 Minecraft.World/HatchetItem.h create mode 100644 Minecraft.World/HeavyTile.cpp create mode 100644 Minecraft.World/HeavyTile.h create mode 100644 Minecraft.World/HellBiome.cpp create mode 100644 Minecraft.World/HellBiome.h create mode 100644 Minecraft.World/HellDimension.cpp create mode 100644 Minecraft.World/HellDimension.h create mode 100644 Minecraft.World/HellFireFeature.cpp create mode 100644 Minecraft.World/HellFireFeature.h create mode 100644 Minecraft.World/HellFlatLevelSource.cpp create mode 100644 Minecraft.World/HellFlatLevelSource.h create mode 100644 Minecraft.World/HellPortalFeature.cpp create mode 100644 Minecraft.World/HellPortalFeature.h create mode 100644 Minecraft.World/HellRandomLevelSource.cpp create mode 100644 Minecraft.World/HellRandomLevelSource.h create mode 100644 Minecraft.World/HellSandTile.cpp create mode 100644 Minecraft.World/HellSandTile.h create mode 100644 Minecraft.World/HellSpringFeature.cpp create mode 100644 Minecraft.World/HellSpringFeature.h create mode 100644 Minecraft.World/HellStoneTile.cpp create mode 100644 Minecraft.World/HellStoneTile.h create mode 100644 Minecraft.World/HitResult.cpp create mode 100644 Minecraft.World/HitResult.h create mode 100644 Minecraft.World/HoeItem.cpp create mode 100644 Minecraft.World/HoeItem.h create mode 100644 Minecraft.World/HouseFeature.cpp create mode 100644 Minecraft.World/HouseFeature.h create mode 100644 Minecraft.World/HugeMushroomFeature.cpp create mode 100644 Minecraft.World/HugeMushroomFeature.h create mode 100644 Minecraft.World/HugeMushroomTile.cpp create mode 100644 Minecraft.World/HugeMushroomTile.h create mode 100644 Minecraft.World/HurtByTargetGoal.cpp create mode 100644 Minecraft.World/HurtByTargetGoal.h create mode 100644 Minecraft.World/I18n.cpp create mode 100644 Minecraft.World/I18n.h create mode 100644 Minecraft.World/IceBiome.cpp create mode 100644 Minecraft.World/IceBiome.h create mode 100644 Minecraft.World/IceTile.cpp create mode 100644 Minecraft.World/IceTile.h create mode 100644 Minecraft.World/Icon.h create mode 100644 Minecraft.World/IconRegister.h create mode 100644 Minecraft.World/ImprovedNoise.cpp create mode 100644 Minecraft.World/ImprovedNoise.h create mode 100644 Minecraft.World/IndirectEntityDamageSource.cpp create mode 100644 Minecraft.World/IndirectEntityDamageSource.h create mode 100644 Minecraft.World/InputOutputStream.h create mode 100644 Minecraft.World/InputStream.cpp create mode 100644 Minecraft.World/InputStream.h create mode 100644 Minecraft.World/InputStreamReader.cpp create mode 100644 Minecraft.World/InputStreamReader.h create mode 100644 Minecraft.World/InstantenousMobEffect.cpp create mode 100644 Minecraft.World/InstantenousMobEffect.h create mode 100644 Minecraft.World/IntArrayTag.h create mode 100644 Minecraft.World/IntBuffer.cpp create mode 100644 Minecraft.World/IntBuffer.h create mode 100644 Minecraft.World/IntCache.cpp create mode 100644 Minecraft.World/IntCache.h create mode 100644 Minecraft.World/IntTag.h create mode 100644 Minecraft.World/InteractGoal.cpp create mode 100644 Minecraft.World/InteractGoal.h create mode 100644 Minecraft.World/InteractPacket.cpp create mode 100644 Minecraft.World/InteractPacket.h create mode 100644 Minecraft.World/Inventory.cpp create mode 100644 Minecraft.World/Inventory.h create mode 100644 Minecraft.World/InventoryMenu.cpp create mode 100644 Minecraft.World/InventoryMenu.h create mode 100644 Minecraft.World/IslandLayer.cpp create mode 100644 Minecraft.World/IslandLayer.h create mode 100644 Minecraft.World/Item.cpp create mode 100644 Minecraft.World/Item.h create mode 100644 Minecraft.World/ItemEntity.cpp create mode 100644 Minecraft.World/ItemEntity.h create mode 100644 Minecraft.World/ItemFrame.cpp create mode 100644 Minecraft.World/ItemFrame.h create mode 100644 Minecraft.World/ItemInstance.cpp create mode 100644 Minecraft.World/ItemInstance.h create mode 100644 Minecraft.World/ItemStat.cpp create mode 100644 Minecraft.World/ItemStat.h create mode 100644 Minecraft.World/JavaIntHash.h create mode 100644 Minecraft.World/JavaMath.cpp create mode 100644 Minecraft.World/JavaMath.h create mode 100644 Minecraft.World/JumpControl.cpp create mode 100644 Minecraft.World/JumpControl.h create mode 100644 Minecraft.World/JungleBiome.cpp create mode 100644 Minecraft.World/JungleBiome.h create mode 100644 Minecraft.World/KeepAlivePacket.cpp create mode 100644 Minecraft.World/KeepAlivePacket.h create mode 100644 Minecraft.World/KickPlayerPacket.cpp create mode 100644 Minecraft.World/KickPlayerPacket.h create mode 100644 Minecraft.World/KillCommand.cpp create mode 100644 Minecraft.World/KillCommand.h create mode 100644 Minecraft.World/KnockbackEnchantment.cpp create mode 100644 Minecraft.World/KnockbackEnchantment.h create mode 100644 Minecraft.World/LadderTile.cpp create mode 100644 Minecraft.World/LadderTile.h create mode 100644 Minecraft.World/LakeFeature.cpp create mode 100644 Minecraft.World/LakeFeature.h create mode 100644 Minecraft.World/Language.cpp create mode 100644 Minecraft.World/Language.h create mode 100644 Minecraft.World/LargeCaveFeature.cpp create mode 100644 Minecraft.World/LargeCaveFeature.h create mode 100644 Minecraft.World/LargeFeature.cpp create mode 100644 Minecraft.World/LargeFeature.h create mode 100644 Minecraft.World/LargeHellCaveFeature.cpp create mode 100644 Minecraft.World/LargeHellCaveFeature.h create mode 100644 Minecraft.World/LavaSlime.cpp create mode 100644 Minecraft.World/LavaSlime.h create mode 100644 Minecraft.World/Layer.cpp create mode 100644 Minecraft.World/Layer.h create mode 100644 Minecraft.World/LeafTile.cpp create mode 100644 Minecraft.World/LeafTile.h create mode 100644 Minecraft.World/LeafTileItem.cpp create mode 100644 Minecraft.World/LeafTileItem.h create mode 100644 Minecraft.World/LeapAtTargetGoal.cpp create mode 100644 Minecraft.World/LeapAtTargetGoal.h create mode 100644 Minecraft.World/Level.cpp create mode 100644 Minecraft.World/Level.h create mode 100644 Minecraft.World/LevelChunk.cpp create mode 100644 Minecraft.World/LevelChunk.h create mode 100644 Minecraft.World/LevelConflictException.cpp create mode 100644 Minecraft.World/LevelConflictException.h create mode 100644 Minecraft.World/LevelData.cpp create mode 100644 Minecraft.World/LevelData.h create mode 100644 Minecraft.World/LevelEvent.h create mode 100644 Minecraft.World/LevelEventPacket.cpp create mode 100644 Minecraft.World/LevelEventPacket.h create mode 100644 Minecraft.World/LevelListener.h create mode 100644 Minecraft.World/LevelObjectInputStream.h create mode 100644 Minecraft.World/LevelSettings.cpp create mode 100644 Minecraft.World/LevelSettings.h create mode 100644 Minecraft.World/LevelSoundPacket.cpp create mode 100644 Minecraft.World/LevelSoundPacket.h create mode 100644 Minecraft.World/LevelSource.h create mode 100644 Minecraft.World/LevelStorage.cpp create mode 100644 Minecraft.World/LevelStorage.h create mode 100644 Minecraft.World/LevelStorageProfilerDecorator.cpp create mode 100644 Minecraft.World/LevelStorageProfilerDecorator.h create mode 100644 Minecraft.World/LevelStorageSource.h create mode 100644 Minecraft.World/LevelSummary.cpp create mode 100644 Minecraft.World/LevelSummary.h create mode 100644 Minecraft.World/LevelType.cpp create mode 100644 Minecraft.World/LevelType.h create mode 100644 Minecraft.World/LeverTile.cpp create mode 100644 Minecraft.World/LeverTile.h create mode 100644 Minecraft.World/LightGemFeature.cpp create mode 100644 Minecraft.World/LightGemFeature.h create mode 100644 Minecraft.World/LightGemTile.cpp create mode 100644 Minecraft.World/LightGemTile.h create mode 100644 Minecraft.World/LightLayer.h create mode 100644 Minecraft.World/LightningBolt.cpp create mode 100644 Minecraft.World/LightningBolt.h create mode 100644 Minecraft.World/LiquidMaterial.h create mode 100644 Minecraft.World/LiquidTile.cpp create mode 100644 Minecraft.World/LiquidTile.h create mode 100644 Minecraft.World/LiquidTileDynamic.cpp create mode 100644 Minecraft.World/LiquidTileDynamic.h create mode 100644 Minecraft.World/LiquidTileStatic.cpp create mode 100644 Minecraft.World/LiquidTileStatic.h create mode 100644 Minecraft.World/ListTag.h create mode 100644 Minecraft.World/LockedChestTile.cpp create mode 100644 Minecraft.World/LockedChestTile.h create mode 100644 Minecraft.World/LoginPacket.cpp create mode 100644 Minecraft.World/LoginPacket.h create mode 100644 Minecraft.World/LongTag.h create mode 100644 Minecraft.World/LookAtPlayerGoal.cpp create mode 100644 Minecraft.World/LookAtPlayerGoal.h create mode 100644 Minecraft.World/LookAtTradingPlayerGoal.cpp create mode 100644 Minecraft.World/LookAtTradingPlayerGoal.h create mode 100644 Minecraft.World/LookControl.cpp create mode 100644 Minecraft.World/LookControl.h create mode 100644 Minecraft.World/LootBonusEnchantment.cpp create mode 100644 Minecraft.World/LootBonusEnchantment.h create mode 100644 Minecraft.World/MakeLoveGoal.cpp create mode 100644 Minecraft.World/MakeLoveGoal.h create mode 100644 Minecraft.World/MapItem.cpp create mode 100644 Minecraft.World/MapItem.h create mode 100644 Minecraft.World/MapItemSavedData.cpp create mode 100644 Minecraft.World/MapItemSavedData.h create mode 100644 Minecraft.World/Material.cpp create mode 100644 Minecraft.World/Material.h create mode 100644 Minecraft.World/MaterialColor.cpp create mode 100644 Minecraft.World/MaterialColor.h create mode 100644 Minecraft.World/McRegionChunkStorage.cpp create mode 100644 Minecraft.World/McRegionChunkStorage.h create mode 100644 Minecraft.World/McRegionLevelStorage.cpp create mode 100644 Minecraft.World/McRegionLevelStorage.h create mode 100644 Minecraft.World/McRegionLevelStorageSource.cpp create mode 100644 Minecraft.World/McRegionLevelStorageSource.h create mode 100644 Minecraft.World/MegaTreeFeature.cpp create mode 100644 Minecraft.World/MegaTreeFeature.h create mode 100644 Minecraft.World/MeleeAttackGoal.cpp create mode 100644 Minecraft.World/MeleeAttackGoal.h create mode 100644 Minecraft.World/MelonTile.cpp create mode 100644 Minecraft.World/MelonTile.h create mode 100644 Minecraft.World/MemoryChunkStorage.cpp create mode 100644 Minecraft.World/MemoryChunkStorage.h create mode 100644 Minecraft.World/MemoryLevelStorage.cpp create mode 100644 Minecraft.World/MemoryLevelStorage.h create mode 100644 Minecraft.World/MemoryLevelStorageSource.cpp create mode 100644 Minecraft.World/MemoryLevelStorageSource.h create mode 100644 Minecraft.World/MenuBackup.cpp create mode 100644 Minecraft.World/MenuBackup.h create mode 100644 Minecraft.World/Merchant.h create mode 100644 Minecraft.World/MerchantContainer.cpp create mode 100644 Minecraft.World/MerchantContainer.h create mode 100644 Minecraft.World/MerchantMenu.cpp create mode 100644 Minecraft.World/MerchantMenu.h create mode 100644 Minecraft.World/MerchantRecipe.cpp create mode 100644 Minecraft.World/MerchantRecipe.h create mode 100644 Minecraft.World/MerchantRecipeList.cpp create mode 100644 Minecraft.World/MerchantRecipeList.h create mode 100644 Minecraft.World/MerchantResultSlot.cpp create mode 100644 Minecraft.World/MerchantResultSlot.h create mode 100644 Minecraft.World/MetalTile.cpp create mode 100644 Minecraft.World/MetalTile.h create mode 100644 Minecraft.World/MilkBucketItem.cpp create mode 100644 Minecraft.World/MilkBucketItem.h create mode 100644 Minecraft.World/MineShaftFeature.cpp create mode 100644 Minecraft.World/MineShaftFeature.h create mode 100644 Minecraft.World/MineShaftPieces.cpp create mode 100644 Minecraft.World/MineShaftPieces.h create mode 100644 Minecraft.World/MineShaftStart.cpp create mode 100644 Minecraft.World/MineShaftStart.h create mode 100644 Minecraft.World/Minecart.cpp create mode 100644 Minecraft.World/Minecart.h create mode 100644 Minecraft.World/MinecartItem.cpp create mode 100644 Minecraft.World/MinecartItem.h create mode 100644 Minecraft.World/Minecraft.World.cpp create mode 100644 Minecraft.World/Minecraft.World.h create mode 100644 Minecraft.World/Minecraft.World.vcxproj create mode 100644 Minecraft.World/Minecraft.World.vcxproj.filters create mode 100644 Minecraft.World/Minecraft.World.vcxproj.user create mode 100644 Minecraft.World/Minecraft.World.vcxproj.vspscc create mode 100644 Minecraft.World/Mob.cpp create mode 100644 Minecraft.World/Mob.h create mode 100644 Minecraft.World/MobCategory.cpp create mode 100644 Minecraft.World/MobCategory.h create mode 100644 Minecraft.World/MobEffect.cpp create mode 100644 Minecraft.World/MobEffect.h create mode 100644 Minecraft.World/MobEffectInstance.cpp create mode 100644 Minecraft.World/MobEffectInstance.h create mode 100644 Minecraft.World/MobSpawner.cpp create mode 100644 Minecraft.World/MobSpawner.h create mode 100644 Minecraft.World/MobSpawnerTile.cpp create mode 100644 Minecraft.World/MobSpawnerTile.h create mode 100644 Minecraft.World/MobSpawnerTileEntity.cpp create mode 100644 Minecraft.World/MobSpawnerTileEntity.h create mode 100644 Minecraft.World/MobType.h create mode 100644 Minecraft.World/MockedLevelStorage.cpp create mode 100644 Minecraft.World/MockedLevelStorage.h create mode 100644 Minecraft.World/Monster.cpp create mode 100644 Minecraft.World/Monster.h create mode 100644 Minecraft.World/MonsterPlacerItem.cpp create mode 100644 Minecraft.World/MonsterPlacerItem.h create mode 100644 Minecraft.World/MonsterRoomFeature.cpp create mode 100644 Minecraft.World/MonsterRoomFeature.h create mode 100644 Minecraft.World/MouseInventoryClickHandler.h create mode 100644 Minecraft.World/MoveControl.cpp create mode 100644 Minecraft.World/MoveControl.h create mode 100644 Minecraft.World/MoveEntityPacket.cpp create mode 100644 Minecraft.World/MoveEntityPacket.h create mode 100644 Minecraft.World/MoveEntityPacketSmall.cpp create mode 100644 Minecraft.World/MoveEntityPacketSmall.h create mode 100644 Minecraft.World/MoveIndoorsGoal.cpp create mode 100644 Minecraft.World/MoveIndoorsGoal.h create mode 100644 Minecraft.World/MovePlayerPacket.cpp create mode 100644 Minecraft.World/MovePlayerPacket.h create mode 100644 Minecraft.World/MoveThroughVillageGoal.cpp create mode 100644 Minecraft.World/MoveThroughVillageGoal.h create mode 100644 Minecraft.World/MoveTowardsRestrictionGoal.cpp create mode 100644 Minecraft.World/MoveTowardsRestrictionGoal.h create mode 100644 Minecraft.World/MoveTowardsTargetGoal.cpp create mode 100644 Minecraft.World/MoveTowardsTargetGoal.h create mode 100644 Minecraft.World/Mth.cpp create mode 100644 Minecraft.World/Mth.h create mode 100644 Minecraft.World/MultiTextureTileItem.cpp create mode 100644 Minecraft.World/MultiTextureTileItem.h create mode 100644 Minecraft.World/Mushroom.cpp create mode 100644 Minecraft.World/Mushroom.h create mode 100644 Minecraft.World/MushroomCow.cpp create mode 100644 Minecraft.World/MushroomCow.h create mode 100644 Minecraft.World/MushroomIslandBiome.cpp create mode 100644 Minecraft.World/MushroomIslandBiome.h create mode 100644 Minecraft.World/MusicTile.cpp create mode 100644 Minecraft.World/MusicTile.h create mode 100644 Minecraft.World/MusicTileEntity.cpp create mode 100644 Minecraft.World/MusicTileEntity.h create mode 100644 Minecraft.World/MycelTile.cpp create mode 100644 Minecraft.World/MycelTile.h create mode 100644 Minecraft.World/NbtIo.cpp create mode 100644 Minecraft.World/NbtIo.h create mode 100644 Minecraft.World/NbtSlotFile.cpp create mode 100644 Minecraft.World/NbtSlotFile.h create mode 100644 Minecraft.World/NearestAttackableTargetGoal.cpp create mode 100644 Minecraft.World/NearestAttackableTargetGoal.h create mode 100644 Minecraft.World/NetherBridgeFeature.cpp create mode 100644 Minecraft.World/NetherBridgeFeature.h create mode 100644 Minecraft.World/NetherBridgePieces.cpp create mode 100644 Minecraft.World/NetherBridgePieces.h create mode 100644 Minecraft.World/NetherSphere.cpp create mode 100644 Minecraft.World/NetherSphere.h create mode 100644 Minecraft.World/NetherStalkTile.cpp create mode 100644 Minecraft.World/NetherStalkTile.h create mode 100644 Minecraft.World/Node.cpp create mode 100644 Minecraft.World/Node.h create mode 100644 Minecraft.World/NonTameRandomTargetGoal.cpp create mode 100644 Minecraft.World/NonTameRandomTargetGoal.h create mode 100644 Minecraft.World/NormalDimension.h create mode 100644 Minecraft.World/NotGateTile.cpp create mode 100644 Minecraft.World/NotGateTile.h create mode 100644 Minecraft.World/Npc.cpp create mode 100644 Minecraft.World/Npc.h create mode 100644 Minecraft.World/NumberFormaters.h create mode 100644 Minecraft.World/ObsidianTile.cpp create mode 100644 Minecraft.World/ObsidianTile.h create mode 100644 Minecraft.World/OceanBiome.h create mode 100644 Minecraft.World/OcelotSitOnTileGoal.cpp create mode 100644 Minecraft.World/OcelotSitOnTileGoal.h create mode 100644 Minecraft.World/OfferFlowerGoal.cpp create mode 100644 Minecraft.World/OfferFlowerGoal.h create mode 100644 Minecraft.World/OldChunkStorage.cpp create mode 100644 Minecraft.World/OldChunkStorage.h create mode 100644 Minecraft.World/OpenDoorGoal.cpp create mode 100644 Minecraft.World/OpenDoorGoal.h create mode 100644 Minecraft.World/OreFeature.cpp create mode 100644 Minecraft.World/OreFeature.h create mode 100644 Minecraft.World/OreRecipies.cpp create mode 100644 Minecraft.World/OreRecipies.h create mode 100644 Minecraft.World/OreTile.cpp create mode 100644 Minecraft.World/OreTile.h create mode 100644 Minecraft.World/OutputStream.h create mode 100644 Minecraft.World/OwnerHurtByTargetGoal.cpp create mode 100644 Minecraft.World/OwnerHurtByTargetGoal.h create mode 100644 Minecraft.World/OwnerHurtTargetGoal.cpp create mode 100644 Minecraft.World/OwnerHurtTargetGoal.h create mode 100644 Minecraft.World/OxygenEnchantment.cpp create mode 100644 Minecraft.World/OxygenEnchantment.h create mode 100644 Minecraft.World/Ozelot.cpp create mode 100644 Minecraft.World/Ozelot.h create mode 100644 Minecraft.World/OzelotAttackGoal.cpp create mode 100644 Minecraft.World/OzelotAttackGoal.h create mode 100644 Minecraft.World/Packet.cpp create mode 100644 Minecraft.World/Packet.h create mode 100644 Minecraft.World/PacketListener.cpp create mode 100644 Minecraft.World/PacketListener.h create mode 100644 Minecraft.World/Painting.cpp create mode 100644 Minecraft.World/Painting.h create mode 100644 Minecraft.World/PanicGoal.cpp create mode 100644 Minecraft.World/PanicGoal.h create mode 100644 Minecraft.World/ParticleTypes.h create mode 100644 Minecraft.World/Path.cpp create mode 100644 Minecraft.World/Path.h create mode 100644 Minecraft.World/PathFinder.cpp create mode 100644 Minecraft.World/PathFinder.h create mode 100644 Minecraft.World/PathNavigation.cpp create mode 100644 Minecraft.World/PathNavigation.h create mode 100644 Minecraft.World/PathfinderMob.cpp create mode 100644 Minecraft.World/PathfinderMob.h create mode 100644 Minecraft.World/PerformanceTimer.cpp create mode 100644 Minecraft.World/PerformanceTimer.h create mode 100644 Minecraft.World/PerlinNoise.cpp create mode 100644 Minecraft.World/PerlinNoise.h create mode 100644 Minecraft.World/PerlinSimplexNoise.cpp create mode 100644 Minecraft.World/PerlinSimplexNoise.h create mode 100644 Minecraft.World/PickaxeItem.cpp create mode 100644 Minecraft.World/PickaxeItem.h create mode 100644 Minecraft.World/Pig.cpp create mode 100644 Minecraft.World/Pig.h create mode 100644 Minecraft.World/PigZombie.cpp create mode 100644 Minecraft.World/PigZombie.h create mode 100644 Minecraft.World/PineFeature.cpp create mode 100644 Minecraft.World/PineFeature.h create mode 100644 Minecraft.World/PistonBaseTile.cpp create mode 100644 Minecraft.World/PistonBaseTile.h create mode 100644 Minecraft.World/PistonExtensionTile.cpp create mode 100644 Minecraft.World/PistonExtensionTile.h create mode 100644 Minecraft.World/PistonMovingPiece.cpp create mode 100644 Minecraft.World/PistonMovingPiece.h create mode 100644 Minecraft.World/PistonPieceEntity.cpp create mode 100644 Minecraft.World/PistonPieceEntity.h create mode 100644 Minecraft.World/PistonTileItem.cpp create mode 100644 Minecraft.World/PistonTileItem.h create mode 100644 Minecraft.World/PlainsBiome.cpp create mode 100644 Minecraft.World/PlainsBiome.h create mode 100644 Minecraft.World/PlayGoal.cpp create mode 100644 Minecraft.World/PlayGoal.h create mode 100644 Minecraft.World/Player.cpp create mode 100644 Minecraft.World/Player.h create mode 100644 Minecraft.World/PlayerAbilitiesPacket.cpp create mode 100644 Minecraft.World/PlayerAbilitiesPacket.h create mode 100644 Minecraft.World/PlayerActionPacket.cpp create mode 100644 Minecraft.World/PlayerActionPacket.h create mode 100644 Minecraft.World/PlayerCommandPacket.cpp create mode 100644 Minecraft.World/PlayerCommandPacket.h create mode 100644 Minecraft.World/PlayerEnderChestContainer.cpp create mode 100644 Minecraft.World/PlayerEnderChestContainer.h create mode 100644 Minecraft.World/PlayerIO.h create mode 100644 Minecraft.World/PlayerInfoPacket.cpp create mode 100644 Minecraft.World/PlayerInfoPacket.h create mode 100644 Minecraft.World/PlayerInputPacket.cpp create mode 100644 Minecraft.World/PlayerInputPacket.h create mode 100644 Minecraft.World/PortalForcer.cpp create mode 100644 Minecraft.World/PortalForcer.h create mode 100644 Minecraft.World/PortalMaterial.h create mode 100644 Minecraft.World/PortalTile.cpp create mode 100644 Minecraft.World/PortalTile.h create mode 100644 Minecraft.World/Pos.cpp create mode 100644 Minecraft.World/Pos.h create mode 100644 Minecraft.World/PotatoTile.cpp create mode 100644 Minecraft.World/PotatoTile.h create mode 100644 Minecraft.World/PotionBrewing.cpp create mode 100644 Minecraft.World/PotionBrewing.h create mode 100644 Minecraft.World/PotionItem.cpp create mode 100644 Minecraft.World/PotionItem.h create mode 100644 Minecraft.World/PreLoginPacket.cpp create mode 100644 Minecraft.World/PreLoginPacket.h create mode 100644 Minecraft.World/PressurePlateTile.cpp create mode 100644 Minecraft.World/PressurePlateTile.h create mode 100644 Minecraft.World/PrimedTnt.cpp create mode 100644 Minecraft.World/PrimedTnt.h create mode 100644 Minecraft.World/ProgressListener.h create mode 100644 Minecraft.World/ProtectionEnchantment.cpp create mode 100644 Minecraft.World/ProtectionEnchantment.h create mode 100644 Minecraft.World/PumpkinFeature.cpp create mode 100644 Minecraft.World/PumpkinFeature.h create mode 100644 Minecraft.World/PumpkinTile.cpp create mode 100644 Minecraft.World/PumpkinTile.h create mode 100644 Minecraft.World/QuartzBlockTile.cpp create mode 100644 Minecraft.World/QuartzBlockTile.h create mode 100644 Minecraft.World/RailTile.cpp create mode 100644 Minecraft.World/RailTile.h create mode 100644 Minecraft.World/RainforestBiome.cpp create mode 100644 Minecraft.World/RainforestBiome.h create mode 100644 Minecraft.World/Random.cpp create mode 100644 Minecraft.World/Random.h create mode 100644 Minecraft.World/RandomLevelSource.cpp create mode 100644 Minecraft.World/RandomLevelSource.h create mode 100644 Minecraft.World/RandomLookAroundGoal.cpp create mode 100644 Minecraft.World/RandomLookAroundGoal.h create mode 100644 Minecraft.World/RandomPos.cpp create mode 100644 Minecraft.World/RandomPos.h create mode 100644 Minecraft.World/RandomScatteredLargeFeature.cpp create mode 100644 Minecraft.World/RandomScatteredLargeFeature.h create mode 100644 Minecraft.World/RandomStrollGoal.cpp create mode 100644 Minecraft.World/RandomStrollGoal.h create mode 100644 Minecraft.World/Rarity.cpp create mode 100644 Minecraft.World/Rarity.h create mode 100644 Minecraft.World/ReadMe.txt create mode 100644 Minecraft.World/ReadOnlyChunkCache.cpp create mode 100644 Minecraft.World/ReadOnlyChunkCache.h create mode 100644 Minecraft.World/Reader.h create mode 100644 Minecraft.World/Recipes.cpp create mode 100644 Minecraft.World/Recipes.h create mode 100644 Minecraft.World/Recipy.h create mode 100644 Minecraft.World/RecordPlayerTile.cpp create mode 100644 Minecraft.World/RecordPlayerTile.h create mode 100644 Minecraft.World/RecordingItem.cpp create mode 100644 Minecraft.World/RecordingItem.h create mode 100644 Minecraft.World/RedStoneDustTile.cpp create mode 100644 Minecraft.World/RedStoneDustTile.h create mode 100644 Minecraft.World/RedStoneItem.cpp create mode 100644 Minecraft.World/RedStoneItem.h create mode 100644 Minecraft.World/RedStoneOreTile.cpp create mode 100644 Minecraft.World/RedStoneOreTile.h create mode 100644 Minecraft.World/RedlightTile.cpp create mode 100644 Minecraft.World/RedlightTile.h create mode 100644 Minecraft.World/ReedTile.cpp create mode 100644 Minecraft.World/ReedTile.h create mode 100644 Minecraft.World/ReedsFeature.cpp create mode 100644 Minecraft.World/ReedsFeature.h create mode 100644 Minecraft.World/Reference.h create mode 100644 Minecraft.World/Region.cpp create mode 100644 Minecraft.World/Region.h create mode 100644 Minecraft.World/RegionFile.cpp create mode 100644 Minecraft.World/RegionFile.h create mode 100644 Minecraft.World/RegionFileCache.cpp create mode 100644 Minecraft.World/RegionFileCache.h create mode 100644 Minecraft.World/RegionHillsLayer.cpp create mode 100644 Minecraft.World/RegionHillsLayer.h create mode 100644 Minecraft.World/RemoveEntitiesPacket.cpp create mode 100644 Minecraft.World/RemoveEntitiesPacket.h create mode 100644 Minecraft.World/RemoveMobEffectPacket.cpp create mode 100644 Minecraft.World/RemoveMobEffectPacket.h create mode 100644 Minecraft.World/RepairContainer.cpp create mode 100644 Minecraft.World/RepairContainer.h create mode 100644 Minecraft.World/RepairMenu.cpp create mode 100644 Minecraft.World/RepairMenu.h create mode 100644 Minecraft.World/RepairResultSlot.cpp create mode 100644 Minecraft.World/RepairResultSlot.h create mode 100644 Minecraft.World/RespawnPacket.cpp create mode 100644 Minecraft.World/RespawnPacket.h create mode 100644 Minecraft.World/RestrictOpenDoorGoal.cpp create mode 100644 Minecraft.World/RestrictOpenDoorGoal.h create mode 100644 Minecraft.World/RestrictSunGoal.cpp create mode 100644 Minecraft.World/RestrictSunGoal.h create mode 100644 Minecraft.World/ResultContainer.cpp create mode 100644 Minecraft.World/ResultContainer.h create mode 100644 Minecraft.World/ResultSlot.cpp create mode 100644 Minecraft.World/ResultSlot.h create mode 100644 Minecraft.World/RiverBiome.h create mode 100644 Minecraft.World/RiverInitLayer.cpp create mode 100644 Minecraft.World/RiverInitLayer.h create mode 100644 Minecraft.World/RiverLayer.cpp create mode 100644 Minecraft.World/RiverLayer.h create mode 100644 Minecraft.World/RiverMixerLayer.cpp create mode 100644 Minecraft.World/RiverMixerLayer.h create mode 100644 Minecraft.World/Rotate.cpp create mode 100644 Minecraft.World/Rotate.h create mode 100644 Minecraft.World/RotateHeadPacket.cpp create mode 100644 Minecraft.World/RotateHeadPacket.h create mode 100644 Minecraft.World/SaddleItem.cpp create mode 100644 Minecraft.World/SaddleItem.h create mode 100644 Minecraft.World/SandFeature.cpp create mode 100644 Minecraft.World/SandFeature.h create mode 100644 Minecraft.World/SandStoneTile.cpp create mode 100644 Minecraft.World/SandStoneTile.h create mode 100644 Minecraft.World/Sapling.cpp create mode 100644 Minecraft.World/Sapling.h create mode 100644 Minecraft.World/SaplingTileItem.cpp create mode 100644 Minecraft.World/SaplingTileItem.h create mode 100644 Minecraft.World/SavedData.cpp create mode 100644 Minecraft.World/SavedData.h create mode 100644 Minecraft.World/SavedDataStorage.cpp create mode 100644 Minecraft.World/SavedDataStorage.h create mode 100644 Minecraft.World/Scale.cpp create mode 100644 Minecraft.World/Scale.h create mode 100644 Minecraft.World/ScatteredFeaturePieces.cpp create mode 100644 Minecraft.World/ScatteredFeaturePieces.h create mode 100644 Minecraft.World/SeedFoodItem.cpp create mode 100644 Minecraft.World/SeedFoodItem.h create mode 100644 Minecraft.World/SeedItem.cpp create mode 100644 Minecraft.World/SeedItem.h create mode 100644 Minecraft.World/Sensing.cpp create mode 100644 Minecraft.World/Sensing.h create mode 100644 Minecraft.World/ServerAuthDataPacket.h create mode 100644 Minecraft.World/ServerSettingsChangedPacket.cpp create mode 100644 Minecraft.World/ServerSettingsChangedPacket.h create mode 100644 Minecraft.World/SetCarriedItemPacket.cpp create mode 100644 Minecraft.World/SetCarriedItemPacket.h create mode 100644 Minecraft.World/SetCreativeModeSlotPacket.cpp create mode 100644 Minecraft.World/SetCreativeModeSlotPacket.h create mode 100644 Minecraft.World/SetEntityDataPacket.cpp create mode 100644 Minecraft.World/SetEntityDataPacket.h create mode 100644 Minecraft.World/SetEntityMotionPacket.cpp create mode 100644 Minecraft.World/SetEntityMotionPacket.h create mode 100644 Minecraft.World/SetEquippedItemPacket.cpp create mode 100644 Minecraft.World/SetEquippedItemPacket.h create mode 100644 Minecraft.World/SetExperiencePacket.cpp create mode 100644 Minecraft.World/SetExperiencePacket.h create mode 100644 Minecraft.World/SetHealthPacket.cpp create mode 100644 Minecraft.World/SetHealthPacket.h create mode 100644 Minecraft.World/SetRidingPacket.cpp create mode 100644 Minecraft.World/SetRidingPacket.h create mode 100644 Minecraft.World/SetSpawnPositionPacket.cpp create mode 100644 Minecraft.World/SetSpawnPositionPacket.h create mode 100644 Minecraft.World/SetTimePacket.cpp create mode 100644 Minecraft.World/SetTimePacket.h create mode 100644 Minecraft.World/ShapedRecipy.cpp create mode 100644 Minecraft.World/ShapedRecipy.h create mode 100644 Minecraft.World/ShapelessRecipy.cpp create mode 100644 Minecraft.World/ShapelessRecipy.h create mode 100644 Minecraft.World/SharedConstants.cpp create mode 100644 Minecraft.World/SharedConstants.h create mode 100644 Minecraft.World/SharedKeyPacket.h create mode 100644 Minecraft.World/ShearsItem.cpp create mode 100644 Minecraft.World/ShearsItem.h create mode 100644 Minecraft.World/Sheep.cpp create mode 100644 Minecraft.World/Sheep.h create mode 100644 Minecraft.World/ShoreLayer.cpp create mode 100644 Minecraft.World/ShoreLayer.h create mode 100644 Minecraft.World/ShortTag.h create mode 100644 Minecraft.World/ShovelItem.cpp create mode 100644 Minecraft.World/ShovelItem.h create mode 100644 Minecraft.World/SignItem.cpp create mode 100644 Minecraft.World/SignItem.h create mode 100644 Minecraft.World/SignTile.cpp create mode 100644 Minecraft.World/SignTile.h create mode 100644 Minecraft.World/SignTileEntity.cpp create mode 100644 Minecraft.World/SignTileEntity.h create mode 100644 Minecraft.World/SignUpdatePacket.cpp create mode 100644 Minecraft.World/SignUpdatePacket.h create mode 100644 Minecraft.World/Silverfish.cpp create mode 100644 Minecraft.World/Silverfish.h create mode 100644 Minecraft.World/SimpleContainer.cpp create mode 100644 Minecraft.World/SimpleContainer.h create mode 100644 Minecraft.World/SimplexNoise.cpp create mode 100644 Minecraft.World/SimplexNoise.h create mode 100644 Minecraft.World/SitGoal.cpp create mode 100644 Minecraft.World/SitGoal.h create mode 100644 Minecraft.World/Skeleton.cpp create mode 100644 Minecraft.World/Skeleton.h create mode 100644 Minecraft.World/SkullItem.cpp create mode 100644 Minecraft.World/SkullItem.h create mode 100644 Minecraft.World/SkullTile.cpp create mode 100644 Minecraft.World/SkullTile.h create mode 100644 Minecraft.World/SkullTileEntity.cpp create mode 100644 Minecraft.World/SkullTileEntity.h create mode 100644 Minecraft.World/SkyIslandDimension.cpp create mode 100644 Minecraft.World/Slime.cpp create mode 100644 Minecraft.World/Slime.h create mode 100644 Minecraft.World/Slot.cpp create mode 100644 Minecraft.World/Slot.h create mode 100644 Minecraft.World/SmallFireball.cpp create mode 100644 Minecraft.World/SmallFireball.h create mode 100644 Minecraft.World/SmoothFloat.cpp create mode 100644 Minecraft.World/SmoothFloat.h create mode 100644 Minecraft.World/SmoothLayer.cpp create mode 100644 Minecraft.World/SmoothLayer.h create mode 100644 Minecraft.World/SmoothStoneBrickTile.cpp create mode 100644 Minecraft.World/SmoothStoneBrickTile.h create mode 100644 Minecraft.World/SmoothStoneBrickTileItem.cpp create mode 100644 Minecraft.World/SmoothStoneBrickTileItem.h create mode 100644 Minecraft.World/SmoothZoomLayer.cpp create mode 100644 Minecraft.World/SmoothZoomLayer.h create mode 100644 Minecraft.World/SnowMan.cpp create mode 100644 Minecraft.World/SnowMan.h create mode 100644 Minecraft.World/SnowTile.cpp create mode 100644 Minecraft.World/SnowTile.h create mode 100644 Minecraft.World/Snowball.cpp create mode 100644 Minecraft.World/Snowball.h create mode 100644 Minecraft.World/SnowballItem.cpp create mode 100644 Minecraft.World/SnowballItem.h create mode 100644 Minecraft.World/Socket.cpp create mode 100644 Minecraft.World/Socket.h create mode 100644 Minecraft.World/SocketAddress.h create mode 100644 Minecraft.World/SoundTypes.h create mode 100644 Minecraft.World/SparseDataStorage.cpp create mode 100644 Minecraft.World/SparseDataStorage.h create mode 100644 Minecraft.World/SparseLightStorage.cpp create mode 100644 Minecraft.World/SparseLightStorage.h create mode 100644 Minecraft.World/Spider.cpp create mode 100644 Minecraft.World/Spider.h create mode 100644 Minecraft.World/SpikeFeature.cpp create mode 100644 Minecraft.World/SpikeFeature.h create mode 100644 Minecraft.World/Sponge.cpp create mode 100644 Minecraft.World/Sponge.h create mode 100644 Minecraft.World/SpringFeature.cpp create mode 100644 Minecraft.World/SpringFeature.h create mode 100644 Minecraft.World/SpringTile.cpp create mode 100644 Minecraft.World/SpringTile.h create mode 100644 Minecraft.World/SpruceFeature.cpp create mode 100644 Minecraft.World/SpruceFeature.h create mode 100644 Minecraft.World/Squid.cpp create mode 100644 Minecraft.World/Squid.h create mode 100644 Minecraft.World/StairTile.cpp create mode 100644 Minecraft.World/StairTile.h create mode 100644 Minecraft.World/Stat.cpp create mode 100644 Minecraft.World/Stat.h create mode 100644 Minecraft.World/StatFormatter.h create mode 100644 Minecraft.World/Stats.cpp create mode 100644 Minecraft.World/Stats.h create mode 100644 Minecraft.World/StemTile.cpp create mode 100644 Minecraft.World/StemTile.h create mode 100644 Minecraft.World/StoneMonsterTile.cpp create mode 100644 Minecraft.World/StoneMonsterTile.h create mode 100644 Minecraft.World/StoneMonsterTileItem.cpp create mode 100644 Minecraft.World/StoneMonsterTileItem.h create mode 100644 Minecraft.World/StoneSlabTile.cpp create mode 100644 Minecraft.World/StoneSlabTile.h create mode 100644 Minecraft.World/StoneSlabTileItem.cpp create mode 100644 Minecraft.World/StoneSlabTileItem.h create mode 100644 Minecraft.World/StoneTile.cpp create mode 100644 Minecraft.World/StoneTile.h create mode 100644 Minecraft.World/StringHelpers.cpp create mode 100644 Minecraft.World/StringHelpers.h create mode 100644 Minecraft.World/StringTag.h create mode 100644 Minecraft.World/StrongholdFeature.cpp create mode 100644 Minecraft.World/StrongholdFeature.h create mode 100644 Minecraft.World/StrongholdPieces.cpp create mode 100644 Minecraft.World/StrongholdPieces.h create mode 100644 Minecraft.World/StructureFeature.cpp create mode 100644 Minecraft.World/StructureFeature.h create mode 100644 Minecraft.World/StructurePiece.cpp create mode 100644 Minecraft.World/StructurePiece.h create mode 100644 Minecraft.World/StructureRecipies.cpp create mode 100644 Minecraft.World/StructureRecipies.h create mode 100644 Minecraft.World/StructureStart.cpp create mode 100644 Minecraft.World/StructureStart.h create mode 100644 Minecraft.World/SwampBiome.cpp create mode 100644 Minecraft.World/SwampBiome.h create mode 100644 Minecraft.World/SwampRiversLayer.cpp create mode 100644 Minecraft.World/SwampRiversLayer.h create mode 100644 Minecraft.World/SwampTreeFeature.cpp create mode 100644 Minecraft.World/SwampTreeFeature.h create mode 100644 Minecraft.World/SwellGoal.cpp create mode 100644 Minecraft.World/SwellGoal.h create mode 100644 Minecraft.World/SynchedEntityData.cpp create mode 100644 Minecraft.World/SynchedEntityData.h create mode 100644 Minecraft.World/Synth.cpp create mode 100644 Minecraft.World/Synth.h create mode 100644 Minecraft.World/System.h create mode 100644 Minecraft.World/Tag.cpp create mode 100644 Minecraft.World/Tag.h create mode 100644 Minecraft.World/TaigaBiome.cpp create mode 100644 Minecraft.World/TaigaBiome.h create mode 100644 Minecraft.World/TakeFlowerGoal.cpp create mode 100644 Minecraft.World/TakeFlowerGoal.h create mode 100644 Minecraft.World/TakeItemEntityPacket.cpp create mode 100644 Minecraft.World/TakeItemEntityPacket.h create mode 100644 Minecraft.World/TallGrass.cpp create mode 100644 Minecraft.World/TallGrass.h create mode 100644 Minecraft.World/TallGrassFeature.cpp create mode 100644 Minecraft.World/TallGrassFeature.h create mode 100644 Minecraft.World/TamableAnimal.cpp create mode 100644 Minecraft.World/TamableAnimal.h create mode 100644 Minecraft.World/TargetGoal.cpp create mode 100644 Minecraft.World/TargetGoal.h create mode 100644 Minecraft.World/TeleportEntityPacket.cpp create mode 100644 Minecraft.World/TeleportEntityPacket.h create mode 100644 Minecraft.World/TemperatureLayer.cpp create mode 100644 Minecraft.World/TemperatureLayer.h create mode 100644 Minecraft.World/TemperatureMixerLayer.cpp create mode 100644 Minecraft.World/TemperatureMixerLayer.h create mode 100644 Minecraft.World/TemptGoal.cpp create mode 100644 Minecraft.World/TemptGoal.h create mode 100644 Minecraft.World/TextureAndGeometryChangePacket.cpp create mode 100644 Minecraft.World/TextureAndGeometryChangePacket.h create mode 100644 Minecraft.World/TextureAndGeometryPacket.cpp create mode 100644 Minecraft.World/TextureAndGeometryPacket.h create mode 100644 Minecraft.World/TextureChangePacket.cpp create mode 100644 Minecraft.World/TextureChangePacket.h create mode 100644 Minecraft.World/TexturePacket.cpp create mode 100644 Minecraft.World/TexturePacket.h create mode 100644 Minecraft.World/TheEndBiome.cpp create mode 100644 Minecraft.World/TheEndBiome.h create mode 100644 Minecraft.World/TheEndBiomeDecorator.cpp create mode 100644 Minecraft.World/TheEndBiomeDecorator.h create mode 100644 Minecraft.World/TheEndDimension.cpp create mode 100644 Minecraft.World/TheEndDimension.h create mode 100644 Minecraft.World/TheEndLevelRandomLevelSource.cpp create mode 100644 Minecraft.World/TheEndLevelRandomLevelSource.h create mode 100644 Minecraft.World/TheEndPortal.cpp create mode 100644 Minecraft.World/TheEndPortal.h create mode 100644 Minecraft.World/TheEndPortalFrameTile.cpp create mode 100644 Minecraft.World/TheEndPortalFrameTile.h create mode 100644 Minecraft.World/TheEndPortalTileEntity.cpp create mode 100644 Minecraft.World/TheEndPortalTileEntity.h create mode 100644 Minecraft.World/ThinFenceTile.cpp create mode 100644 Minecraft.World/ThinFenceTile.h create mode 100644 Minecraft.World/ThornsEnchantment.cpp create mode 100644 Minecraft.World/ThornsEnchantment.h create mode 100644 Minecraft.World/ThreadName.cpp create mode 100644 Minecraft.World/ThreadName.h create mode 100644 Minecraft.World/Throwable.cpp create mode 100644 Minecraft.World/Throwable.h create mode 100644 Minecraft.World/ThrownEgg.cpp create mode 100644 Minecraft.World/ThrownEgg.h create mode 100644 Minecraft.World/ThrownEnderpearl.cpp create mode 100644 Minecraft.World/ThrownEnderpearl.h create mode 100644 Minecraft.World/ThrownExpBottle.cpp create mode 100644 Minecraft.World/ThrownExpBottle.h create mode 100644 Minecraft.World/ThrownPotion.cpp create mode 100644 Minecraft.World/ThrownPotion.h create mode 100644 Minecraft.World/TickNextTickData.cpp create mode 100644 Minecraft.World/TickNextTickData.h create mode 100644 Minecraft.World/Tile.cpp create mode 100644 Minecraft.World/Tile.h create mode 100644 Minecraft.World/TileDestructionPacket.cpp create mode 100644 Minecraft.World/TileDestructionPacket.h create mode 100644 Minecraft.World/TileEntity.cpp create mode 100644 Minecraft.World/TileEntity.h create mode 100644 Minecraft.World/TileEntityDataPacket.cpp create mode 100644 Minecraft.World/TileEntityDataPacket.h create mode 100644 Minecraft.World/TileEventData.cpp create mode 100644 Minecraft.World/TileEventData.h create mode 100644 Minecraft.World/TileEventPacket.cpp create mode 100644 Minecraft.World/TileEventPacket.h create mode 100644 Minecraft.World/TileItem.cpp create mode 100644 Minecraft.World/TileItem.h create mode 100644 Minecraft.World/TilePlanterItem.cpp create mode 100644 Minecraft.World/TilePlanterItem.h create mode 100644 Minecraft.World/TilePos.cpp create mode 100644 Minecraft.World/TilePos.h create mode 100644 Minecraft.World/TileUpdatePacket.cpp create mode 100644 Minecraft.World/TileUpdatePacket.h create mode 100644 Minecraft.World/TimeCommand.cpp create mode 100644 Minecraft.World/TimeCommand.h create mode 100644 Minecraft.World/TntTile.cpp create mode 100644 Minecraft.World/TntTile.h create mode 100644 Minecraft.World/ToggleDownfallCommand.cpp create mode 100644 Minecraft.World/ToggleDownfallCommand.h create mode 100644 Minecraft.World/ToolRecipies.cpp create mode 100644 Minecraft.World/ToolRecipies.h create mode 100644 Minecraft.World/TopSnowTile.cpp create mode 100644 Minecraft.World/TopSnowTile.h create mode 100644 Minecraft.World/TorchTile.cpp create mode 100644 Minecraft.World/TorchTile.h create mode 100644 Minecraft.World/TownFeature.h create mode 100644 Minecraft.World/TradeItemPacket.cpp create mode 100644 Minecraft.World/TradeItemPacket.h create mode 100644 Minecraft.World/TradeWithPlayerGoal.cpp create mode 100644 Minecraft.World/TradeWithPlayerGoal.h create mode 100644 Minecraft.World/TransparentTile.cpp create mode 100644 Minecraft.World/TransparentTile.h create mode 100644 Minecraft.World/TrapDoorTile.cpp create mode 100644 Minecraft.World/TrapDoorTile.h create mode 100644 Minecraft.World/TrapMenu.cpp create mode 100644 Minecraft.World/TrapMenu.h create mode 100644 Minecraft.World/TreeFeature.cpp create mode 100644 Minecraft.World/TreeFeature.h create mode 100644 Minecraft.World/TreeTile.cpp create mode 100644 Minecraft.World/TreeTile.h create mode 100644 Minecraft.World/TreeTileItem.cpp create mode 100644 Minecraft.World/TreeTileItem.h create mode 100644 Minecraft.World/TripWireSourceTile.cpp create mode 100644 Minecraft.World/TripWireSourceTile.h create mode 100644 Minecraft.World/TripWireTile.cpp create mode 100644 Minecraft.World/TripWireTile.h create mode 100644 Minecraft.World/UntouchingEnchantment.cpp create mode 100644 Minecraft.World/UntouchingEnchantment.h create mode 100644 Minecraft.World/UpdateGameRuleProgressPacket.cpp create mode 100644 Minecraft.World/UpdateGameRuleProgressPacket.h create mode 100644 Minecraft.World/UpdateMobEffectPacket.cpp create mode 100644 Minecraft.World/UpdateMobEffectPacket.h create mode 100644 Minecraft.World/UpdateProgressPacket.cpp create mode 100644 Minecraft.World/UpdateProgressPacket.h create mode 100644 Minecraft.World/UseAnim.h create mode 100644 Minecraft.World/UseItemPacket.cpp create mode 100644 Minecraft.World/UseItemPacket.h create mode 100644 Minecraft.World/Vec3.cpp create mode 100644 Minecraft.World/Vec3.h create mode 100644 Minecraft.World/Village.cpp create mode 100644 Minecraft.World/Village.h create mode 100644 Minecraft.World/VillageFeature.cpp create mode 100644 Minecraft.World/VillageFeature.h create mode 100644 Minecraft.World/VillagePieces.cpp create mode 100644 Minecraft.World/VillagePieces.h create mode 100644 Minecraft.World/VillageSiege.cpp create mode 100644 Minecraft.World/VillageSiege.h create mode 100644 Minecraft.World/Villager.cpp create mode 100644 Minecraft.World/Villager.h create mode 100644 Minecraft.World/VillagerGolem.cpp create mode 100644 Minecraft.World/VillagerGolem.h create mode 100644 Minecraft.World/Villages.cpp create mode 100644 Minecraft.World/Villages.h create mode 100644 Minecraft.World/VineTile.cpp create mode 100644 Minecraft.World/VineTile.h create mode 100644 Minecraft.World/VinesFeature.cpp create mode 100644 Minecraft.World/VinesFeature.h create mode 100644 Minecraft.World/VoronoiZoom.cpp create mode 100644 Minecraft.World/VoronoiZoom.h create mode 100644 Minecraft.World/WallTile.cpp create mode 100644 Minecraft.World/WallTile.h create mode 100644 Minecraft.World/WaterAnimal.cpp create mode 100644 Minecraft.World/WaterAnimal.h create mode 100644 Minecraft.World/WaterColor.cpp create mode 100644 Minecraft.World/WaterColor.h create mode 100644 Minecraft.World/WaterLevelChunk.cpp create mode 100644 Minecraft.World/WaterLevelChunk.h create mode 100644 Minecraft.World/WaterLilyTile.cpp create mode 100644 Minecraft.World/WaterLilyTile.h create mode 100644 Minecraft.World/WaterLilyTileItem.cpp create mode 100644 Minecraft.World/WaterLilyTileItem.h create mode 100644 Minecraft.World/WaterWorkerEnchantment.cpp create mode 100644 Minecraft.World/WaterWorkerEnchantment.h create mode 100644 Minecraft.World/WaterlilyFeature.cpp create mode 100644 Minecraft.World/WaterlilyFeature.h create mode 100644 Minecraft.World/WeaponItem.cpp create mode 100644 Minecraft.World/WeaponItem.h create mode 100644 Minecraft.World/WeaponRecipies.cpp create mode 100644 Minecraft.World/WeaponRecipies.h create mode 100644 Minecraft.World/WebMaterial.h create mode 100644 Minecraft.World/WebTile.cpp create mode 100644 Minecraft.World/WebTile.h create mode 100644 Minecraft.World/WeighedRandom.cpp create mode 100644 Minecraft.World/WeighedRandom.h create mode 100644 Minecraft.World/WeighedTreasure.cpp create mode 100644 Minecraft.World/WeighedTreasure.h create mode 100644 Minecraft.World/Wolf.cpp create mode 100644 Minecraft.World/Wolf.h create mode 100644 Minecraft.World/WoodSlabTile.cpp create mode 100644 Minecraft.World/WoodSlabTile.h create mode 100644 Minecraft.World/WoodTile.cpp create mode 100644 Minecraft.World/WoodTile.h create mode 100644 Minecraft.World/WoolCarpetTile.cpp create mode 100644 Minecraft.World/WoolCarpetTile.h create mode 100644 Minecraft.World/WorkbenchTile.cpp create mode 100644 Minecraft.World/WorkbenchTile.h create mode 100644 Minecraft.World/XZPacket.cpp create mode 100644 Minecraft.World/XZPacket.h create mode 100644 Minecraft.World/Zombie.cpp create mode 100644 Minecraft.World/Zombie.h create mode 100644 Minecraft.World/ZoneFile.cpp create mode 100644 Minecraft.World/ZoneFile.h create mode 100644 Minecraft.World/ZoneIo.cpp create mode 100644 Minecraft.World/ZoneIo.h create mode 100644 Minecraft.World/ZonedChunkStorage.cpp create mode 100644 Minecraft.World/ZonedChunkStorage.h create mode 100644 Minecraft.World/ZoomLayer.cpp create mode 100644 Minecraft.World/ZoomLayer.h create mode 100644 Minecraft.World/com.mojang.nbt.h create mode 100644 Minecraft.World/compression.cpp create mode 100644 Minecraft.World/compression.h create mode 100644 Minecraft.World/net.minecraft.commands.common.h create mode 100644 Minecraft.World/net.minecraft.commands.h create mode 100644 Minecraft.World/net.minecraft.h create mode 100644 Minecraft.World/net.minecraft.locale.h create mode 100644 Minecraft.World/net.minecraft.network.h create mode 100644 Minecraft.World/net.minecraft.network.packet.h create mode 100644 Minecraft.World/net.minecraft.stats.h create mode 100644 Minecraft.World/net.minecraft.world.ContainerListener.h create mode 100644 Minecraft.World/net.minecraft.world.damagesource.h create mode 100644 Minecraft.World/net.minecraft.world.effect.h create mode 100644 Minecraft.World/net.minecraft.world.entity.ai.control.h create mode 100644 Minecraft.World/net.minecraft.world.entity.ai.goal.h create mode 100644 Minecraft.World/net.minecraft.world.entity.ai.goal.target.h create mode 100644 Minecraft.World/net.minecraft.world.entity.ai.navigation.h create mode 100644 Minecraft.World/net.minecraft.world.entity.ai.sensing.h create mode 100644 Minecraft.World/net.minecraft.world.entity.ai.util.h create mode 100644 Minecraft.World/net.minecraft.world.entity.ai.village.h create mode 100644 Minecraft.World/net.minecraft.world.entity.animal.h create mode 100644 Minecraft.World/net.minecraft.world.entity.boss.enderdragon.h create mode 100644 Minecraft.World/net.minecraft.world.entity.boss.h create mode 100644 Minecraft.World/net.minecraft.world.entity.global.h create mode 100644 Minecraft.World/net.minecraft.world.entity.h create mode 100644 Minecraft.World/net.minecraft.world.entity.item.h create mode 100644 Minecraft.World/net.minecraft.world.entity.monster.h create mode 100644 Minecraft.World/net.minecraft.world.entity.npc.h create mode 100644 Minecraft.World/net.minecraft.world.entity.player.h create mode 100644 Minecraft.World/net.minecraft.world.entity.projectile.h create mode 100644 Minecraft.World/net.minecraft.world.food.h create mode 100644 Minecraft.World/net.minecraft.world.h create mode 100644 Minecraft.World/net.minecraft.world.inventory.ContainerListener.h create mode 100644 Minecraft.World/net.minecraft.world.inventory.h create mode 100644 Minecraft.World/net.minecraft.world.item.alchemy.h create mode 100644 Minecraft.World/net.minecraft.world.item.crafting.h create mode 100644 Minecraft.World/net.minecraft.world.item.enchantment.h create mode 100644 Minecraft.World/net.minecraft.world.item.h create mode 100644 Minecraft.World/net.minecraft.world.item.trading.h create mode 100644 Minecraft.World/net.minecraft.world.level.biome.h create mode 100644 Minecraft.World/net.minecraft.world.level.chunk.h create mode 100644 Minecraft.World/net.minecraft.world.level.chunk.storage.h create mode 100644 Minecraft.World/net.minecraft.world.level.dimension.h create mode 100644 Minecraft.World/net.minecraft.world.level.h create mode 100644 Minecraft.World/net.minecraft.world.level.levelgen.feature.h create mode 100644 Minecraft.World/net.minecraft.world.level.levelgen.h create mode 100644 Minecraft.World/net.minecraft.world.level.levelgen.structure.h create mode 100644 Minecraft.World/net.minecraft.world.level.levelgen.synth.h create mode 100644 Minecraft.World/net.minecraft.world.level.material.h create mode 100644 Minecraft.World/net.minecraft.world.level.newbiome.layer.h create mode 100644 Minecraft.World/net.minecraft.world.level.pathfinder.h create mode 100644 Minecraft.World/net.minecraft.world.level.saveddata.h create mode 100644 Minecraft.World/net.minecraft.world.level.storage.h create mode 100644 Minecraft.World/net.minecraft.world.level.tile.entity.h create mode 100644 Minecraft.World/net.minecraft.world.level.tile.h create mode 100644 Minecraft.World/net.minecraft.world.level.tile.piston.h create mode 100644 Minecraft.World/net.minecraft.world.phys.h create mode 100644 Minecraft.World/stdafx.cpp create mode 100644 Minecraft.World/stdafx.h create mode 100644 Minecraft.World/system.cpp create mode 100644 Minecraft.World/x64headers/extraX64.h create mode 100644 Minecraft.World/x64headers/qnet.h create mode 100644 Minecraft.World/x64headers/xmcore.h create mode 100644 Minecraft.World/x64headers/xrnm.h create mode 100644 Minecraft.World/x64headers/xsocialpost.h create mode 100644 Minecraft.World/x64headers/xuiapp.h create mode 100644 Minecraft.World/x64headers/xuiresource.h (limited to 'Minecraft.World') diff --git a/Minecraft.World/AABB.cpp b/Minecraft.World/AABB.cpp new file mode 100644 index 00000000..4c867f0d --- /dev/null +++ b/Minecraft.World/AABB.cpp @@ -0,0 +1,342 @@ +//package net.minecraft.world.phys; + +//import java->util.ArrayList; +//import java->util.List; + +#include "stdafx.h" +#include "AABB.h" +#include "HitResult.h" + +DWORD AABB::tlsIdx = 0; +AABB::ThreadStorage *AABB::tlsDefault = NULL; + +AABB::ThreadStorage::ThreadStorage() +{ + pool = new AABB[POOL_SIZE]; + poolPointer = 0; +} + + +AABB::ThreadStorage::~ThreadStorage() +{ + delete pool; +} + +void AABB::CreateNewThreadStorage() +{ + ThreadStorage *tls = new ThreadStorage(); + if(tlsDefault == NULL ) + { + tlsIdx = TlsAlloc(); + tlsDefault = tls; + } + + TlsSetValue(tlsIdx, tls); +} + +void AABB::UseDefaultThreadStorage() +{ + TlsSetValue(tlsIdx, tlsDefault); +} + +void AABB::ReleaseThreadStorage() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); + if( tls == tlsDefault ) return; + + delete tls; +} + +AABB *AABB::newPermanent(double x0, double y0, double z0, double x1, double y1, double z1) +{ + return new AABB(x0, y0, z0, x1, y1, z1); +} + +void AABB::clearPool() +{ +} + +void AABB::resetPool() +{ +} + +AABB *AABB::newTemp(double x0, double y0, double z0, double x1, double y1, double z1) +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); + AABB *thisAABB = &tls->pool[tls->poolPointer]; + thisAABB->set(x0, y0, z0, x1, y1, z1); + tls->poolPointer = ( tls->poolPointer + 1 ) % ThreadStorage::POOL_SIZE; + return thisAABB; +} + +AABB::AABB(double x0, double y0, double z0, double x1, double y1, double z1) +{ + this->x0 = x0; + this->y0 = y0; + this->z0 = z0; + this->x1 = x1; + this->y1 = y1; + this->z1 = z1; +} + + +AABB *AABB::set(double x0, double y0, double z0, double x1, double y1, double z1) +{ + this->x0 = x0; + this->y0 = y0; + this->z0 = z0; + this->x1 = x1; + this->y1 = y1; + this->z1 = z1; + return this; +} + +AABB *AABB::expand(double xa, double ya, double za) +{ + double _x0 = x0; + double _y0 = y0; + double _z0 = z0; + double _x1 = x1; + double _y1 = y1; + double _z1 = z1; + + if (xa < 0) _x0 += xa; + if (xa > 0) _x1 += xa; + + if (ya < 0) _y0 += ya; + if (ya > 0) _y1 += ya; + + if (za < 0) _z0 += za; + if (za > 0) _z1 += za; + + return AABB::newTemp(_x0, _y0, _z0, _x1, _y1, _z1); +} + +AABB *AABB::grow(double xa, double ya, double za) +{ + double _x0 = x0 - xa; + double _y0 = y0 - ya; + double _z0 = z0 - za; + double _x1 = x1 + xa; + double _y1 = y1 + ya; + double _z1 = z1 + za; + + return AABB::newTemp(_x0, _y0, _z0, _x1, _y1, _z1); +} + +AABB *AABB::cloneMove(double xa, double ya, double za) +{ + return AABB::newTemp(x0 + xa, y0 + ya, z0 + za, x1 + xa, y1 + ya, z1 + za); +} + +double AABB::clipXCollide(AABB *c, double xa) +{ + if (c->y1 <= y0 || c->y0 >= y1) return xa; + if (c->z1 <= z0 || c->z0 >= z1) return xa; + + if (xa > 0 && c->x1 <= x0) + { + double max = x0 - c->x1; + if (max < xa) xa = max; + } + if (xa < 0 && c->x0 >= x1) + { + double max = x1 - c->x0; + if (max > xa) xa = max; + } + + return xa; +} + +double AABB::clipYCollide(AABB *c, double ya) +{ + if (c->x1 <= x0 || c->x0 >= x1) return ya; + if (c->z1 <= z0 || c->z0 >= z1) return ya; + + if (ya > 0 && c->y1 <= y0) + { + double max = y0 - c->y1; + if (max < ya) ya = max; + } + if (ya < 0 && c->y0 >= y1) + { + double max = y1 - c->y0; + if (max > ya) ya = max; + } + + return ya; +} + +double AABB::clipZCollide(AABB *c, double za) +{ + if (c->x1 <= x0 || c->x0 >= x1) return za; + if (c->y1 <= y0 || c->y0 >= y1) return za; + + if (za > 0 && c->z1 <= z0) + { + double max = z0 - c->z1; + if (max < za) za = max; + } + if (za < 0 && c->z0 >= z1) + { + double max = z1 - c->z0; + if (max > za) za = max; + } + + return za; +} + +bool AABB::intersects(AABB *c) +{ + if (c->x1 <= x0 || c->x0 >= x1) return false; + if (c->y1 <= y0 || c->y0 >= y1) return false; + if (c->z1 <= z0 || c->z0 >= z1) return false; + return true; +} + +bool AABB::intersectsInner(AABB *c) +{ + if (c->x1 < x0 || c->x0 > x1) return false; + if (c->y1 < y0 || c->y0 > y1) return false; + if (c->z1 < z0 || c->z0 > z1) return false; + return true; +} + +AABB *AABB::move(double xa, double ya, double za) +{ + x0 += xa; + y0 += ya; + z0 += za; + x1 += xa; + y1 += ya; + z1 += za; + return this; +} + +bool AABB::intersects(double x02, double y02, double z02, double x12, double y12, double z12) +{ + if (x12 <= x0 || x02 >= x1) return false; + if (y12 <= y0 || y02 >= y1) return false; + if (z12 <= z0 || z02 >= z1) return false; + return true; +} + +bool AABB::contains(Vec3 *p) +{ + if (p->x <= x0 || p->x >= x1) return false; + if (p->y <= y0 || p->y >= y1) return false; + if (p->z <= z0 || p->z >= z1) return false; + return true; +} + +// 4J Added +bool AABB::containsIncludingLowerBound(Vec3 *p) +{ + if (p->x < x0 || p->x >= x1) return false; + if (p->y < y0 || p->y >= y1) return false; + if (p->z < z0 || p->z >= z1) return false; + return true; +} + +double AABB::getSize() +{ + double xs = x1 - x0; + double ys = y1 - y0; + double zs = z1 - z0; + return (xs + ys + zs) / 3.0f; +} + +AABB *AABB::shrink(double xa, double ya, double za) +{ + double _x0 = x0 + xa; + double _y0 = y0 + ya; + double _z0 = z0 + za; + double _x1 = x1 - xa; + double _y1 = y1 - ya; + double _z1 = z1 - za; + + return AABB::newTemp(_x0, _y0, _z0, _x1, _y1, _z1); +} + +AABB *AABB::copy() +{ + return AABB::newTemp(x0, y0, z0, x1, y1, z1); +} + +HitResult *AABB::clip(Vec3 *a, Vec3 *b) +{ + Vec3 *xh0 = a->clipX(b, x0); + Vec3 *xh1 = a->clipX(b, x1); + + Vec3 *yh0 = a->clipY(b, y0); + Vec3 *yh1 = a->clipY(b, y1); + + Vec3 *zh0 = a->clipZ(b, z0); + Vec3 *zh1 = a->clipZ(b, z1); + + if (!containsX(xh0)) xh0 = NULL; + if (!containsX(xh1)) xh1 = NULL; + if (!containsY(yh0)) yh0 = NULL; + if (!containsY(yh1)) yh1 = NULL; + if (!containsZ(zh0)) zh0 = NULL; + if (!containsZ(zh1)) zh1 = NULL; + + Vec3 *closest = NULL; + + if (xh0 != NULL && (closest == NULL || a->distanceToSqr(xh0) < a->distanceToSqr(closest))) closest = xh0; + if (xh1 != NULL && (closest == NULL || a->distanceToSqr(xh1) < a->distanceToSqr(closest))) closest = xh1; + if (yh0 != NULL && (closest == NULL || a->distanceToSqr(yh0) < a->distanceToSqr(closest))) closest = yh0; + if (yh1 != NULL && (closest == NULL || a->distanceToSqr(yh1) < a->distanceToSqr(closest))) closest = yh1; + if (zh0 != NULL && (closest == NULL || a->distanceToSqr(zh0) < a->distanceToSqr(closest))) closest = zh0; + if (zh1 != NULL && (closest == NULL || a->distanceToSqr(zh1) < a->distanceToSqr(closest))) closest = zh1; + + if (closest == NULL) return NULL; + + int face = -1; + + if (closest == xh0) face = 4; + if (closest == xh1) face = 5; + if (closest == yh0) face = 0; + if (closest == yh1) face = 1; + if (closest == zh0) face = 2; + if (closest == zh1) face = 3; + + return new HitResult(0, 0, 0, face, closest); +} + + +bool AABB::containsX(Vec3 *v) +{ + if (v == NULL) return false; + return v->y >= y0 && v->y <= y1 && v->z >= z0 && v->z <= z1; +} + +bool AABB::containsY(Vec3 *v) +{ + if (v == NULL) return false; + return v->x >= x0 && v->x <= x1 && v->z >= z0 && v->z <= z1; +} + +bool AABB::containsZ(Vec3 *v) +{ + if (v == NULL) return false; + return v->x >= x0 && v->x <= x1 && v->y >= y0 && v->y <= y1; +} + + +void AABB::set(AABB *b) +{ + this->x0 = b->x0; + this->y0 = b->y0; + this->z0 = b->z0; + this->x1 = b->x1; + this->y1 = b->y1; + this->z1 = b->z1; +} + +wstring AABB::toString() +{ + return L"box[" + _toString(x0) + L", " + _toString(y0) + L", " + _toString(z0) + L" -> " + + _toString(x1) + L", " + _toString(y1) + L", " + _toString(z1) + L"]"; +} + diff --git a/Minecraft.World/AABB.h b/Minecraft.World/AABB.h new file mode 100644 index 00000000..81405ea1 --- /dev/null +++ b/Minecraft.World/AABB.h @@ -0,0 +1,65 @@ +#pragma once + +#include "Vec3.h" +#include "Definitions.h" + +class HitResult; +using namespace std; + +class AABB +{ + // 4J added so we can have separate pools for different threads + class ThreadStorage + { + public: + static const int POOL_SIZE = 1024; + AABB *pool; + unsigned int poolPointer; + ThreadStorage(); + ~ThreadStorage(); + }; + static DWORD tlsIdx; + static ThreadStorage *tlsDefault; +public: + // Each new thread that needs to use Vec3 pools will need to call one of the following 2 functions, to either create its own + // local storage, or share the default storage already allocated by the main thread + static void CreateNewThreadStorage(); + static void UseDefaultThreadStorage(); + static void ReleaseThreadStorage(); + + static AABB *newPermanent(double x0, double y0, double z0, double x1, double y1, double z1); + static void clearPool(); + static void resetPool(); + static AABB *newTemp(double x0, double y0, double z0, double x1, double y1, double z1); + + double x0, y0, z0; + double x1, y1, z1; + +private: + AABB(double x0, double y0, double z0, double x1, double y1, double z1); + AABB() {} +public: + AABB *set(double x0, double y0, double z0, double x1, double y1, double z1); + AABB *expand(double xa, double ya, double za); + AABB *grow(double xa, double ya, double za); +public: + AABB *cloneMove(double xa, double ya, double za); + double clipXCollide(AABB *c, double xa); + double clipYCollide(AABB *c, double ya); + double clipZCollide(AABB *c, double za); + bool intersects(AABB *c); + bool intersectsInner(AABB *c); + AABB *move(double xa, double ya, double za); + bool intersects(double x02, double y02, double z02, double x12, double y12, double z12); + bool contains(Vec3 *p); + bool containsIncludingLowerBound(Vec3 *p); // 4J Added + double getSize(); + AABB *shrink(double xa, double ya, double za); + AABB *copy(); + HitResult *clip(Vec3 *a, Vec3 *b); + bool containsX(Vec3 *v); + bool containsY(Vec3 *v); + bool containsZ(Vec3 *v); + void set(AABB *b); + wstring toString(); +}; diff --git a/Minecraft.World/Abilities.cpp b/Minecraft.World/Abilities.cpp new file mode 100644 index 00000000..06006860 --- /dev/null +++ b/Minecraft.World/Abilities.cpp @@ -0,0 +1,77 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "Abilities.h" + +Abilities::Abilities() +{ + invulnerable = false; + flying = false; + mayfly = false; + instabuild = false; + mayBuild = true; + flyingSpeed = 0.05f; + walkingSpeed = 0.1f; + +#ifdef _DEBUG_MENUS_ENABLED + debugflying = false; +#endif +} + +void Abilities::addSaveData(CompoundTag *parentTag) +{ + CompoundTag *tag = new CompoundTag(); + + tag->putBoolean(L"invulnerable", invulnerable); + tag->putBoolean(L"flying", flying); + tag->putBoolean(L"mayfly", mayfly); + tag->putBoolean(L"instabuild", instabuild); + tag->putBoolean(L"mayBuild", mayBuild); + tag->putFloat(L"flySpeed", flyingSpeed); + tag->putFloat(L"walkSpeed", walkingSpeed); + + parentTag->put(L"abilities", tag); + +} + +void Abilities::loadSaveData(CompoundTag *parentTag) +{ + if (parentTag->contains(L"abilities")) + { + CompoundTag *tag = parentTag->getCompound(L"abilities"); + + invulnerable = tag->getBoolean(L"invulnerable"); + flying = tag->getBoolean(L"flying"); + mayfly = tag->getBoolean(L"mayfly"); + instabuild = tag->getBoolean(L"instabuild"); + + if (tag->contains(L"flySpeed")) + { + flyingSpeed = tag->getFloat(L"flySpeed"); + walkingSpeed = tag->getFloat(L"walkSpeed"); + } + if (tag->contains(L"mayBuild")) + { + mayBuild = tag->getBoolean(L"mayBuild"); + } + } +} + +float Abilities::getFlyingSpeed() +{ + return flyingSpeed; +} + +void Abilities::setFlyingSpeed(float value) +{ + this->flyingSpeed = value; +} + +float Abilities::getWalkingSpeed() +{ + return walkingSpeed; +} + +void Abilities::setWalkingSpeed(float value) +{ + this->walkingSpeed = value; +} \ No newline at end of file diff --git a/Minecraft.World/Abilities.h b/Minecraft.World/Abilities.h new file mode 100644 index 00000000..f5895cdd --- /dev/null +++ b/Minecraft.World/Abilities.h @@ -0,0 +1,32 @@ +#pragma once + +class CompoundTag; + +class Abilities +{ +public: + bool invulnerable; + bool flying; + bool mayfly; + bool instabuild; + bool mayBuild; + +private: + float flyingSpeed; + float walkingSpeed; + +public: +#ifdef _DEBUG_MENUS_ENABLED + bool debugflying; +#endif + + Abilities(); + + void addSaveData(CompoundTag *parentTag); + void loadSaveData(CompoundTag *parentTag); + + float getFlyingSpeed(); + void setFlyingSpeed(float value); + float getWalkingSpeed(); + void setWalkingSpeed(float value); +}; \ No newline at end of file diff --git a/Minecraft.World/AbstractContainerMenu.cpp b/Minecraft.World/AbstractContainerMenu.cpp new file mode 100644 index 00000000..71d30feb --- /dev/null +++ b/Minecraft.World/AbstractContainerMenu.cpp @@ -0,0 +1,537 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "Slot.h" +#include "AbstractContainerMenu.h" + +// 4J Stu - The java does not have ctor here (being an abstract) but we need one to initialise the member variables +// TODO Make sure all derived classes also call this +AbstractContainerMenu::AbstractContainerMenu() +{ + lastSlots = new vector >(); + slots = new vector(); + containerId = 0; + + changeUid = 0; + m_bNeedsRendered = false; + + containerListeners = new vector(); +} + +AbstractContainerMenu::~AbstractContainerMenu() +{ + delete lastSlots; + for( unsigned int i = 0; i < slots->size(); i++ ) + { + delete slots->at(i); + } + delete slots; + delete containerListeners; +} + +Slot *AbstractContainerMenu::addSlot(Slot *slot) +{ + slot->index = (int)slots->size(); + slots->push_back(slot); + lastSlots->push_back(nullptr); + return slot; +} + + +void AbstractContainerMenu::addSlotListener(ContainerListener *listener) +{ + // TODO 4J Add exceptions + /* + if (containerListeners->contains(listener)) { + throw new IllegalArgumentException("Listener already listening"); + } + */ + containerListeners->push_back(listener); + + + vector > *items = getItems(); + listener->refreshContainer(this, items); + delete items; + broadcastChanges(); +} + +vector > *AbstractContainerMenu::getItems() +{ + vector > *items = new vector >(); + AUTO_VAR(itEnd, slots->end()); + for (AUTO_VAR(it, slots->begin()); it != itEnd; it++) + { + items->push_back((*it)->getItem()); + } + return items; +} + +void AbstractContainerMenu::sendData(int id, int value) +{ + AUTO_VAR(itEnd, containerListeners->end()); + for (AUTO_VAR(it, containerListeners->begin()); it != itEnd; it++) + { + (*it)->setContainerData(this, id, value); + } +} + +void AbstractContainerMenu::broadcastChanges() +{ + for (unsigned int i = 0; i < slots->size(); i++) + { + shared_ptr current = slots->at(i)->getItem(); + shared_ptr expected = lastSlots->at(i); + if (!ItemInstance::matches(expected, current)) + { + expected = current == NULL ? nullptr : current->copy(); + (*lastSlots)[i] = expected; + m_bNeedsRendered = true; + + AUTO_VAR(itEnd, containerListeners->end()); + for (AUTO_VAR(it, containerListeners->begin()); it != itEnd; it++) + { + (*it)->slotChanged(this, i, expected); + } + } + } +} + +bool AbstractContainerMenu::needsRendered() +{ + bool needsRendered = m_bNeedsRendered; + m_bNeedsRendered = false; + + for (unsigned int i = 0; i < slots->size(); i++) + { + shared_ptr current = slots->at(i)->getItem(); + shared_ptr expected = lastSlots->at(i); + if (!ItemInstance::matches(expected, current)) + { + expected = current == NULL ? nullptr : current->copy(); + (*lastSlots)[i] = expected; + needsRendered = true; + } + } + + return needsRendered; +} + +bool AbstractContainerMenu::clickMenuButton(shared_ptr player, int buttonId) +{ + return false; +} + +Slot *AbstractContainerMenu::getSlotFor(shared_ptr c, int index) +{ + AUTO_VAR(itEnd, slots->end()); + for (AUTO_VAR(it, slots->begin()); it != itEnd; it++) + { + Slot *slot = *it; //slots->at(i); + if (slot->isAt(c, index)) + { + return slot; + } + } + return NULL; +} + +Slot *AbstractContainerMenu::getSlot(int index) +{ + return slots->at(index); +} + +shared_ptr AbstractContainerMenu::quickMoveStack(shared_ptr player, int slotIndex) +{ + Slot *slot = slots->at(slotIndex); + if (slot != NULL) + { + return slot->getItem(); + } + return nullptr; +} + +shared_ptr AbstractContainerMenu::clicked(int slotIndex, int buttonNum, int clickType, shared_ptr player) +{ + shared_ptr clickedEntity = nullptr; + shared_ptr inventory = player->inventory; + + if ((clickType == CLICK_PICKUP || clickType == CLICK_QUICK_MOVE) && (buttonNum == 0 || buttonNum == 1)) + { + if (slotIndex == CLICKED_OUTSIDE) + { + if (inventory->getCarried() != NULL) + { + if (slotIndex == CLICKED_OUTSIDE) + { + if (buttonNum == 0) + { + player->drop(inventory->getCarried()); + inventory->setCarried(nullptr); + } + if (buttonNum == 1) + { + player->drop(inventory->getCarried()->remove(1)); + if (inventory->getCarried()->count == 0) inventory->setCarried(nullptr); + } + + } + } + } + else if (clickType == CLICK_QUICK_MOVE) + { + Slot *slot = slots->at(slotIndex); + if(slot != NULL && slot->mayPickup(player)) + { + shared_ptr piiClicked = quickMoveStack(player, slotIndex); + if (piiClicked != NULL) + { + //int oldSize = piiClicked->count; // 4J - Commented 1.8.2 and replaced with below + int oldType = piiClicked->id; + + clickedEntity = piiClicked->copy(); + + if (slot != NULL) + { + if (slot->getItem() != NULL && slot->getItem()->id == oldType) + { + // 4J Stu - Brought forward loopClick from 1.2 to fix infinite recursion bug in creative + loopClick(slotIndex, buttonNum, true, player); + } + } + } + } + } + else + { + if (slotIndex < 0) return nullptr; + + Slot *slot = slots->at(slotIndex); + if (slot != NULL) + { + shared_ptr clicked = slot->getItem(); + shared_ptr carried = inventory->getCarried(); + + if (clicked != NULL) + { + clickedEntity = clicked->copy(); + } + + if (clicked == NULL) + { + if (carried != NULL && slot->mayPlace(carried)) + { + int c = buttonNum == 0 ? carried->count : 1; + if (c > slot->getMaxStackSize()) + { + c = slot->getMaxStackSize(); + } + slot->set(carried->remove(c)); + if (carried->count == 0) + { + inventory->setCarried(nullptr); + } + } + } + // 4J Added for dyable armour and combinining damaged items + else if (buttonNum == 1 && mayCombine(slot, carried)) + { + shared_ptr combined = slot->combine(carried); + if(combined != NULL) + { + slot->set(combined); + if(!player->abilities.instabuild) carried->remove(1); + if (carried->count == 0) + { + inventory->setCarried(nullptr); + } + } + } + else if (slot->mayPickup(player)) + { + if (carried == NULL) + { + // pick up to empty hand + int c = buttonNum == 0 ? clicked->count : (clicked->count + 1) / 2; + shared_ptr removed = slot->remove(c); + + inventory->setCarried(removed); + if (clicked->count == 0) + { + slot->set(nullptr); + } + slot->onTake(player, inventory->getCarried()); + } + else if (slot->mayPlace(carried)) + { + // put down and/or pick up + if (clicked->id != carried->id || clicked->getAuxValue() != carried->getAuxValue() || !ItemInstance::tagMatches(clicked, carried)) + { + // no match, replace + if (carried->count <= slot->getMaxStackSize()) + { + slot->set(carried); + inventory->setCarried(clicked); + } + } + else + { + // match, attempt to fill slot + int c = buttonNum == 0 ? carried->count : 1; + if (c > slot->getMaxStackSize() - clicked->count) + { + c = slot->getMaxStackSize() - clicked->count; + } + if (c > carried->getMaxStackSize() - clicked->count) + { + c = carried->getMaxStackSize() - clicked->count; + } + carried->remove(c); + if (carried->count == 0) + { + inventory->setCarried(nullptr); + } + clicked->count += c; + } + } + else + { + // pick up to non-empty hand + if (clicked->id == carried->id && carried->getMaxStackSize() > 1 && (!clicked->isStackedByData() || clicked->getAuxValue() == carried->getAuxValue()) + && ItemInstance::tagMatches(clicked, carried)) + { + int c = clicked->count; + if (c > 0 && c + carried->count <= carried->getMaxStackSize()) + { + carried->count += c; + clicked = slot->remove(c); + if (clicked->count == 0) slot->set(nullptr); + slot->onTake(player, inventory->getCarried()); + } + } + } + + + } + slot->setChanged(); + } + } + } + else if (clickType == CLICK_SWAP && buttonNum >= 0 && buttonNum < 9) + { + Slot *slot = slots->at(slotIndex); + if (slot->mayPickup(player)) + { + shared_ptr current = inventory->getItem(buttonNum); + bool canMove = current == NULL || (slot->container == inventory && slot->mayPlace(current)); + int freeSlot = -1; + + if (!canMove) + { + freeSlot = inventory->getFreeSlot(); + canMove |= freeSlot > -1; + } + + if (slot->hasItem() && canMove) + { + shared_ptr taking = slot->getItem(); + inventory->setItem(buttonNum, taking); + + if ((slot->container == inventory && slot->mayPlace(current)) || current == NULL) + { + slot->remove(taking->count); + slot->set(current); + slot->onTake(player, taking); + } + else if (freeSlot > -1) + { + inventory->add(current); + slot->remove(taking->count); + slot->set(nullptr); + slot->onTake(player, taking); + } + } + else if (!slot->hasItem() && current != NULL && slot->mayPlace(current)) + { + inventory->setItem(buttonNum, nullptr); + slot->set(current); + } + } + } + else if (clickType == CLICK_CLONE && player->abilities.instabuild && inventory->getCarried() == NULL && slotIndex >= 0) + { + Slot *slot = slots->at(slotIndex); + if (slot != NULL && slot->hasItem()) + { + shared_ptr copy = slot->getItem()->copy(); + copy->count = copy->getMaxStackSize(); + inventory->setCarried(copy); + } + } + return clickedEntity; +} + +// 4J Stu - Brought forward from 1.2 to fix infinite recursion bug in creative +void AbstractContainerMenu::loopClick(int slotIndex, int buttonNum, bool quickKeyHeld, shared_ptr player) +{ + clicked(slotIndex, buttonNum, CLICK_QUICK_MOVE, player); +} + +bool AbstractContainerMenu::mayCombine(Slot *slot, shared_ptr item) +{ + return false; +} + +void AbstractContainerMenu::removed(shared_ptr player) +{ + shared_ptr inventory = player->inventory; + if (inventory->getCarried() != NULL) + { + player->drop(inventory->getCarried()); + inventory->setCarried(nullptr); + } +} + +void AbstractContainerMenu::slotsChanged()// 4J used to take a shared_ptr but wasn't using it, so removed to simplify things +{ + broadcastChanges(); +} + +bool AbstractContainerMenu::isPauseScreen() +{ + return false; +} + +void AbstractContainerMenu::setItem(unsigned int slot, shared_ptr item) +{ + getSlot(slot)->set(item); +} + +void AbstractContainerMenu::setAll(ItemInstanceArray *items) +{ + for (unsigned int i = 0; i < items->length; i++) + { + getSlot(i)->set( (*items)[i] ); + } +} + +void AbstractContainerMenu::setData(int id, int value) +{ +} + +short AbstractContainerMenu::backup(shared_ptr inventory) +{ + changeUid++; + return changeUid; +} + +bool AbstractContainerMenu::isSynched(shared_ptr player) +{ + return !(unSynchedPlayers.find(player) != unSynchedPlayers.end()); +} + +void AbstractContainerMenu::setSynched(shared_ptr player, bool synched) +{ + if (synched) + { + AUTO_VAR(it, unSynchedPlayers.find(player)); + + if(it != unSynchedPlayers.end()) unSynchedPlayers.erase( it ); + } + else + { + unSynchedPlayers.insert(player); + } +} + +// 4J Stu - Brought a few changes in this function forward from 1.2 to make it return a bool +bool AbstractContainerMenu::moveItemStackTo(shared_ptr itemStack, int startSlot, int endSlot, bool backwards) +{ + bool anythingChanged = false; + + int destSlot = startSlot; + if (backwards) + { + destSlot = endSlot - 1; + } + + // find stackable slots first + if (itemStack->isStackable()) + { + while (itemStack->count > 0 && ((!backwards && destSlot < endSlot) || (backwards && destSlot >= startSlot))) + { + + Slot *slot = slots->at(destSlot); + shared_ptr target = slot->getItem(); + if (target != NULL && target->id == itemStack->id && (!itemStack->isStackedByData() || itemStack->getAuxValue() == target->getAuxValue()) + && ItemInstance::tagMatches(itemStack, target) ) + { + int totalStack = target->count + itemStack->count; + if (totalStack <= itemStack->getMaxStackSize()) + { + itemStack->count = 0; + target->count = totalStack; + slot->setChanged(); + anythingChanged = true; + } + else if (target->count < itemStack->getMaxStackSize()) + { + itemStack->count -= (itemStack->getMaxStackSize() - target->count); + target->count = itemStack->getMaxStackSize(); + slot->setChanged(); + anythingChanged = true; + } + } + + if (backwards) + { + destSlot--; + } + else + { + destSlot++; + } + } + } + + // find empty slot + if (itemStack->count > 0) + { + if (backwards) + { + destSlot = endSlot - 1; + } + else + { + destSlot = startSlot; + } + while ((!backwards && destSlot < endSlot) || (backwards && destSlot >= startSlot)) + { + Slot *slot = slots->at(destSlot); + shared_ptr target = slot->getItem(); + + if (target == NULL) + { + slot->set(itemStack->copy()); + slot->setChanged(); + itemStack->count = 0; + anythingChanged = true; + break; + } + + if (backwards) + { + destSlot--; + } + else + { + destSlot++; + } + } + } + return anythingChanged; +} + +bool AbstractContainerMenu::isOverrideResultClick(int slotNum, int buttonNum) +{ + return false; +} diff --git a/Minecraft.World/AbstractContainerMenu.h b/Minecraft.World/AbstractContainerMenu.h new file mode 100644 index 00000000..f34e1afc --- /dev/null +++ b/Minecraft.World/AbstractContainerMenu.h @@ -0,0 +1,88 @@ +#pragma once +using namespace std; + +#include "Player.h" +#include "net.minecraft.world.inventory.ContainerListener.h" +using net_minecraft_world_inventory::ContainerListener; + +class Inventory; +class Slot; +class Item; +class ItemInstance; +class Container; + +class AbstractContainerMenu +{ +public: + static const int CLICKED_OUTSIDE = -999; + + static const int CLICK_PICKUP = 0; + static const int CLICK_QUICK_MOVE = 1; + static const int CLICK_SWAP = 2; + static const int CLICK_CLONE = 3; + + // 4J Stu - Added these to fix problem with items picked up while in the creative menu replacing slots in the creative menu + static const int CONTAINER_ID_CARRIED = -1; + static const int CONTAINER_ID_INVENTORY = 0; + static const int CONTAINER_ID_CREATIVE = -2; + + vector > *lastSlots; + vector *slots; + int containerId; + +private: + short changeUid; + bool m_bNeedsRendered; // 4J added + +protected: + vector *containerListeners; + + // 4J Stu - The java does not have ctor here (being an abstract) but we need one to initialise the member variables + // TODO Make sure all derived classes also call this + AbstractContainerMenu(); + + Slot *addSlot(Slot *slot); + +public: + virtual ~AbstractContainerMenu(); + virtual void addSlotListener(ContainerListener *listener); + vector > *getItems(); + void sendData(int id, int value); + virtual void broadcastChanges(); + virtual bool needsRendered(); + virtual bool clickMenuButton(shared_ptr player, int buttonId); + Slot *getSlotFor(shared_ptr c, int index); + Slot *getSlot(int index); + virtual shared_ptr quickMoveStack(shared_ptr player, int slotIndex); + virtual shared_ptr clicked(int slotIndex, int buttonNum, int clickType, shared_ptr player); + virtual bool mayCombine(Slot *slot, shared_ptr item); +protected: + virtual void loopClick(int slotIndex, int buttonNum, bool quickKeyHeld, shared_ptr player); +public: + virtual void removed(shared_ptr player); + virtual void slotsChanged();// 4J used to take a shared_ptr container but wasn't using it, so removed to simplify things + bool isPauseScreen(); + void setItem(unsigned int slot, shared_ptr item); + void setAll(ItemInstanceArray *items); + virtual void setData(int id, int value); + short backup(shared_ptr inventory); + +private: + unordered_set , PlayerKeyHash, PlayerKeyEq> unSynchedPlayers; + +public: + bool isSynched(shared_ptr player); + void setSynched(shared_ptr player, bool synched); + virtual bool stillValid(shared_ptr player) = 0; + + // 4J Stu Added for UI + unsigned int getSize() { return (unsigned int)slots->size(); } + + +protected: + // 4J Stu - Changes to return bool brought forward from 1.2 + bool moveItemStackTo(shared_ptr itemStack, int startSlot, int endSlot, bool backwards); + +public: + virtual bool isOverrideResultClick(int slotNum, int buttonNum); +}; diff --git a/Minecraft.World/Achievement.cpp b/Minecraft.World/Achievement.cpp new file mode 100644 index 00000000..a0dfd533 --- /dev/null +++ b/Minecraft.World/Achievement.cpp @@ -0,0 +1,82 @@ +#include "stdafx.h" +#include "net.minecraft.locale.h" +#include "ItemInstance.h" +#include "Achievements.h" +#include "DescFormatter.h" +#include "Achievement.h" + +void Achievement::_init() +{ + isGoldenVar = false; + + if (x < Achievements::xMin) Achievements::xMin = x; + if (y < Achievements::yMin) Achievements::yMin = y; + if (x > Achievements::xMax) Achievements::xMax = x; + if (y > Achievements::yMax) Achievements::yMax = y; +} + +Achievement::Achievement(int id, const wstring& name, int x, int y, Item *icon, Achievement *requires) + : Stat( Achievements::ACHIEVEMENT_OFFSET + id, I18n::get(wstring(L"achievement.").append(name)) ), desc( I18n::get(wstring(L"achievement.").append(name).append(L".desc"))), icon( new ItemInstance(icon) ), x(x), y(y), requires(requires) +{ +} + +Achievement::Achievement(int id, const wstring& name, int x, int y, Tile *icon, Achievement *requires) + : Stat( Achievements::ACHIEVEMENT_OFFSET + id, I18n::get(wstring(L"achievement.").append(name)) ), desc( I18n::get(wstring(L"achievement.").append(name).append(L".desc"))), icon( new ItemInstance(icon) ), x(x), y(y), requires(requires) +{ +} + +Achievement::Achievement(int id, const wstring& name, int x, int y, shared_ptr icon, Achievement *requires) + : Stat( Achievements::ACHIEVEMENT_OFFSET + id, I18n::get(wstring(L"achievement.").append(name)) ), desc( I18n::get(wstring(L"achievement.").append(name).append(L".desc"))), icon(icon), x(x), y(y), requires(requires) +{ +} + +Achievement *Achievement::setAwardLocallyOnly() +{ + awardLocallyOnly = true; + return this; +} + +Achievement *Achievement::setGolden() +{ + isGoldenVar = true; + return this; +} + +Achievement *Achievement::postConstruct() +{ + Stat::postConstruct(); + + Achievements::achievements->push_back(this); + + return this; +} + +bool Achievement::isAchievement() +{ + return true; +} + +wstring Achievement::getDescription() +{ + if (descFormatter != NULL) + { + return descFormatter->format(desc); + } + return desc; +} + +Achievement *Achievement::setDescFormatter(DescFormatter *descFormatter) +{ + this->descFormatter = descFormatter; + return this; +} + +bool Achievement::isGolden() +{ + return isGoldenVar; +} + +int Achievement::getAchievementID() +{ + return id - Achievements::ACHIEVEMENT_OFFSET; +} diff --git a/Minecraft.World/Achievement.h b/Minecraft.World/Achievement.h new file mode 100644 index 00000000..b39c44b3 --- /dev/null +++ b/Minecraft.World/Achievement.h @@ -0,0 +1,38 @@ +#pragma once +using namespace std; + +#include "Stat.h" + +class DescFormatter; + +class Achievement : public Stat +{ +public: + const int x, y; + Achievement *requires; + +private: + const wstring desc; + DescFormatter *descFormatter; + +public: + const shared_ptr icon; + +private: + bool isGoldenVar; + void _init(); + +public: + Achievement(int id, const wstring& name, int x, int y, Item *icon, Achievement *requires); + Achievement(int id, const wstring& name, int x, int y, Tile *icon, Achievement *requires); + Achievement(int id, const wstring& name, int x, int y, shared_ptr icon, Achievement *requires); + + Achievement *setAwardLocallyOnly(); + Achievement *setGolden(); + Achievement *postConstruct(); + bool isAchievement(); + wstring getDescription(); + Achievement *setDescFormatter(DescFormatter *descFormatter); + bool isGolden(); + int getAchievementID(); +}; diff --git a/Minecraft.World/Achievements.cpp b/Minecraft.World/Achievements.cpp new file mode 100644 index 00000000..a3d33cbd --- /dev/null +++ b/Minecraft.World/Achievements.cpp @@ -0,0 +1,185 @@ +#include "stdafx.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.h" +#include "Achievement.h" +#include "Achievements.h" + + +const int Achievements::ACHIEVEMENT_OFFSET = 0x500000; + +// maximum position of achievements (min and max) + +int Achievements::xMin = 4294967295; // 4J Stu Was 4294967296 which is 1 larger than maxint. Hopefully no side effects +int Achievements::yMin = 4294967295; // 4J Stu Was 4294967296 which is 1 larger than maxint. Hopefully no side effects +int Achievements::xMax = 0; +int Achievements::yMax = 0; + +vector *Achievements::achievements = new vector; + +Achievement *Achievements::openInventory = NULL; +Achievement *Achievements::mineWood = NULL; +Achievement *Achievements::buildWorkbench = NULL; +Achievement *Achievements::buildPickaxe = NULL; +Achievement *Achievements::buildFurnace = NULL; +Achievement *Achievements::acquireIron = NULL; +Achievement *Achievements::buildHoe = NULL; +Achievement *Achievements::makeBread = NULL; +Achievement *Achievements::bakeCake = NULL; +Achievement *Achievements::buildBetterPickaxe = NULL; +Achievement *Achievements::cookFish = NULL; +Achievement *Achievements::onARail = NULL; +Achievement *Achievements::buildSword = NULL; +Achievement *Achievements::killEnemy = NULL; +Achievement *Achievements::killCow = NULL; +Achievement *Achievements::flyPig = NULL; + +Achievement *Achievements::snipeSkeleton = NULL; +Achievement *Achievements::diamonds = NULL; +//Achievement *Achievements::portal = NULL; +Achievement *Achievements::ghast = NULL; +Achievement *Achievements::blazeRod = NULL; +Achievement *Achievements::potion = NULL; +Achievement *Achievements::theEnd = NULL; +Achievement *Achievements::winGame = NULL; +Achievement *Achievements::enchantments = NULL; +//Achievement *Achievements::overkill = NULL; +//Achievement *Achievements::bookcase = NULL; + +// 4J : WESTY : Added new acheivements. +Achievement *Achievements::leaderOfThePack = NULL; +Achievement *Achievements::MOARTools = NULL; +Achievement *Achievements::dispenseWithThis = NULL; +Achievement *Achievements::InToTheNether = NULL; + +// 4J : WESTY : Added other awards. +Achievement *Achievements::socialPost = NULL; +Achievement *Achievements::eatPorkChop = NULL; +Achievement *Achievements::play100Days = NULL; +Achievement *Achievements::arrowKillCreeper = NULL; +Achievement *Achievements::mine100Blocks = NULL; +Achievement *Achievements::kill10Creepers = NULL; + +#ifdef _EXTENDED_ACHIEVEMENTS +Achievement *Achievements::overkill = NULL; // Restored old achivements. +Achievement *Achievements::bookcase = NULL; // Restored old achivements. + +// 4J-JEV: New Achievements for Orbis. +Achievement *Achievements::adventuringTime = NULL; +Achievement *Achievements::repopulation = NULL; +//Achievement *Achievements::porkChop = NULL; +Achievement *Achievements::diamondsToYou = NULL; +//Achievement *Achievements::passingTheTime = NULL; +//Achievement *Achievements::archer = NULL; +Achievement *Achievements::theHaggler = NULL; +Achievement *Achievements::potPlanter = NULL; +Achievement *Achievements::itsASign = NULL; +Achievement *Achievements::ironBelly = NULL; +Achievement *Achievements::haveAShearfulDay = NULL; +Achievement *Achievements::rainbowCollection = NULL; +Achievement *Achievements::stayinFrosty = NULL; +Achievement *Achievements::chestfulOfCobblestone = NULL; +Achievement *Achievements::renewableEnergy = NULL; +Achievement *Achievements::musicToMyEars = NULL; +Achievement *Achievements::bodyGuard = NULL; +Achievement *Achievements::ironMan = NULL; +Achievement *Achievements::zombieDoctor = NULL; +Achievement *Achievements::lionTamer = NULL; +#endif + +void Achievements::staticCtor() +{ + Achievements::openInventory = (new Achievement(eAward_TakingInventory, L"openInventory", 0, 0, Item::book, NULL))->setAwardLocallyOnly()->postConstruct(); + Achievements::mineWood = (new Achievement(eAward_GettingWood, L"mineWood", 2, 1, Tile::treeTrunk, (Achievement *) openInventory))->postConstruct(); + Achievements::buildWorkbench = (new Achievement(eAward_Benchmarking, L"buildWorkBench", 4, -1, Tile::workBench, (Achievement *) mineWood))->postConstruct(); + Achievements::buildPickaxe = (new Achievement(eAward_TimeToMine, L"buildPickaxe", 4, 2, Item::pickAxe_wood, (Achievement *) buildWorkbench))->postConstruct(); + Achievements::buildFurnace = (new Achievement(eAward_HotTopic, L"buildFurnace", 3, 4, Tile::furnace_lit, (Achievement *) buildPickaxe))->postConstruct(); + Achievements::acquireIron = (new Achievement(eAward_AquireHardware, L"acquireIron", 1, 4, Item::ironIngot, (Achievement *) buildFurnace))->postConstruct(); + Achievements::buildHoe = (new Achievement(eAward_TimeToFarm, L"buildHoe", 2, -3, Item::hoe_wood, (Achievement *) buildWorkbench))->postConstruct(); + Achievements::makeBread = (new Achievement(eAward_BakeBread, L"makeBread", -1, -3, Item::bread, (Achievement *) buildHoe))->postConstruct(); + Achievements::bakeCake = (new Achievement(eAward_TheLie, L"bakeCake", 0, -5, Item::cake, (Achievement *) buildHoe))->postConstruct(); + Achievements::buildBetterPickaxe = (new Achievement(eAward_GettingAnUpgrade, L"buildBetterPickaxe", 6, 2, Item::pickAxe_stone, (Achievement *) buildPickaxe))->postConstruct(); + Achievements::cookFish = (new Achievement(eAward_DeliciousFish, L"cookFish", 2, 6, Item::fish_cooked, (Achievement *) buildFurnace))->postConstruct(); + Achievements::onARail = (new Achievement(eAward_OnARail, L"onARail", 2, 3, Tile::rail, (Achievement *) acquireIron))->setGolden()->postConstruct(); + Achievements::buildSword = (new Achievement(eAward_TimeToStrike, L"buildSword", 6, -1, Item::sword_wood, (Achievement *) buildWorkbench))->postConstruct(); + Achievements::killEnemy = (new Achievement(eAward_MonsterHunter, L"killEnemy", 8, -1, Item::bone, (Achievement *) buildSword))->postConstruct(); + Achievements::killCow = (new Achievement(eAward_CowTipper, L"killCow", 7, -3, Item::leather, (Achievement *) buildSword))->postConstruct(); + Achievements::flyPig = (new Achievement(eAward_WhenPigsFly, L"flyPig", 8, -4, Item::saddle, (Achievement *) killCow))->setGolden()->postConstruct(); + + // 4J Stu - The order of these achievemnts is very important, as they map directly to data stored in the profile data. New achievements should be added at the end. + + // 4J : WESTY : Added new achievements. Note, params "x", "y", "icon" and "requires" are ignored on xbox. + Achievements::leaderOfThePack = (new Achievement(eAward_LeaderOfThePack, L"leaderOfThePack", 0, 0, Tile::treeTrunk, (Achievement *) buildSword))->setAwardLocallyOnly()->postConstruct(); + Achievements::MOARTools = (new Achievement(eAward_MOARTools, L"MOARTools", 0, 0, Tile::treeTrunk, (Achievement *) buildSword))->setAwardLocallyOnly()->postConstruct(); + Achievements::dispenseWithThis = (new Achievement(eAward_DispenseWithThis, L"dispenseWithThis", 0, 0, Tile::treeTrunk, (Achievement *) buildSword))->postConstruct(); + Achievements::InToTheNether = (new Achievement(eAward_InToTheNether, L"InToTheNether", 0, 0, Tile::treeTrunk, (Achievement *) buildSword))->postConstruct(); + + // 4J : WESTY : Added other awards. + Achievements::mine100Blocks = (new Achievement(eAward_mine100Blocks, L"mine100Blocks", 0, 0, Tile::treeTrunk, (Achievement *) buildSword))->setAwardLocallyOnly()->postConstruct(); + Achievements::kill10Creepers = (new Achievement(eAward_kill10Creepers, L"kill10Creepers", 0, 0, Tile::treeTrunk, (Achievement *) buildSword))->setAwardLocallyOnly()->postConstruct(); +#ifdef _EXTENDED_ACHIEVEMENTS + Achievements::eatPorkChop = (new Achievement(eAward_eatPorkChop, L"eatPorkChop", 0, 0, Tile::treeTrunk, (Achievement *) buildSword))->setAwardLocallyOnly()->postConstruct(); +#else + Achievements::eatPorkChop = (new Achievement(eAward_eatPorkChop, L"eatPorkChop", 0, 0, Tile::treeTrunk, (Achievement *) buildSword))->postConstruct(); +#endif + Achievements::play100Days = (new Achievement(eAward_play100Days, L"play100Days", 0, 0, Tile::treeTrunk, (Achievement *) buildSword))->setAwardLocallyOnly()->postConstruct(); + Achievements::arrowKillCreeper = (new Achievement(eAward_arrowKillCreeper, L"arrowKillCreeper", 0, 0, Tile::treeTrunk, (Achievement *) buildSword))->postConstruct(); + Achievements::socialPost = (new Achievement(eAward_socialPost, L"socialPost", 0, 0, Tile::treeTrunk, (Achievement *) buildSword))->postConstruct(); + +#ifndef _XBOX +// WARNING: NO NEW ACHIEVMENTS CAN BE ADDED HERE +// These stats (achievements) are directly followed by new stats/achievements in the profile data, so cannot be changed without migrating the profile data + + // 4J Stu - All new Java achievements removed to stop them using the profile data + + // 4J Stu - This achievment added in 1.8.2, but does not map to any Xbox achievements + Achievements::snipeSkeleton = (new Achievement(eAward_snipeSkeleton, L"snipeSkeleton", 7, 0, Item::bow, (Achievement *) killEnemy))->setGolden()->postConstruct(); + + // 4J Stu - These added in 1.0.1, but do not map to any Xbox achievements + Achievements::diamonds = (new Achievement(eAward_diamonds, L"diamonds", -1, 5, Item::diamond, (Achievement *) acquireIron) )->postConstruct(); + //Achievements::portal = (new Achievement(eAward_portal, L"portal", -1, 7, Tile::obsidian, (Achievement *)diamonds) )->postConstruct(); + Achievements::ghast = (new Achievement(eAward_ghast, L"ghast", -4, 8, Item::ghastTear, (Achievement *)ghast) )->setGolden()->postConstruct(); + Achievements::blazeRod = (new Achievement(eAward_blazeRod, L"blazeRod", 0, 9, Item::blazeRod, (Achievement *)blazeRod) )->postConstruct(); + Achievements::potion = (new Achievement(eAward_potion, L"potion", 2, 8, Item::potion, (Achievement *)potion) )->postConstruct(); + Achievements::theEnd = (new Achievement(eAward_theEnd, L"theEnd", 3, 10, Item::eyeOfEnder, (Achievement *)theEnd) )->setGolden()->postConstruct(); + Achievements::winGame = (new Achievement(eAward_winGame, L"theEnd2", 4, 13, Tile::dragonEgg, (Achievement *)winGame) )->setGolden()->postConstruct(); + Achievements::enchantments = (new Achievement(eAward_enchantments, L"enchantments", -4, 4, Tile::enchantTable, (Achievement *)enchantments) )->postConstruct(); + // Achievements::overkill = (new Achievement(eAward_overkill, L"overkill", -4, 1, Item::sword_diamond, (Achievement *)enchantments) )->setGolden()->postConstruct(); + // Achievements::bookcase = (new Achievement(eAward_bookcase, L"bookcase", -3, 6, Tile::bookshelf, (Achievement *)enchantments) )->postConstruct(); +#endif + +#ifdef _EXTENDED_ACHIEVEMENTS + Achievements::overkill = (new Achievement(eAward_overkill, L"overkill", -4,1, Item::sword_diamond, (Achievement *)enchantments) )->setGolden()->postConstruct(); + Achievements::bookcase = (new Achievement(eAward_bookcase, L"bookcase", -3,6, Tile::bookshelf, (Achievement *)enchantments) )->postConstruct(); + + Achievements::adventuringTime = (new Achievement(eAward_adventuringTime, L"adventuringTime", 0,0, Tile::bookshelf, (Achievement*) NULL) )->setAwardLocallyOnly()->postConstruct(); + Achievements::repopulation = (new Achievement(eAward_repopulation, L"repopulation", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); + //Achievements::porkChoop // // // // // // + Achievements::diamondsToYou = (new Achievement(eAward_diamondsToYou, L"diamondsToYou", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); + //Achievements::passingTheTime = (new Achievement(eAward_play100Days, L"passingTheTime", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); + //Achievements::archer = (new Achievement(eAward_arrowKillCreeper, L"archer", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); + Achievements::theHaggler = (new Achievement(eAward_theHaggler, L"theHaggler", 0,0, Tile::bookshelf, (Achievement*) NULL) )->setAwardLocallyOnly()->postConstruct(); + Achievements::potPlanter = (new Achievement(eAward_potPlanter, L"potPlanter", 0,0, Tile::bookshelf, (Achievement*) NULL) )->setAwardLocallyOnly()->postConstruct(); + Achievements::itsASign = (new Achievement(eAward_itsASign, L"itsASign", 0,0, Tile::bookshelf, (Achievement*) NULL) )->setAwardLocallyOnly()->postConstruct(); + Achievements::ironBelly = (new Achievement(eAward_ironBelly, L"ironBelly", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); + Achievements::haveAShearfulDay = (new Achievement(eAward_haveAShearfulDay, L"haveAShearfulDay", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); + Achievements::rainbowCollection = (new Achievement(eAward_rainbowCollection, L"rainbowCollection", 0,0, Tile::bookshelf, (Achievement*) NULL) )->setAwardLocallyOnly()->postConstruct(); + Achievements::stayinFrosty = (new Achievement(eAward_stayinFrosty, L"stayingFrosty", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); + Achievements::chestfulOfCobblestone = (new Achievement(eAward_chestfulOfCobblestone, L"chestfulOfCobblestone", 0,0, Tile::bookshelf, (Achievement*) NULL) )->setAwardLocallyOnly()->postConstruct(); + Achievements::renewableEnergy = (new Achievement(eAward_renewableEnergy, L"renewableEnergy", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); + Achievements::musicToMyEars = (new Achievement(eAward_musicToMyEars, L"musicToMyEars", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); + Achievements::bodyGuard = (new Achievement(eAward_bodyGuard, L"bodyGuard", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); + Achievements::ironMan = (new Achievement(eAward_ironMan, L"ironMan", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); + Achievements::zombieDoctor = (new Achievement(eAward_zombieDoctor, L"zombieDoctor", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); + Achievements::lionTamer = (new Achievement(eAward_lionTamer, L"lionTamer", 0,0, Tile::bookshelf, (Achievement*) NULL) )->postConstruct(); +#endif + +} + +// Static { System.out.println(achievements.size() + " achievements"); } TODO + + +void Achievements::init() +{ +} + diff --git a/Minecraft.World/Achievements.h b/Minecraft.World/Achievements.h new file mode 100644 index 00000000..177c0679 --- /dev/null +++ b/Minecraft.World/Achievements.h @@ -0,0 +1,92 @@ +#pragma once +using namespace std; + + +class Achievement; + +class Achievements +{ + friend class Achievement; + +protected: + static const int ACHIEVEMENT_OFFSET; + + // maximum position of achievements (min and max) +public: + static const int ACHIEVEMENT_WIDTH_POSITION = 12; + static const int ACHIEVEMENT_HEIGHT_POSITION = 12; + + static int xMin, yMin, xMax, yMax; + + static vector *achievements; + + static Achievement *openInventory; + static Achievement *mineWood; + static Achievement *buildWorkbench; + static Achievement *buildPickaxe; + static Achievement *buildFurnace; + static Achievement *acquireIron; + static Achievement *buildHoe; + static Achievement *makeBread; + static Achievement *bakeCake; + static Achievement *buildBetterPickaxe; + static Achievement *cookFish; + static Achievement *onARail; + static Achievement *buildSword; + static Achievement *killEnemy; + static Achievement *killCow; + static Achievement *flyPig; + + // 4J-JEV: Present on the PS3. + static Achievement *snipeSkeleton; + static Achievement *diamonds; + //static Achievement *portal; //4J-JEV: Whats this? + static Achievement *ghast; + static Achievement *blazeRod; + static Achievement *potion; + static Achievement *theEnd; + static Achievement *winGame; + static Achievement *enchantments; + + // 4J : WESTY : Added new acheivements. + static Achievement *leaderOfThePack; + static Achievement *MOARTools; + static Achievement *dispenseWithThis; + static Achievement *InToTheNether; + + // 4J : WESTY : Added other awards. + static Achievement *socialPost; + static Achievement *eatPorkChop; + static Achievement *play100Days; + static Achievement *arrowKillCreeper; + static Achievement *mine100Blocks; + static Achievement *kill10Creepers; + +#ifdef _EXTENDED_ACHIEVEMENTS + static Achievement *overkill; // Old achievements; + static Achievement *bookcase; // Old achievements; + static Achievement *adventuringTime; + static Achievement *repopulation; + static Achievement *diamondsToYou; + static Achievement *passingTheTime; + static Achievement *archer; + static Achievement *theHaggler; + static Achievement *potPlanter; + static Achievement *itsASign; + static Achievement *ironBelly; + static Achievement *haveAShearfulDay; + static Achievement *rainbowCollection; + static Achievement *stayinFrosty; + static Achievement *chestfulOfCobblestone; + static Achievement *renewableEnergy; + static Achievement *musicToMyEars; + static Achievement *bodyGuard; + static Achievement *ironMan; + static Achievement *zombieDoctor; + static Achievement *lionTamer; +#endif + + static void staticCtor(); + + static void init(); +}; diff --git a/Minecraft.World/AddEntityPacket.cpp b/Minecraft.World/AddEntityPacket.cpp new file mode 100644 index 00000000..6231537d --- /dev/null +++ b/Minecraft.World/AddEntityPacket.cpp @@ -0,0 +1,111 @@ +#include "stdafx.h" +#include +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "PacketListener.h" +#include "AddEntityPacket.h" + + + +void AddEntityPacket::_init(shared_ptr e, int type, int data, int xp, int yp, int zp, int yRotp, int xRotp) +{ + id = e->entityId; + // 4J Stu - We should add entities at their "last sent" position so that the relative update packets + // put them in the correct place + x = xp;//(int) floor(e->x * 32); + y = yp;//(int) floor(e->y * 32); + z = zp;//(int) floor(e->z * 32); + yRot = yRotp; + xRot = xRotp; + this->type = type; + this->data = data; + if (data > -1) // 4J - changed "no data" value to be -1, we can have a valid entity id of 0 + { + double xd = e->xd; + double yd = e->yd; + double zd = e->zd; + double m = 3.9; + if (xd < -m) xd = -m; + if (yd < -m) yd = -m; + if (zd < -m) zd = -m; + if (xd > m) xd = m; + if (yd > m) yd = m; + if (zd > m) zd = m; + xa = (int) (xd * 8000.0); + ya = (int) (yd * 8000.0); + za = (int) (zd * 8000.0); + } +} + +AddEntityPacket::AddEntityPacket() +{ +} + +AddEntityPacket::AddEntityPacket(shared_ptr e, int type, int yRotp, int xRotp, int xp, int yp, int zp ) +{ + _init(e, type, -1, xp, yp, zp, yRotp, xRotp); // 4J - changed "no data" value to be -1, we can have a valid entity id of 0 +} + +AddEntityPacket::AddEntityPacket(shared_ptr e, int type, int data, int yRotp, int xRotp, int xp, int yp, int zp ) +{ + _init(e, type, data, xp, yp, zp, yRotp, xRotp); +} + +void AddEntityPacket::read(DataInputStream *dis) // throws IOException TODO 4J JEV add throws statement +{ + id = dis->readShort(); + type = dis->readByte(); +#ifdef _LARGE_WORLDS + x = dis->readInt(); + y = dis->readInt(); + z = dis->readInt(); +#else + x = dis->readShort(); + y = dis->readShort(); + z = dis->readShort(); +#endif + yRot = dis->readByte(); + xRot = dis->readByte(); + data = dis->readInt(); + if (data > -1) // 4J - changed "no data" value to be -1, we can have a valid entity id of 0 + { + xa = dis->readShort(); + ya = dis->readShort(); + za = dis->readShort(); + } +} + +void AddEntityPacket::write(DataOutputStream *dos) // throws IOException TODO 4J JEV add throws statement +{ + dos->writeShort(id); + dos->writeByte(type); +#ifdef _LARGE_WORLDS + dos->writeInt(x); + dos->writeInt(y); + dos->writeInt(z); +#else + dos->writeShort(x); + dos->writeShort(y); + dos->writeShort(z); +#endif + dos->writeByte(yRot); + dos->writeByte(xRot); + dos->writeInt(data); + if (data > -1) // 4J - changed "no data" value to be -1, we can have a valid entity id of 0 + { + dos->writeShort(xa); + dos->writeShort(ya); + dos->writeShort(za); + } +} + +void AddEntityPacket::handle(PacketListener *listener) +{ + listener->handleAddEntity(shared_from_this()); +} + +int AddEntityPacket::getEstimatedSize() +{ + return 11 + data > -1 ? 6 : 0; +} diff --git a/Minecraft.World/AddEntityPacket.h b/Minecraft.World/AddEntityPacket.h new file mode 100644 index 00000000..5ac7286c --- /dev/null +++ b/Minecraft.World/AddEntityPacket.h @@ -0,0 +1,58 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class AddEntityPacket : public Packet, public enable_shared_from_this +{ +public: + static const int BOAT = 1; + static const int ITEM = 2; + static const int MINECART_RIDEABLE = 10; + static const int MINECART_CHEST = 11; + static const int MINECART_FURNACE = 12; + static const int PRIMED_TNT = 50; + static const int ENDER_CRYSTAL = 51; + static const int ARROW = 60; + static const int SNOWBALL = 61; + static const int EGG = 62; + static const int FIREBALL = 63; + static const int SMALL_FIREBALL = 64; + static const int THROWN_ENDERPEARL = 65; + + static const int FALLING = 70; + static const int ITEM_FRAME = 71; + static const int EYEOFENDERSIGNAL = 72; + static const int THROWN_POTION = 73; + static const int FALLING_EGG = 74; + static const int THROWN_EXPBOTTLE = 75; + + static const int FISH_HOOK = 90; + + // 4J Added TU9 + static const int DRAGON_FIRE_BALL = 200; + + int id; + int x, y, z; + int xa, ya, za; + int type; + int data; + byte yRot,xRot; // 4J added + +private: + void _init(shared_ptr e, int type, int data, int xp, int yp, int zp, int yRotp, int xRotp ); + +public: + AddEntityPacket(); + AddEntityPacket(shared_ptr e, int type, int yRotp, int xRotp, int xp, int yp, int zp); + AddEntityPacket(shared_ptr e, int type, int data, int yRotp, int xRotp, int xp, int yp, int zp ); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new AddEntityPacket()); } + virtual int getId() { return 23; } +}; \ No newline at end of file diff --git a/Minecraft.World/AddExperienceOrbPacket.cpp b/Minecraft.World/AddExperienceOrbPacket.cpp new file mode 100644 index 00000000..a1a22844 --- /dev/null +++ b/Minecraft.World/AddExperienceOrbPacket.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "AddExperienceOrbPacket.h" + + + +AddExperienceOrbPacket::AddExperienceOrbPacket() +{ + id = 0; + x = y = z = 0; + value = 0; +} + +AddExperienceOrbPacket::AddExperienceOrbPacket(shared_ptr e) +{ + id = e->entityId; + x = Mth::floor(e->x * 32); + y = Mth::floor(e->y * 32); + z = Mth::floor(e->z * 32); + value = e->getValue(); +} + +void AddExperienceOrbPacket::read(DataInputStream *dis) +{ + id = dis->readInt(); + x = dis->readInt(); + y = dis->readInt(); + z = dis->readInt(); + value = dis->readShort(); +} + +void AddExperienceOrbPacket::write(DataOutputStream *dos) +{ + dos->writeInt(id); + dos->writeInt(x); + dos->writeInt(y); + dos->writeInt(z); + dos->writeShort(value); +} + +void AddExperienceOrbPacket::handle(PacketListener *listener) +{ + listener->handleAddExperienceOrb(shared_from_this()); +} + +int AddExperienceOrbPacket::getEstimatedSize() +{ + return 18; +} \ No newline at end of file diff --git a/Minecraft.World/AddExperienceOrbPacket.h b/Minecraft.World/AddExperienceOrbPacket.h new file mode 100644 index 00000000..59accfb3 --- /dev/null +++ b/Minecraft.World/AddExperienceOrbPacket.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Packet.h" + +class ExperienceOrb; + +class AddExperienceOrbPacket : public Packet, public enable_shared_from_this +{ +public: + int id; + int x, y, z; + int value; + + AddExperienceOrbPacket(); + AddExperienceOrbPacket(shared_ptr e); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + + static shared_ptr create() { return shared_ptr(new AddExperienceOrbPacket()); } + virtual int getId() { return 26; } +}; \ No newline at end of file diff --git a/Minecraft.World/AddGlobalEntityPacket.cpp b/Minecraft.World/AddGlobalEntityPacket.cpp new file mode 100644 index 00000000..bfb27331 --- /dev/null +++ b/Minecraft.World/AddGlobalEntityPacket.cpp @@ -0,0 +1,65 @@ +#include "stdafx.h" +#include +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.global.h" +#include "PacketListener.h" +#include "AddGlobalEntityPacket.h" + + + +const int AddGlobalEntityPacket::LIGHTNING = 1; + +AddGlobalEntityPacket::AddGlobalEntityPacket() +{ + id = -1; + x = 0; + y = 0; + x = 0; + type = 0; +} + +AddGlobalEntityPacket::AddGlobalEntityPacket(shared_ptr e) +{ + id = e->entityId; + x = Mth::floor(e->x * 32); + y = Mth::floor(e->y * 32); + z = Mth::floor(e->z * 32); + if (dynamic_pointer_cast(e) != NULL) + { + this->type = LIGHTNING; + } + else + { + this->type = 0; + } +} + +void AddGlobalEntityPacket::read(DataInputStream *dis) // throws IOException +{ + id = dis->readInt(); + type = dis->readByte(); + x = dis->readInt(); + y = dis->readInt(); + z = dis->readInt(); +} + +void AddGlobalEntityPacket::write(DataOutputStream *dos) // throws IOException +{ + dos->writeInt(id); + dos->writeByte(type); + dos->writeInt(x); + dos->writeInt(y); + dos->writeInt(z); +} + +void AddGlobalEntityPacket::handle(PacketListener *listener) +{ + listener->handleAddGlobalEntity(shared_from_this()); +} + +int AddGlobalEntityPacket::getEstimatedSize() +{ + return 17; +} diff --git a/Minecraft.World/AddGlobalEntityPacket.h b/Minecraft.World/AddGlobalEntityPacket.h new file mode 100644 index 00000000..80c10db2 --- /dev/null +++ b/Minecraft.World/AddGlobalEntityPacket.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Packet.h" + +class AddGlobalEntityPacket : public Packet, public enable_shared_from_this +{ +public: + static const int LIGHTNING; + + int id; + int x, y, z; + int type; + + AddGlobalEntityPacket(); + AddGlobalEntityPacket(shared_ptr e); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new AddGlobalEntityPacket()); } + virtual int getId() { return 71; } +}; \ No newline at end of file diff --git a/Minecraft.World/AddIslandLayer.cpp b/Minecraft.World/AddIslandLayer.cpp new file mode 100644 index 00000000..21e9a96d --- /dev/null +++ b/Minecraft.World/AddIslandLayer.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" +#include "net.minecraft.world.level.biome.h" + +AddIslandLayer::AddIslandLayer(__int64 seedMixup, shared_ptrparent) : Layer(seedMixup) +{ + this->parent = parent; +} + +intArray AddIslandLayer::getArea(int xo, int yo, int w, int h) +{ + int px = xo - 1; + int py = yo - 1; + int pw = w + 2; + int ph = h + 2; + intArray p = parent->getArea(px, py, pw, ph); + + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + int n1 = p[(x + 0) + (y + 0) * pw]; + int n2 = p[(x + 2) + (y + 0) * pw]; + int n3 = p[(x + 0) + (y + 2) * pw]; + int n4 = p[(x + 2) + (y + 2) * pw]; + int c = p[(x + 1) + (y + 1) * pw]; + initRandom(x + xo, y + yo); + if (c == 0 && (n1 != 0 || n2 != 0 || n3 != 0 || n4 != 0)) + { + int odds = 1; + int swap = 1; + if (n1 != 0 && nextRandom(odds++) == 0) swap = n1; + if (n2 != 0 && nextRandom(odds++) == 0) swap = n2; + if (n3 != 0 && nextRandom(odds++) == 0) swap = n3; + if (n4 != 0 && nextRandom(odds++) == 0) swap = n4; + if (nextRandom(3) == 0) + { + result[x + y * w] = swap; + } + else + { + if (swap == Biome::iceFlats->id) result[x + y * w] = Biome::frozenOcean->id; + else result[x + y * w] = 0; + } + } + else if (c > 0 && (n1 == 0 || n2 == 0 || n3 == 0 || n4 == 0)) + { + if (nextRandom(5) == 0) + { + if (c == Biome::iceFlats->id) result[x + y * w] = Biome::frozenOcean->id; + else result[x + y * w] = 0; + } + else result[x + y * w] = c; + } + else + { + result[x + y * w] = c; + } + } + } + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/AddIslandLayer.h b/Minecraft.World/AddIslandLayer.h new file mode 100644 index 00000000..e7c9384b --- /dev/null +++ b/Minecraft.World/AddIslandLayer.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Layer.h" + +class AddIslandLayer : public Layer +{ +public: + AddIslandLayer(__int64 seedMixup, shared_ptrparent); + + intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/AddMobPacket.cpp b/Minecraft.World/AddMobPacket.cpp new file mode 100644 index 00000000..7e744c04 --- /dev/null +++ b/Minecraft.World/AddMobPacket.cpp @@ -0,0 +1,140 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "PacketListener.h" +#include "AddMobPacket.h" + + + +AddMobPacket::AddMobPacket() +{ + id = -1; + type = 0; + x = 0; + y = 0; + z = 0; + yRot = 0; + xRot = 0; + entityData = nullptr; + unpack = NULL; +} + +AddMobPacket::~AddMobPacket() +{ + delete unpack; +} + +AddMobPacket::AddMobPacket(shared_ptr mob, int yRotp, int xRotp, int xp, int yp, int zp, int yHeadRotp) +{ + id = mob->entityId; + + type = (byte) EntityIO::getId(mob); + // 4J Stu - We should add entities at their "last sent" position so that the relative update packets + // put them in the correct place + x = xp;//Mth::floor(mob->x * 32); + y = yp;//Mth::floor(mob->y * 32); + z = zp;//Mth::floor(mob->z * 32); + // 4J - changed - send current "previously sent" value of rotations to put this in sync with other clients + yRot = yRotp; + xRot = xRotp; + yHeadRot = yHeadRotp; + // yRot = (byte) (mob->yRot * 256 / 360); + // xRot = (byte) (mob->xRot * 256 / 360); + // yHeadRot = (byte) (mob->yHeadRot * 256 / 360); + + // From SetEntityMotionpacket + double m = 3.9; + double xd = mob->xd; + double yd = mob->yd; + double zd = mob->zd; + if (xd < -m) xd = -m; + if (yd < -m) yd = -m; + if (zd < -m) zd = -m; + if (xd > m) xd = m; + if (yd > m) yd = m; + if (zd > m) zd = m; + this->xd = (int) (xd * 8000.0); + this->yd = (int) (yd * 8000.0); + this->zd = (int) (zd * 8000.0); + + // printf("%d: New add mob rot %d\n",id,yRot); + + entityData = mob->getEntityData(); + unpack = NULL; +} + +void AddMobPacket::read(DataInputStream *dis) //throws IOException +{ + id = dis->readShort(); + type = dis->readByte() & 0xff; +#ifdef _LARGE_WORLDS + x = dis->readInt(); + y = dis->readInt(); + z = dis->readInt(); +#else + x = dis->readShort(); + y = dis->readShort(); + z = dis->readShort(); +#endif + yRot = dis->readByte(); + xRot = dis->readByte(); + yHeadRot = dis->readByte(); + xd = dis->readShort(); + yd = dis->readShort(); + zd = dis->readShort(); + MemSect(1); + unpack = SynchedEntityData::unpack(dis); + MemSect(0); +} + +void AddMobPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeShort(id); + dos->writeByte(type & 0xff); +#ifdef _LARGE_WORLDS + dos->writeInt(x); + dos->writeInt(y); + dos->writeInt(z); +#else + dos->writeShort(x); + dos->writeShort(y); + dos->writeShort(z); +#endif + dos->writeByte(yRot); + dos->writeByte(xRot); + dos->writeByte(yHeadRot); + dos->writeShort(xd); + dos->writeShort(yd); + dos->writeShort(zd); + entityData->packAll(dos); +} + +void AddMobPacket::handle(PacketListener *listener) +{ + listener->handleAddMob(shared_from_this()); +} + +int AddMobPacket::getEstimatedSize() +{ + int size = 11; + if( entityData != NULL ) + { + size += entityData->getSizeInBytes(); + } + else if( unpack != NULL ) + { + // 4J Stu - This is an incoming value which we aren't currently analysing + //size += unpack->get + } + return size; +} + +vector > *AddMobPacket::getUnpackedData() +{ + if (unpack == NULL) + { + unpack = entityData->getAll(); + } + return unpack; +} diff --git a/Minecraft.World/AddMobPacket.h b/Minecraft.World/AddMobPacket.h new file mode 100644 index 00000000..37de84c3 --- /dev/null +++ b/Minecraft.World/AddMobPacket.h @@ -0,0 +1,37 @@ +#pragma once +using namespace std; + +#include "Packet.h" +#include "SynchedEntityData.h" + +class Mob; + +class AddMobPacket : public Packet, public enable_shared_from_this +{ +public: + int id; + int type; + int x, y, z; + int xd, yd, zd; + byte yRot, xRot, yHeadRot; + +private: + shared_ptr entityData; + vector > *unpack; + +public: + AddMobPacket(); + ~AddMobPacket(); + AddMobPacket(shared_ptr mob, int yRotp, int xRotp, int xp, int yp, int zp, int yHeadRotp); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + + vector > *getUnpackedData(); + +public: + static shared_ptr create() { return shared_ptr(new AddMobPacket()); } + virtual int getId() { return 24; } +}; diff --git a/Minecraft.World/AddMushroomIslandLayer.cpp b/Minecraft.World/AddMushroomIslandLayer.cpp new file mode 100644 index 00000000..022a3b29 --- /dev/null +++ b/Minecraft.World/AddMushroomIslandLayer.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" +#include "net.minecraft.world.level.biome.h" + + +AddMushroomIslandLayer::AddMushroomIslandLayer(__int64 seedMixup, shared_ptr parent) : Layer(seedMixup) +{ + this->parent = parent; +} + +intArray AddMushroomIslandLayer::getArea(int xo, int yo, int w, int h) +{ + int px = xo - 1; + int py = yo - 1; + int pw = w + 2; + int ph = h + 2; + intArray p = parent->getArea(px, py, pw, ph); + + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + int n1 = p[(x + 0) + (y + 0) * pw]; + int n2 = p[(x + 2) + (y + 0) * pw]; + int n3 = p[(x + 0) + (y + 2) * pw]; + int n4 = p[(x + 2) + (y + 2) * pw]; + int c = p[(x + 1) + (y + 1) * pw]; + initRandom(x + xo, y + yo); + if (c == 0 && (n1 == 0 && n2 == 0 && n3 == 0 && n4 == 0) && nextRandom(100) == 0) + { + result[x + y * w] = Biome::mushroomIsland->id; + } + else + { + result[x + y * w] = c; + } + } + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/AddMushroomIslandLayer.h b/Minecraft.World/AddMushroomIslandLayer.h new file mode 100644 index 00000000..acda9fa7 --- /dev/null +++ b/Minecraft.World/AddMushroomIslandLayer.h @@ -0,0 +1,9 @@ +#pragma once +#include "Layer.h" + +class AddMushroomIslandLayer : public Layer +{ +public: + AddMushroomIslandLayer(__int64 seedMixup, shared_ptr parent); + virtual intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/AddPaintingPacket.cpp b/Minecraft.World/AddPaintingPacket.cpp new file mode 100644 index 00000000..f574cbf2 --- /dev/null +++ b/Minecraft.World/AddPaintingPacket.cpp @@ -0,0 +1,58 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "PacketListener.h" +#include "AddPaintingPacket.h" + + + +AddPaintingPacket::AddPaintingPacket() +{ + id = -1; + x = 0; + y = 0; + z = 0; + dir = 0; + motive = L""; +} + +AddPaintingPacket::AddPaintingPacket(shared_ptr e) +{ + id = e->entityId; + x = e->xTile; + y = e->yTile; + z = e->zTile; + dir = e->dir; + motive = e->motive->name; +} + +void AddPaintingPacket::read(DataInputStream *dis) //throws IOException +{ + id = dis->readInt(); + motive = readUtf(dis, Painting::Motive::MAX_MOTIVE_NAME_LENGTH); + x = dis->readInt(); + y = dis->readInt(); + z = dis->readInt(); + dir = dis->readInt(); +} + +void AddPaintingPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(id); + writeUtf(motive, dos); + dos->writeInt(x); + dos->writeInt(y); + dos->writeInt(z); + dos->writeInt(dir); +} + +void AddPaintingPacket::handle(PacketListener *listener) +{ + listener->handleAddPainting(shared_from_this()); +} + +int AddPaintingPacket::getEstimatedSize() +{ + return 24; +} diff --git a/Minecraft.World/AddPaintingPacket.h b/Minecraft.World/AddPaintingPacket.h new file mode 100644 index 00000000..a5694b74 --- /dev/null +++ b/Minecraft.World/AddPaintingPacket.h @@ -0,0 +1,27 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class Painting; + +class AddPaintingPacket : public Packet, public enable_shared_from_this +{ +public: + int id; + int x, y, z; + int dir; + wstring motive; + +public: + AddPaintingPacket(); + AddPaintingPacket(shared_ptr e); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); +public: + static shared_ptr create() { return shared_ptr(new AddPaintingPacket()); } + virtual int getId() { return 25; } +}; diff --git a/Minecraft.World/AddPlayerPacket.cpp b/Minecraft.World/AddPlayerPacket.cpp new file mode 100644 index 00000000..f3d8a32b --- /dev/null +++ b/Minecraft.World/AddPlayerPacket.cpp @@ -0,0 +1,142 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "PacketListener.h" +#include "AddPlayerPacket.h" + + + +AddPlayerPacket::AddPlayerPacket() +{ + id = -1; + name = L""; + x = 0; + y = 0; + z = 0; + yRot = 0; + xRot = 0; + carriedItem = 0; + xuid = INVALID_XUID; + m_playerIndex = 0; + m_skinId = 0; + m_capeId = 0; + m_uiGamePrivileges = 0; + entityData = nullptr; + unpack = NULL; +} + +AddPlayerPacket::~AddPlayerPacket() +{ + if(unpack != NULL) delete unpack; +} + +AddPlayerPacket::AddPlayerPacket(shared_ptr player, PlayerUID xuid, PlayerUID OnlineXuid,int xp, int yp, int zp, int yRotp, int xRotp, int yHeadRotp) +{ + id = player->entityId; + name = player->name; + + // 4J Stu - Send "previously sent" value of position as well so that we stay in sync + x = xp;//Mth::floor(player->x * 32); + y = yp;//Mth::floor(player->y * 32); + z = zp;//Mth::floor(player->z * 32); + // 4J - changed - send current "previously sent" value of rotations to put this in sync with other clients + yRot = yRotp; + xRot = xRotp; + yHeadRot = yHeadRotp; // 4J Added + // yRot = (byte) (player->yRot * 256 / 360); + // xRot = (byte) (player->xRot * 256 / 360); + + //printf("%d: New add player (%f,%f,%f) : (%d,%d,%d) : xRot %d, yRot %d\n",id,player->x,player->y,player->z,x,y,z,xRot,yRot); + + shared_ptr itemInstance = player->inventory->getSelected(); + carriedItem = itemInstance == NULL ? 0 : itemInstance->id; + + this->xuid = xuid; + this->OnlineXuid = OnlineXuid; + m_playerIndex = (BYTE)player->getPlayerIndex(); + m_skinId = player->getCustomSkin(); + m_capeId = player->getCustomCape(); + m_uiGamePrivileges = player->getAllPlayerGamePrivileges(); + + entityData = player->getEntityData(); + unpack = NULL; +} + +void AddPlayerPacket::read(DataInputStream *dis) //throws IOException +{ + id = dis->readInt(); + name = readUtf(dis, Player::MAX_NAME_LENGTH); + x = dis->readInt(); + y = dis->readInt(); + z = dis->readInt(); + yRot = dis->readByte(); + xRot = dis->readByte(); + yHeadRot = dis->readByte(); // 4J Added + carriedItem = dis->readShort(); + xuid = dis->readPlayerUID(); + OnlineXuid = dis->readPlayerUID(); + m_playerIndex = dis->readByte(); + INT skinId = dis->readInt(); + m_skinId = *(DWORD *)&skinId; + INT capeId = dis->readInt(); + m_capeId = *(DWORD *)&capeId; + INT privileges = dis->readInt(); + m_uiGamePrivileges = *(unsigned int *)&privileges; + MemSect(1); + unpack = SynchedEntityData::unpack(dis); + MemSect(0); +} + +void AddPlayerPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(id); + writeUtf(name, dos); + dos->writeInt(x); + dos->writeInt(y); + dos->writeInt(z); + dos->writeByte(yRot); + dos->writeByte(xRot); + dos->writeByte(yHeadRot); // 4J Added + dos->writeShort(carriedItem); + dos->writePlayerUID(xuid); + dos->writePlayerUID(OnlineXuid); + dos->writeByte(m_playerIndex); + dos->writeInt(m_skinId); + dos->writeInt(m_capeId); + dos->writeInt(m_uiGamePrivileges); + entityData->packAll(dos); + +} + +void AddPlayerPacket::handle(PacketListener *listener) +{ + listener->handleAddPlayer(shared_from_this()); +} + +int AddPlayerPacket::getEstimatedSize() +{ + int iSize= sizeof(int) + Player::MAX_NAME_LENGTH + sizeof(int) + sizeof(int) + sizeof(int) + sizeof(BYTE) + sizeof(BYTE) +sizeof(short) + sizeof(PlayerUID) + sizeof(PlayerUID) + sizeof(int) + sizeof(BYTE) + sizeof(unsigned int) + sizeof(byte); + + if( entityData != NULL ) + { + iSize += entityData->getSizeInBytes(); + } + else if( unpack != NULL ) + { + // 4J Stu - This is an incoming value which we aren't currently analysing + //iSize += unpack->get + } + + return iSize; +} + +vector > *AddPlayerPacket::getUnpackedData() +{ + if (unpack == NULL) + { + unpack = entityData->getAll(); + } + return unpack; +} diff --git a/Minecraft.World/AddPlayerPacket.h b/Minecraft.World/AddPlayerPacket.h new file mode 100644 index 00000000..30f6bcdd --- /dev/null +++ b/Minecraft.World/AddPlayerPacket.h @@ -0,0 +1,43 @@ +#pragma once +using namespace std; + +#include "Packet.h" +#include "SynchedEntityData.h" + +class Player; + +class AddPlayerPacket : public Packet, public enable_shared_from_this +{ + +private: + shared_ptr entityData; + vector > *unpack; + +public: + int id; + wstring name; + int x, y, z; + char yRot, xRot; + int carriedItem; + PlayerUID xuid; // 4J Added + PlayerUID OnlineXuid; // 4J Added + BYTE m_playerIndex; // 4J Added + DWORD m_skinId; // 4J Added + DWORD m_capeId; // 4J Added + unsigned int m_uiGamePrivileges; // 4J Added + byte yHeadRot; // 4J Added + + AddPlayerPacket(); + ~AddPlayerPacket(); + AddPlayerPacket(shared_ptr player, PlayerUID xuid, PlayerUID OnlineXuid,int xp, int yp, int zp, int yRotp, int xRotp, int yHeadRotp); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + + vector > *getUnpackedData(); +public: + static shared_ptr create() { return shared_ptr(new AddPlayerPacket()); } + virtual int getId() { return 20; } +}; diff --git a/Minecraft.World/AddSnowLayer.cpp b/Minecraft.World/AddSnowLayer.cpp new file mode 100644 index 00000000..d8a33cc9 --- /dev/null +++ b/Minecraft.World/AddSnowLayer.cpp @@ -0,0 +1,39 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" +#include "net.minecraft.world.level.biome.h" + +AddSnowLayer::AddSnowLayer(__int64 seedMixup, shared_ptr parent) : Layer(seedMixup) +{ + this->parent = parent; +} + +intArray AddSnowLayer::getArea(int xo, int yo, int w, int h) +{ + int px = xo - 1; + int py = yo - 1; + int pw = w + 2; + int ph = h + 2; + intArray p = parent->getArea(px, py, pw, ph); + + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + int c = p[(x + 1) + (y + 1) * pw]; + initRandom(x + xo, y + yo); + if (c == 0) + { + result[x + y * w] = 0; + } + else + { + int r = nextRandom(5); + if (r == 0) r = Biome::iceFlats->id; + else r = 1; + result[x + y * w] = r; + } + } + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/AddSnowLayer.h b/Minecraft.World/AddSnowLayer.h new file mode 100644 index 00000000..ede7a226 --- /dev/null +++ b/Minecraft.World/AddSnowLayer.h @@ -0,0 +1,9 @@ +#pragma once +#include "Layer.h" + +class AddSnowLayer : public Layer +{ +public: + AddSnowLayer(__int64 seedMixup, shared_ptr parent); + virtual intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/AdminLogCommand.h b/Minecraft.World/AdminLogCommand.h new file mode 100644 index 00000000..26299c56 --- /dev/null +++ b/Minecraft.World/AdminLogCommand.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ChatPacket.h" + +class CommandSender; + +class AdminLogCommand +{ +public: + static const int LOGTYPE_DONT_SHOW_TO_SELF = 1; + + virtual void logAdminCommand(shared_ptr source, int type, ChatPacket::EChatPacketMessage messageType, const wstring& message = L"", int customData = -1, const wstring& additionalMessage = L"") = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/AgableMob.cpp b/Minecraft.World/AgableMob.cpp new file mode 100644 index 00000000..39bcadbe --- /dev/null +++ b/Minecraft.World/AgableMob.cpp @@ -0,0 +1,131 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "SynchedEntityData.h" +#include "GenericStats.h" +#include "AgableMob.h" + +AgableMob::AgableMob(Level *level) : PathfinderMob(level) +{ + registeredBBWidth = -1; + registeredBBHeight = 0; +} + +bool AgableMob::interact(shared_ptr player) +{ + shared_ptr item = player->inventory->getSelected(); + + if (item != NULL && item->id == Item::monsterPlacer_Id) + { + if (!level->isClientSide) + { + eINSTANCEOF classToSpawn = EntityIO::getClass(item->getAuxValue()); + if (classToSpawn != eTYPE_NOTSET && (classToSpawn & eTYPE_AGABLE_MOB) == eTYPE_AGABLE_MOB && classToSpawn == GetType() ) // 4J Added GetType() check to only spawn same type + { + shared_ptr offspring = getBreedOffspring(dynamic_pointer_cast(shared_from_this())); + if (offspring != NULL) + { + offspring->setAge(-20 * 60 * 20); + offspring->moveTo(x, y, z, 0, 0); + + level->addEntity(offspring); + + if (!player->abilities.instabuild) + { + item->count--; + + if (item->count <= 0) + { + player->inventory->setItem(player->inventory->selected, nullptr); + } + } + } + } + } + } + + return PathfinderMob::interact(player); +} + +void AgableMob::defineSynchedData() +{ + PathfinderMob::defineSynchedData(); + entityData->define(DATA_AGE_ID, 0); +} + +int AgableMob::getAge() +{ + return entityData->getInteger(DATA_AGE_ID); +} + +void AgableMob::setAge(int age) +{ + entityData->set(DATA_AGE_ID, age); + updateSize(isBaby()); +} + +void AgableMob::addAdditonalSaveData(CompoundTag *tag) +{ + PathfinderMob::addAdditonalSaveData(tag); + tag->putInt(L"Age", getAge()); +} + +void AgableMob::readAdditionalSaveData(CompoundTag *tag) +{ + PathfinderMob::readAdditionalSaveData(tag); + setAge(tag->getInt(L"Age")); +} + +void AgableMob::aiStep() +{ + PathfinderMob::aiStep(); + + if(level->isClientSide) + { + updateSize(isBaby()); + } + else + { + int age = getAge(); + if (age < 0) + { + age++; + setAge(age); + } + else if (age > 0) + { + age--; + setAge(age); + } + } +} + +bool AgableMob::isBaby() +{ + return getAge() < 0; +} + +void AgableMob::updateSize(bool isBaby) +{ + internalSetSize(isBaby ? .5f : 1.0f); +} + +void AgableMob::setSize(float w, float h) +{ + bool inited = registeredBBWidth > 0; + + registeredBBWidth = w; + registeredBBHeight = h; + + if (!inited) + { + internalSetSize(1.0f); + } +} + +void AgableMob::internalSetSize(float scale) +{ + PathfinderMob::setSize(registeredBBWidth * scale, registeredBBHeight * scale); +} diff --git a/Minecraft.World/AgableMob.h b/Minecraft.World/AgableMob.h new file mode 100644 index 00000000..0020e876 --- /dev/null +++ b/Minecraft.World/AgableMob.h @@ -0,0 +1,34 @@ +#pragma once + +#include "PathfinderMob.h" + +class AgableMob : public PathfinderMob +{ +private: + static const int DATA_AGE_ID = 12; + + float registeredBBWidth; + float registeredBBHeight; + +public: + AgableMob(Level *level); + + virtual bool interact(shared_ptr player); + +protected: + virtual void defineSynchedData(); + +public: + virtual shared_ptr getBreedOffspring(shared_ptr target) = 0; + virtual int getAge(); + virtual void setAge(int age); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual void aiStep(); + virtual bool isBaby(); + virtual void updateSize(bool isBaby); + +protected: + virtual void setSize(float w, float h); + void internalSetSize(float scale); +}; \ No newline at end of file diff --git a/Minecraft.World/AirTile.cpp b/Minecraft.World/AirTile.cpp new file mode 100644 index 00000000..478dae3e --- /dev/null +++ b/Minecraft.World/AirTile.cpp @@ -0,0 +1,6 @@ +#include "stdafx.h" +#include "AirTile.h" + +AirTile::AirTile(int id) : Tile(id, Material::air) +{ +} diff --git a/Minecraft.World/AirTile.h b/Minecraft.World/AirTile.h new file mode 100644 index 00000000..4395615c --- /dev/null +++ b/Minecraft.World/AirTile.h @@ -0,0 +1,11 @@ +#pragma once +#include "Tile.h" +#include "Material.h" + +class AirTile : public Tile +{ + friend class Tile; + +protected: + AirTile(int id); +}; \ No newline at end of file diff --git a/Minecraft.World/Animal.cpp b/Minecraft.World/Animal.cpp new file mode 100644 index 00000000..45fc304f --- /dev/null +++ b/Minecraft.World/Animal.cpp @@ -0,0 +1,458 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.storage.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.damagesource.h" +#include "Random.h" +#include "Animal.h" + + + +Animal::Animal(Level *level) : AgableMob( level ) +{ +// inLove = 0; // 4J removed - now synched data + loveTime = 0; + loveCause = shared_ptr(); + + setDespawnProtected(); +} + +void Animal::defineSynchedData() +{ + AgableMob::defineSynchedData(); + + entityData->define(DATA_IN_LOVE, (int)0); // 4J added +} + +void Animal::serverAiMobStep() +{ + if (getAge() != 0) setInLoveValue(0); + AgableMob::serverAiMobStep(); +} + + +void Animal::aiStep() +{ + AgableMob::aiStep(); + + if (getAge() != 0) setInLoveValue(0); + + if (getInLoveValue() > 0) + { + setInLoveValue(getInLoveValue()-1); + if (getInLoveValue() % 10 == 0) + { + double xa = random->nextGaussian() * 0.02; + double ya = random->nextGaussian() * 0.02; + double za = random->nextGaussian() * 0.02; + level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); + } + } + else + { + loveTime = 0; + } + + updateDespawnProtectedState(); // 4J added +} + +void Animal::checkHurtTarget(shared_ptr target, float d) +{ + if (dynamic_pointer_cast(target) != NULL) + { + if (d < 3) + { + double xd = target->x - x; + double zd = target->z - z; + yRot = (float) (atan2(zd, xd) * 180 / PI) - 90; + + holdGround = true; + } + + shared_ptr p = dynamic_pointer_cast(target); + if (p->getSelectedItem() != NULL && this->isFood(p->getSelectedItem())) + { + } + else + { + attackTarget = nullptr; + } + + } + else if (dynamic_pointer_cast(target) != NULL) + { + shared_ptr a = dynamic_pointer_cast(target); + if (getAge() > 0 && a->getAge() < 0) + { + if (d < 2.5) + { + holdGround = true; + } + } + else if (getInLoveValue() > 0 && a->getInLoveValue() > 0) + { + if (a->attackTarget == NULL) a->attackTarget = shared_from_this(); + + if (a->attackTarget == shared_from_this() && d < 3.5) + { + a->setInLoveValue(a->getInLoveValue()+1); + setInLoveValue(getInLoveValue()+1); + loveTime++; + if (loveTime % 4 == 0) + { + level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, 0, 0, 0); + } + + if (loveTime == 20 * 3) breedWith(a); + } + else loveTime = 0; + } + else + { + loveTime = 0; + attackTarget = nullptr; + } + + } +} + + +void Animal::breedWith(shared_ptr target) +{ + shared_ptr offspring = getBreedOffspring(target); + + setInLoveValue(0); + loveTime = 0; + attackTarget = nullptr; + target->attackTarget = nullptr; + target->loveTime = 0; + target->setInLoveValue(0); + + // 4J - we have offspring of NULL returned when we have hit our limits of spawning any particular type of animal. In these cases try and do everything we can apart from actually + // spawning the entity. + if (offspring != NULL) + { + // Only want to set the age to this +ve value if something is actually spawned, as during this period the animal will attempt to follow offspring and ignore players. + setAge(5 * 60 * 20); + target->setAge(5 * 60 * 20); + + offspring->setAge(-20 * 60 * 20); + offspring->moveTo(x, y, z, yRot, xRot); + offspring->setDespawnProtected(); + for (int i = 0; i < 7; i++) + { + double xa = random->nextGaussian() * 0.02; + double ya = random->nextGaussian() * 0.02; + double za = random->nextGaussian() * 0.02; + level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); + } + level->addEntity(offspring); + + level->addEntity( shared_ptr( new ExperienceOrb(level, x, y, z, random->nextInt(4) + 1) ) ); + } + + setDespawnProtected(); +} + +float Animal::getWalkTargetValue(int x, int y, int z) +{ + if (level->getTile(x, y - 1, z) == Tile::grass_Id) return 10; + return level->getBrightness(x, y, z) - 0.5f; +} + +bool Animal::hurt(DamageSource *dmgSource, int dmg) +{ + if (dynamic_cast(dmgSource) != NULL) + { + shared_ptr source = dmgSource->getDirectEntity(); + + if (dynamic_pointer_cast(source) != NULL && !dynamic_pointer_cast(source)->isAllowedToAttackAnimals() ) + { + return false; + } + + if (source != NULL && source->GetType() == eTYPE_ARROW) + { + shared_ptr arrow = dynamic_pointer_cast(source); + if (dynamic_pointer_cast(arrow->owner) != NULL && ! dynamic_pointer_cast(arrow->owner)->isAllowedToAttackAnimals() ) + { + return false; + } + } + } + + fleeTime = 20 * 3; + attackTarget = nullptr; + setInLoveValue(0); + + return AgableMob::hurt(dmgSource, dmg); +} + +void Animal::addAdditonalSaveData(CompoundTag *tag) +{ + AgableMob::addAdditonalSaveData(tag); + tag->putInt(L"InLove", getInLoveValue()); +} + +void Animal::readAdditionalSaveData(CompoundTag *tag) +{ + AgableMob::readAdditionalSaveData(tag); + setInLoveValue(tag->getInt(L"InLove")); + setDespawnProtected(); +} + +shared_ptr Animal::findAttackTarget() +{ + if (fleeTime > 0) return nullptr; + + float r = 8; + if (getInLoveValue() > 0) + { + vector > *others = level->getEntitiesOfClass(typeid(*this), bb->grow(r, r, r)); + //for (int i = 0; i < others->size(); i++) + for(AUTO_VAR(it, others->begin()); it != others->end(); ++it) + { + shared_ptr p = dynamic_pointer_cast(*it); + if (p != shared_from_this() && p->getInLoveValue() > 0) + { + delete others; + return p; + } + } + delete others; + } + else + { + if (getAge() == 0) + { + vector > *players = level->getEntitiesOfClass(typeid(Player), bb->grow(r, r, r)); + //for (int i = 0; i < players.size(); i++) + for(AUTO_VAR(it, players->begin()); it != players->end(); ++it) + { + setDespawnProtected(); + + shared_ptr p = dynamic_pointer_cast(*it); + if (p->getSelectedItem() != NULL && this->isFood(p->getSelectedItem())) + { + delete players; + return p; + } + } + delete players; + } + else if (getAge() > 0) + { + vector > *others = level->getEntitiesOfClass(typeid(*this), bb->grow(r, r, r)); + //for (int i = 0; i < others.size(); i++) + for(AUTO_VAR(it, others->begin()); it != others->end(); ++it) + { + shared_ptr p = dynamic_pointer_cast(*it); + if (p != shared_from_this() && p->getAge() < 0) + { + delete others; + return p; + } + } + delete others; + } + } + return nullptr; +} + +bool Animal::canSpawn() +{ + int xt = Mth::floor(x); + int yt = Mth::floor(bb->y0); + int zt = Mth::floor(z); + return level->getTile(xt, yt - 1, zt) == Tile::grass_Id && level->getDaytimeRawBrightness(xt, yt, zt) > 8 && AgableMob::canSpawn(); +} + +int Animal::getAmbientSoundInterval() +{ + return 20 * 6; +} + +bool Animal::removeWhenFarAway() +{ + return !isDespawnProtected(); // 4J changed - was false +} + +int Animal::getExperienceReward(shared_ptr killedBy) +{ + return 1 + level->random->nextInt(3); +} + +bool Animal::isFood(shared_ptr itemInstance) +{ + return itemInstance->id == Item::wheat_Id; +} + +bool Animal::interact(shared_ptr player) +{ + shared_ptr item = player->inventory->getSelected(); + if (item != NULL && isFood(item) && getAge() == 0) + { + if (!player->abilities.instabuild) + { + item->count--; + if (item->count <= 0) + { + player->inventory->setItem(player->inventory->selected, nullptr); + } + } + + + // 4J-PB - If we can't produce another animal through breeding because of the spawn limits, display a message here + if(!level->isClientSide) + { + switch(GetType()) + { + case eTYPE_CHICKEN: + if( !level->canCreateMore(eTYPE_CHICKEN, Level::eSpawnType_Breed) ) + { + player->displayClientMessage(IDS_MAX_CHICKENS_BRED ); + return false; + } + break; + case eTYPE_WOLF: + if( !level->canCreateMore(eTYPE_WOLF, Level::eSpawnType_Breed) ) + { + player->displayClientMessage(IDS_MAX_WOLVES_BRED ); + return false; + } + break; + case eTYPE_MUSHROOMCOW: + if( !level->canCreateMore(eTYPE_MUSHROOMCOW, Level::eSpawnType_Breed) ) + { + player->displayClientMessage(IDS_MAX_MUSHROOMCOWS_BRED ); + return false; + } + break; + default: + if((GetType() & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) == eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) + { + if( !level->canCreateMore(GetType(), Level::eSpawnType_Breed) ) + { + player->displayClientMessage(IDS_MAX_PIGS_SHEEP_COWS_CATS_BRED ); + + return false; + } + } + else if( (GetType() & eTYPE_MONSTER) == eTYPE_MONSTER) + { + + } + break; + } + setInLove(player); + } + + + attackTarget = nullptr; + for (int i = 0; i < 7; i++) + { + double xa = random->nextGaussian() * 0.02; + double ya = random->nextGaussian() * 0.02; + double za = random->nextGaussian() * 0.02; + level->addParticle(eParticleType_heart, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); + } + + return true; + } + return AgableMob::interact(player); +} + +// 4J added +int Animal::getInLoveValue() +{ + return entityData->getInteger(DATA_IN_LOVE); +} + +void Animal::setInLoveValue(int value) +{ + entityData->set(DATA_IN_LOVE, value); +} + +// 4J added +void Animal::setInLove(shared_ptr player) +{ + loveCause = player; + setInLoveValue(20*30); +} + +shared_ptr Animal::getLoveCause() +{ + return loveCause.lock(); +} + +bool Animal::isInLove() +{ + return entityData->getInteger(DATA_IN_LOVE) > 0; +} + +void Animal::resetLove() { + entityData->set(DATA_IN_LOVE, 0); +} + +bool Animal::canMate(shared_ptr partner) +{ + if (partner == shared_from_this()) return false; + if (typeid(*partner) != typeid(*this)) return false; + return isInLove() && partner->isInLove(); +} + +void Animal::updateDespawnProtectedState() +{ + if( level->isClientSide ) return; + + if( m_isDespawnProtected ) + { + int xt = Mth::floor(x); + int zt = Mth::floor(z); + + if ( xt > m_maxWanderX ) m_maxWanderX = xt; + if ( xt < m_minWanderX ) m_minWanderX = xt; + if ( zt > m_maxWanderZ ) m_maxWanderZ = zt; + if ( zt < m_minWanderZ ) m_minWanderZ = zt; + + if( ( ( m_maxWanderX - m_minWanderX ) > MAX_WANDER_DISTANCE ) || + ( ( m_maxWanderZ - m_minWanderZ ) > MAX_WANDER_DISTANCE ) ) + { +// printf("Unprotecting : %d to %d, %d to %d\n", m_minWanderX, m_maxWanderX, m_minWanderZ, m_maxWanderZ ); + m_isDespawnProtected = false; + } + +/* + if( isExtraWanderingEnabled() ) + { + printf("%d: %d %d, %d\n",entityId,m_maxWanderX - m_minWanderX, m_maxWanderZ - m_minWanderZ, getWanderingQuadrant()); + } + */ + } +} + +bool Animal::isDespawnProtected() +{ + return m_isDespawnProtected; +} + +void Animal::setDespawnProtected() +{ + if( level && level->isClientSide ) return; + + int xt = Mth::floor(x); + int zt = Mth::floor(z); + + m_minWanderX = xt; + m_maxWanderX = xt; + m_minWanderZ = zt; + m_maxWanderZ = zt; + + m_isDespawnProtected = true; +} diff --git a/Minecraft.World/Animal.h b/Minecraft.World/Animal.h new file mode 100644 index 00000000..8c0cda8e --- /dev/null +++ b/Minecraft.World/Animal.h @@ -0,0 +1,80 @@ +#pragma once +#include "AgableMob.h" +#include "Creature.h" + +class Level; +class CompoundTag; +class DamageSource; + +class Animal : public AgableMob, public Creature +{ +private: + static const int DATA_IN_LOVE = 13; // 4J added + +// int inLove; // 4J removed - now synched data + int loveTime; + weak_ptr loveCause; + +public: + Animal(Level *level); + +protected: + virtual void defineSynchedData(); + virtual void serverAiMobStep(); + +public: + virtual void aiStep(); + +protected: + virtual void checkHurtTarget(shared_ptr target, float d); + +private: + virtual void breedWith(shared_ptr target); + +public: + virtual float getWalkTargetValue(int x, int y, int z); + +public: + virtual bool hurt(DamageSource *source, int dmg); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +protected: + virtual shared_ptr findAttackTarget(); + +public: + virtual bool canSpawn(); + virtual int getAmbientSoundInterval(); + +protected: + virtual bool removeWhenFarAway(); + virtual int getExperienceReward(shared_ptr killedBy); + +public: + virtual bool isFood(shared_ptr itemInstance); + virtual bool interact(shared_ptr player); + +protected: + int getInLoveValue(); // 4J added + +public: + void setInLoveValue(int value); // 4J added + void setInLove(shared_ptr player); // 4J added, then modified to match latest Java for XboxOne achievements + shared_ptr getLoveCause(); + bool isInLove(); + void resetLove(); + virtual bool canMate(shared_ptr partner); + + // 4J added for determining whether animals are enclosed or not +private: + bool m_isDespawnProtected; + + static const int MAX_WANDER_DISTANCE = 20; // Maximum distance that the entity can wander before being considered as not enclosed & therefore not protected (in tiles) + short m_minWanderX, m_maxWanderX; // Bounding x values that this has moved since last being set to protected ( in tiles ) + short m_minWanderZ, m_maxWanderZ; // Bounding z values that this has moved since last being set to protected ( in tiles ) + void updateDespawnProtectedState(); +public: + virtual bool isDespawnProtected(); + virtual void setDespawnProtected(); + +}; diff --git a/Minecraft.World/AnimatePacket.cpp b/Minecraft.World/AnimatePacket.cpp new file mode 100644 index 00000000..2ca4ed49 --- /dev/null +++ b/Minecraft.World/AnimatePacket.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "PacketListener.h" +#include "AnimatePacket.h" + + + +AnimatePacket::AnimatePacket() +{ + id = -1; + action = 0; +} + +AnimatePacket::AnimatePacket(shared_ptr e, int action) +{ + id = e->entityId; + this->action = action; +} + +void AnimatePacket::read(DataInputStream *dis) //throws IOException +{ + id = dis->readInt(); + action = dis->readByte(); +} + +void AnimatePacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(id); + dos->writeByte(action); +} + +void AnimatePacket::handle(PacketListener *listener) +{ + listener->handleAnimate(shared_from_this()); +} + +int AnimatePacket::getEstimatedSize() +{ + return 5; +} diff --git a/Minecraft.World/AnimatePacket.h b/Minecraft.World/AnimatePacket.h new file mode 100644 index 00000000..0287b9a6 --- /dev/null +++ b/Minecraft.World/AnimatePacket.h @@ -0,0 +1,31 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class AnimatePacket : public Packet, public enable_shared_from_this +{ +public: + static const int SWING = 1; + static const int HURT = 2; + static const int WAKE_UP = 3; + static const int RESPAWN = 4; + static const int EAT = 5; // 1.8.2 + static const int CRITICAL_HIT = 6; + static const int MAGIC_CRITICAL_HIT = 7; + + int id; + int action; + + AnimatePacket(); + AnimatePacket(shared_ptr e, int action); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new AnimatePacket()); } + virtual int getId() { return 18; } +}; \ No newline at end of file diff --git a/Minecraft.World/AnvilTile.cpp b/Minecraft.World/AnvilTile.cpp new file mode 100644 index 00000000..e5fd736d --- /dev/null +++ b/Minecraft.World/AnvilTile.cpp @@ -0,0 +1,117 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.h" +#include "net.minecraft.h" +#include "AnvilTile.h" + +const unsigned int AnvilTile::ANVIL_NAMES[ANVIL_NAMES_LENGTH] = { IDS_TILE_ANVIL_INTACT, + IDS_TILE_ANVIL_SLIGHTLYDAMAGED, + IDS_TILE_ANVIL_VERYDAMAGED, + }; + + +wstring AnvilTile::TEXTURE_DAMAGE_NAMES[ANVIL_NAMES_LENGTH] = { + L"anvil_top", L"anvil_top_damaged_1", L"anvil_top_damaged_2" +}; + +AnvilTile::AnvilTile(int id) : HeavyTile(id, Material::heavyMetal, isSolidRender() ) +{ + part = PART_BASE; + setLightBlock(0); + icons = NULL; +} + +bool AnvilTile::isCubeShaped() +{ + return false; +} + +bool AnvilTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +Icon *AnvilTile::getTexture(int face, int data) +{ + if (part == PART_TOP && face == Facing::UP) + { + int damage = (data >> 2) % ANVIL_NAMES_LENGTH; + return icons[damage]; + } + return icon; +} + +void AnvilTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"anvil_base"); + icons = new Icon*[ANVIL_NAMES_LENGTH]; + + for (int i = 0; i < ANVIL_NAMES_LENGTH; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURE_DAMAGE_NAMES[i]); + } +} + +void AnvilTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int dir = (Mth::floor(by->yRot * 4 / (360) + 0.5)) & 3; + int dmg = level->getData(x, y, z) >> 2; + + dir = ++dir % 4; + if (dir == 0) level->setData(x, y, z, Direction::NORTH | (dmg << 2)); + if (dir == 1) level->setData(x, y, z, Direction::EAST | (dmg << 2)); + if (dir == 2) level->setData(x, y, z, Direction::SOUTH | (dmg << 2)); + if (dir == 3) level->setData(x, y, z, Direction::WEST | (dmg << 2)); +} + +bool AnvilTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly) +{ + if (level->isClientSide) + { + return true; + } + player->startRepairing(x, y, z); + return true; +} + +int AnvilTile::getRenderShape() +{ + return SHAPE_ANVIL; +} + +int AnvilTile::getSpawnResourcesAuxValue(int data) +{ + return data >> 2; +} + +void AnvilTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) +{ + int dir = level->getData(x, y, z) & 3; + + if (dir == Direction::EAST || dir == Direction::WEST) + { + setShape(0, 0, 2 / 16.0f, 1, 1, 1 - 2 / 16.0f); + } + else + { + setShape(2 / 16.0f, 0, 0, 1 - 2 / 16.0f, 1, 1); + } +} + +void AnvilTile::falling(shared_ptr entity) +{ + entity->setHurtsEntities(true); +} + +void AnvilTile::onLand(Level *level, int xt, int yt, int zt, int data) +{ + level->levelEvent(LevelEvent::SOUND_ANVIL_LAND, xt, yt, zt, 0); +} + +bool AnvilTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/AnvilTile.h b/Minecraft.World/AnvilTile.h new file mode 100644 index 00000000..1a4f0ed7 --- /dev/null +++ b/Minecraft.World/AnvilTile.h @@ -0,0 +1,50 @@ +#pragma once + +#include "HeavyTile.h" + +class FallingTile; + +class AnvilTile : public HeavyTile +{ + friend class ChunkRebuildData; + friend class Tile; +public: + static const int PART_BASE = 0; + static const int PART_JOINT = 1; + static const int PART_COLUMN = 2; + static const int PART_TOP = 3; + + static const int ANVIL_NAMES_LENGTH = 3; + + static const unsigned int ANVIL_NAMES[ANVIL_NAMES_LENGTH]; + +private: + static wstring TEXTURE_DAMAGE_NAMES[ANVIL_NAMES_LENGTH]; + +public: + int part; + +private: + Icon **icons; + +protected: + AnvilTile(int id); + +public: + bool isCubeShaped(); + bool isSolidRender(bool isServerLevel = false); + Icon *getTexture(int face, int data); + void registerIcons(IconRegister *iconRegister); + void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); + int getRenderShape(); + int getSpawnResourcesAuxValue(int data); + void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); + +protected: + void falling(shared_ptr entity); + +public: + void onLand(Level *level, int xt, int yt, int zt, int data); + bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); +}; \ No newline at end of file diff --git a/Minecraft.World/AnvilTileItem.cpp b/Minecraft.World/AnvilTileItem.cpp new file mode 100644 index 00000000..49965925 --- /dev/null +++ b/Minecraft.World/AnvilTileItem.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.tile.h" +#include "AnvilTileItem.h" + +AnvilTileItem::AnvilTileItem(Tile *tile) : MultiTextureTileItem(tile->id - 256, tile, (int *)AnvilTile::ANVIL_NAMES, 3) +{ +} + +int AnvilTileItem::getLevelDataForAuxValue(int auxValue) +{ + return auxValue << 2; +} + +unsigned int AnvilTileItem::getDescriptionId(int iData) +{ + int damage = iData >> 2; + return MultiTextureTileItem::getDescriptionId(damage); +} diff --git a/Minecraft.World/AnvilTileItem.h b/Minecraft.World/AnvilTileItem.h new file mode 100644 index 00000000..0604f11d --- /dev/null +++ b/Minecraft.World/AnvilTileItem.h @@ -0,0 +1,13 @@ +#pragma once + +#include "MultiTextureTileItem.h" + +class AnvilTileItem : public MultiTextureTileItem +{ +public: + AnvilTileItem(Tile *tile); + + int getLevelDataForAuxValue(int auxValue); + virtual unsigned int getDescriptionId(int iData); +private: +}; diff --git a/Minecraft.World/ArmorDyeRecipe.cpp b/Minecraft.World/ArmorDyeRecipe.cpp new file mode 100644 index 00000000..3b74a8bb --- /dev/null +++ b/Minecraft.World/ArmorDyeRecipe.cpp @@ -0,0 +1,240 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.crafting.h" +#include "ArmorDyeRecipe.h" + +bool ArmorDyeRecipe::matches(shared_ptr craftSlots, Level *level) +{ + shared_ptr target = nullptr; + vector > dyes; + + for (int slot = 0; slot < craftSlots->getContainerSize(); slot++) + { + shared_ptr item = craftSlots->getItem(slot); + if (item == NULL) continue; + + ArmorItem *armor = dynamic_cast(item->getItem()); + if (armor) + { + if (armor->getMaterial() == ArmorItem::ArmorMaterial::CLOTH && target == NULL) + { + target = item; + } + else + { + return false; + } + } + else if (item->id == Item::dye_powder_Id) + { + dyes.push_back(item); + } + else + { + return false; + } + } + + return target != NULL && !dyes.empty(); +} + +shared_ptr ArmorDyeRecipe::assembleDyedArmor(shared_ptr craftSlots) +{ + shared_ptr target = nullptr; + int colorTotals[3]; + colorTotals[0] = 0; + colorTotals[1] = 0; + colorTotals[2] = 0; + int intensityTotal = 0; + int colourCounts = 0; + ArmorItem *armor = NULL; + + if(craftSlots != NULL) + { + for (int slot = 0; slot < craftSlots->getContainerSize(); slot++) + { + shared_ptr item = craftSlots->getItem(slot); + if (item == NULL) continue; + + armor = dynamic_cast(item->getItem()); + if (armor) + { + if (armor->getMaterial() == ArmorItem::ArmorMaterial::CLOTH && target == NULL) + { + target = item->copy(); + + if (armor->hasCustomColor(item)) + { + int color = armor->getColor(target); + float red = (float) ((color >> 16) & 0xFF) / 0xFF; + float green = (float) ((color >> 8) & 0xFF) / 0xFF; + float blue = (float) (color & 0xFF) / 0xFF; + + intensityTotal += max(red, max(green, blue)) * 0xFF; + + colorTotals[0] += red * 0xFF; + colorTotals[1] += green * 0xFF; + colorTotals[2] += blue * 0xFF; + colourCounts++; + } + } + else + { + return nullptr; + } + } + else if (item->id == Item::dye_powder_Id) + { + int tileData = ClothTile::getTileDataForItemAuxValue(item->getAuxValue()); + int red = (int) (Sheep::COLOR[tileData][0] * 0xFF); + int green = (int) (Sheep::COLOR[tileData][1] * 0xFF); + int blue = (int) (Sheep::COLOR[tileData][2] * 0xFF); + + intensityTotal += max(red, max(green, blue)); + + colorTotals[0] += red; + colorTotals[1] += green; + colorTotals[2] += blue; + colourCounts++; + } + else + { + return nullptr; + } + } + } + + if (armor == NULL) return nullptr; + + int red = (colorTotals[0] / colourCounts); + int green = (colorTotals[1] / colourCounts); + int blue = (colorTotals[2] / colourCounts); + + float averageIntensity = (float) intensityTotal / colourCounts; + float resultIntensity = (float) max(red, max(green, blue)); + // System.out.println(averageIntensity + ", " + resultIntensity); + + red = (int) ((float) red * averageIntensity / resultIntensity); + green = (int) ((float) green * averageIntensity / resultIntensity); + blue = (int) ((float) blue * averageIntensity / resultIntensity); + + int rgb = red; + rgb = (rgb << 8) + green; + rgb = (rgb << 8) + blue; + + armor->setColor(target, rgb); + return target; +} + +shared_ptr ArmorDyeRecipe::assemble(shared_ptr craftSlots) +{ + return ArmorDyeRecipe::assembleDyedArmor(craftSlots); +} + +int ArmorDyeRecipe::size() +{ + return 10; +} + +const ItemInstance *ArmorDyeRecipe::getResultItem() +{ + return NULL; +} + +const int ArmorDyeRecipe::getGroup() +{ + return ShapedRecipy::eGroupType_Armour; +} + +// 4J-PB +bool ArmorDyeRecipe::requires(int iRecipe) +{ + return false; +} + +void ArmorDyeRecipe::requires(INGREDIENTS_REQUIRED *pIngReq) +{ + //int iCount=0; + //bool bFound; + //int j; + INGREDIENTS_REQUIRED TempIngReq; + + // shapeless doesn't have the 3x3 shape, but we'll just use this to store the ingredients anyway + TempIngReq.iIngC=0; + TempIngReq.iType = RECIPE_TYPE_2x2; // all the dyes can be made in a 2x2 + TempIngReq.uiGridA = new unsigned int [9]; + TempIngReq.iIngIDA= new int [3*3]; + TempIngReq.iIngValA = new int [3*3]; + TempIngReq.iIngAuxValA = new int [3*3]; + + ZeroMemory(TempIngReq.iIngIDA,sizeof(int)*9); + ZeroMemory(TempIngReq.iIngValA,sizeof(int)*9); + memset(TempIngReq.iIngAuxValA,Recipes::ANY_AUX_VALUE,sizeof(int)*9); + ZeroMemory(TempIngReq.uiGridA,sizeof(unsigned int)*9); + +#if 0 + AUTO_VAR(citEnd, ingredients->end()); + + for (vector::const_iterator ingredient = ingredients->begin(); ingredient != citEnd; ingredient++) + { + ItemInstance *expected = *ingredient; + + if (expected!=NULL) + { + int iAuxVal = (*ingredient)->getAuxValue(); + TempIngReq.uiGridA[iCount++]=expected->id | iAuxVal<<24; + // 4J-PB - put the ingredients in boxes 1,2,4,5 so we can see them in a 2x2 crafting screen + if(iCount==2) iCount=3; + bFound=false; + for(j=0;jid) && (iAuxVal == Recipes::ANY_AUX_VALUE || TempIngReq.iIngAuxValA[j] == iAuxVal)) + { + bFound= true; + break; + } + } + if(bFound) + { + TempIngReq.iIngValA[j]++; + } + else + { + TempIngReq.iIngIDA[TempIngReq.iIngC]=expected->id; + TempIngReq.iIngAuxValA[TempIngReq.iIngC]=iAuxVal; + TempIngReq.iIngValA[TempIngReq.iIngC++]++; + } + } + } +#endif + + pIngReq->iIngIDA = new int [TempIngReq.iIngC]; + pIngReq->iIngValA = new int [TempIngReq.iIngC]; + pIngReq->iIngAuxValA = new int [TempIngReq.iIngC]; + pIngReq->uiGridA = new unsigned int [9]; + + pIngReq->pRecipy=this; + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + pIngReq->bCanMake[i]=false; + } + + pIngReq->iIngC=TempIngReq.iIngC; + pIngReq->iType=TempIngReq.iType; + + if(pIngReq->iIngC!=0) + { + memcpy(pIngReq->iIngIDA,TempIngReq.iIngIDA,sizeof(int)*TempIngReq.iIngC); + memcpy(pIngReq->iIngValA,TempIngReq.iIngValA,sizeof(int)*TempIngReq.iIngC); + memcpy(pIngReq->iIngAuxValA,TempIngReq.iIngAuxValA,sizeof(int)*TempIngReq.iIngC); + } + memcpy(pIngReq->uiGridA,TempIngReq.uiGridA,sizeof(unsigned int) *9); + + delete [] TempIngReq.iIngIDA; + delete [] TempIngReq.iIngValA; + delete [] TempIngReq.iIngAuxValA; + delete [] TempIngReq.uiGridA; +} diff --git a/Minecraft.World/ArmorDyeRecipe.h b/Minecraft.World/ArmorDyeRecipe.h new file mode 100644 index 00000000..b983ba7b --- /dev/null +++ b/Minecraft.World/ArmorDyeRecipe.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Recipy.h" + +class ArmorDyeRecipe : public Recipy +{ +public: + bool matches(shared_ptr craftSlots, Level *level); + + // 4J Stu - Made static as we use this in a different way from the Java (but needs to be a different name otherwise Orbis compiler complains + static shared_ptr assembleDyedArmor(shared_ptr craftSlots); + shared_ptr assemble(shared_ptr craftSlots); + + int size(); + const ItemInstance *getResultItem(); + + + virtual const int getGroup(); + + // 4J-PB + virtual bool requires(int iRecipe); + virtual void requires(INGREDIENTS_REQUIRED *pIngReq); +}; \ No newline at end of file diff --git a/Minecraft.World/ArmorItem.cpp b/Minecraft.World/ArmorItem.cpp new file mode 100644 index 00000000..0b2c24e4 --- /dev/null +++ b/Minecraft.World/ArmorItem.cpp @@ -0,0 +1,229 @@ +#include "stdafx.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.entity.player.h" +#include "com.mojang.nbt.h" +#include "ArmorItem.h" + +const int ArmorItem::healthPerSlot[] = { + 11, 16, 15, 13 +}; + +const wstring ArmorItem::LEATHER_OVERLAYS[] = { + L"helmetCloth_overlay", L"chestplateCloth_overlay", L"leggingsCloth_overlay", L"bootsCloth_overlay" + }; + +const wstring ArmorItem::TEXTURE_EMPTY_SLOTS[] = { + L"slot_empty_helmet", L"slot_empty_chestplate", L"slot_empty_leggings", L"slot_empty_boots" + }; + +typedef ArmorItem::ArmorMaterial _ArmorMaterial; + +const int _ArmorMaterial::clothArray[] = {1,3,2,1}; +const int _ArmorMaterial::chainArray[] = {2, 5, 4, 1}; +const int _ArmorMaterial::ironArray[] = {2, 6, 5, 2}; +const int _ArmorMaterial::goldArray[] = {2, 5, 3, 1}; +const int _ArmorMaterial::diamondArray[] = {3, 8, 6, 3}; +const _ArmorMaterial *_ArmorMaterial::CLOTH = new _ArmorMaterial(5, _ArmorMaterial::clothArray, 15); +const _ArmorMaterial *_ArmorMaterial::CHAIN = new _ArmorMaterial(15, _ArmorMaterial::chainArray, 12); +const _ArmorMaterial *_ArmorMaterial::IRON = new _ArmorMaterial(15, _ArmorMaterial::ironArray, 9); +const _ArmorMaterial *_ArmorMaterial::GOLD = new _ArmorMaterial(7, _ArmorMaterial::goldArray, 25); +const _ArmorMaterial *_ArmorMaterial::DIAMOND = new _ArmorMaterial(33, _ArmorMaterial::diamondArray, 10); + +_ArmorMaterial::ArmorMaterial(int durabilityMultiplier, const int slotProtections[], int enchantmentValue) +{ + this->durabilityMultiplier = durabilityMultiplier; + this->slotProtections = (int *)slotProtections; + this->enchantmentValue = enchantmentValue; +} + +_ArmorMaterial::~ArmorMaterial() +{ + delete [] slotProtections; +} + +int _ArmorMaterial::getHealthForSlot(int slot) const +{ + return healthPerSlot[slot] * durabilityMultiplier; +} + +int _ArmorMaterial::getDefenseForSlot(int slot) const +{ + return slotProtections[slot]; +} + +int _ArmorMaterial::getEnchantmentValue() const +{ + return enchantmentValue; +} + +int _ArmorMaterial::getTierItemId() const +{ + if (this == CLOTH) + { + return Item::leather_Id; + } + else if (this == CHAIN) + { + return Item::ironIngot_Id; + } + else if (this == GOLD) + { + return Item::goldIngot_Id; + } + else if (this == IRON) + { + return Item::ironIngot_Id; + } + else if (this == DIAMOND) + { + return Item::diamond_Id; + } + return 0; +} + +ArmorItem::ArmorItem(int id, const ArmorMaterial *armorType, int icon, int slot) : Item( id ), armorType( armorType ), slot( slot ), modelIndex( icon ), defense( armorType->getDefenseForSlot(slot) ) +{ + setMaxDamage(armorType->getHealthForSlot(slot)); + maxStackSize = 1; +} + +int ArmorItem::getColor(shared_ptr item, int spriteLayer) +{ + if (spriteLayer > 0) + { + return 0xFFFFFF; + } + + int color = getColor(item); + if (color < 0) color = 0xFFFFFF; + return color; +} + +//@Override +bool ArmorItem::hasMultipleSpriteLayers() +{ + return armorType == ArmorMaterial::CLOTH; +} + +int ArmorItem::getEnchantmentValue() +{ + return armorType->getEnchantmentValue(); +} + +const _ArmorMaterial *ArmorItem::getMaterial() +{ + return armorType; +} + +bool ArmorItem::hasCustomColor(shared_ptr item) +{ + if (armorType != ArmorMaterial::CLOTH) return false; + if (!item->hasTag()) return false; + if (!item->getTag()->contains(L"display")) return false; + if (!item->getTag()->getCompound(L"display")->contains(L"color")) return false; + + return true; +} + +int ArmorItem::getColor(shared_ptr item) +{ + if (armorType != ArmorMaterial::CLOTH) return -1; + + CompoundTag *tag = item->getTag(); + if (tag == NULL) return Minecraft::GetInstance()->getColourTable()->getColor( DEFAULT_LEATHER_COLOR ); + CompoundTag *display = tag->getCompound(L"display"); + if (display == NULL) return Minecraft::GetInstance()->getColourTable()->getColor( DEFAULT_LEATHER_COLOR ); + + if (display->contains(L"color")) + { + return display->getInt(L"color"); + } + else + { + return Minecraft::GetInstance()->getColourTable()->getColor( DEFAULT_LEATHER_COLOR ); + } +} + +//@Override +Icon *ArmorItem::getLayerIcon(int auxValue, int spriteLayer) +{ + if (spriteLayer == 1) + { + return overlayIcon; + } + return Item::getLayerIcon(auxValue, spriteLayer); +} + +void ArmorItem::clearColor(shared_ptr item) +{ + if (armorType != ArmorMaterial::CLOTH) return; + CompoundTag *tag = item->getTag(); + if (tag == NULL) return; + CompoundTag *display = tag->getCompound(L"display"); + if (display->contains(L"color")) display->remove(L"color"); +} + +void ArmorItem::setColor(shared_ptr item, int color) +{ + if (armorType != ArmorMaterial::CLOTH) + { +#ifndef _CONTENT_PACKAGE + printf("Can't dye non-leather!"); + __debugbreak(); +#endif + //throw new UnsupportedOperationException("Can't dye non-leather!"); + } + + CompoundTag *tag = item->getTag(); + + if (tag == NULL) + { + tag = new CompoundTag(); + item->setTag(tag); + } + + CompoundTag *display = tag->getCompound(L"display"); + if (!tag->contains(L"display")) tag->putCompound(L"display", display); + + display->putInt(L"color", color); +} + +bool ArmorItem::isValidRepairItem(shared_ptr source, shared_ptr repairItem) +{ + if (armorType->getTierItemId() == repairItem->id) + { + return true; + } + return Item::isValidRepairItem(source, repairItem); +} + +//@Override +void ArmorItem::registerIcons(IconRegister *iconRegister) +{ + Item::registerIcons(iconRegister); + + if (armorType == ArmorMaterial::CLOTH) + { + overlayIcon = iconRegister->registerIcon(LEATHER_OVERLAYS[slot]); + } + + iconEmpty = iconRegister->registerIcon(TEXTURE_EMPTY_SLOTS[slot]); +} + +Icon *ArmorItem::getEmptyIcon(int slot) +{ + switch (slot) + { + case 0: + return Item::helmet_diamond->iconEmpty; + case 1: + return Item::chestplate_diamond->iconEmpty; + case 2: + return Item::leggings_diamond->iconEmpty; + case 3: + return Item::boots_diamond->iconEmpty; + } + + return NULL; +} \ No newline at end of file diff --git a/Minecraft.World/ArmorItem.h b/Minecraft.World/ArmorItem.h new file mode 100644 index 00000000..c711e2f9 --- /dev/null +++ b/Minecraft.World/ArmorItem.h @@ -0,0 +1,90 @@ +#pragma once + +#include "Item.h" + +class ArmorItem : public Item +{ +public: + static const int SLOT_HEAD = 0; + static const int SLOT_TORSO = 1; + static const int SLOT_LEGS = 2; + static const int SLOT_FEET = 3; + static const eMinecraftColour DEFAULT_LEATHER_COLOR = eMinecraftColour_Armour_Default_Leather_Colour; + +private: + static const int healthPerSlot[]; + static const wstring LEATHER_OVERLAYS[] ; + +public: + static const wstring TEXTURE_EMPTY_SLOTS[]; + +public: + class ArmorMaterial + { + public: + static const int clothArray[]; + static const int chainArray[]; + static const int ironArray[]; + static const int goldArray[]; + static const int diamondArray[]; + public: + static const ArmorMaterial *CLOTH; + static const ArmorMaterial *CHAIN; + static const ArmorMaterial *IRON; + static const ArmorMaterial *GOLD; + static const ArmorMaterial *DIAMOND; + + private: + int durabilityMultiplier; + int *slotProtections; + int enchantmentValue; + + // 4J Stu - Had to make this public but was private + // We shouldn't be creating these except the static initialisation + public: + ArmorMaterial(int durabilityMultiplier, const int slotProtections[], int enchantmentValue); + ~ArmorMaterial(); + + public: + int getHealthForSlot(int slot) const; + int getDefenseForSlot(int slot) const; + int getEnchantmentValue() const; + int getTierItemId() const; + }; + + const int slot; + const int defense; + const int modelIndex; + +private: + const ArmorMaterial *armorType; + Icon *overlayIcon; + Icon *iconEmpty; + +public: + ArmorItem(int id, const ArmorMaterial *armorType, int icon, int slot); + + //@Override + int getColor(shared_ptr item, int spriteLayer); + + //@Override + bool hasMultipleSpriteLayers(); + + virtual int getEnchantmentValue(); + + const ArmorMaterial *getMaterial(); + bool hasCustomColor(shared_ptr item); + int getColor(shared_ptr item); + + //@Override + Icon *getLayerIcon(int auxValue, int spriteLayer); + void clearColor(shared_ptr item); + void setColor(shared_ptr item, int color); + + bool isValidRepairItem(shared_ptr source, shared_ptr repairItem); + + //@Override + void registerIcons(IconRegister *iconRegister); + + static Icon *getEmptyIcon(int slot); +}; \ No newline at end of file diff --git a/Minecraft.World/ArmorRecipes.cpp b/Minecraft.World/ArmorRecipes.cpp new file mode 100644 index 00000000..8d3483b1 --- /dev/null +++ b/Minecraft.World/ArmorRecipes.cpp @@ -0,0 +1,158 @@ +//package net.minecraft.world.item.crafting; + +//import net.minecraft.world.item.*; +//import net.minecraft.world.level.tile.Tile; +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "Tile.h" +#include "Recipy.h" +#include "Recipes.h" +#include "ArmorRecipes.h" + +// 4J-PB - adding "" on the end of these so we can detect it +wstring ArmorRecipes::shapes[][4] = +{ + {L"XXX", // + L"X X",L""},// + + {L"X X", // + L"XXX",// + L"XXX",L""},// + + {L"XXX", // + L"X X",// + L"X X",L""},// + + {L"X X",// + L"X X",L""},// +}; + +/* +ArmorRecipes::map[5] = +{ + {Item::leather, Tile::fire, Item::ironIngot, Item::diamond, Item::goldIngot}, + {Item::helmet_cloth, Item::helmet_chain, Item::helmet_iron, Item::helmet_diamond, Item::helmet_gold}, + {Item::chestplate_cloth, Item::chestplate_chain, Item::chestplate_iron, Item::chestplate_diamond, Item::chestplate_gold}, + {Item::leggings_cloth, Item::leggings_chain, Item::leggings_iron, Item::leggings_diamond, Item::leggings_gold}, + {Item::boots_cloth, Item::boots_chain, Item::boots_iron, Item::boots_diamond, Item::boots_gold}, +}; +*/ + +void ArmorRecipes::_init() +{ + map = new vector [MAX_ARMOUR_RECIPES]; + + // 4J-PB - removing the chain armour, since we show all possible recipes in the xbox game, and it's not one you can make + ADD_OBJECT(map[0],Item::leather); +// ADD_OBJECT(map[0],Tile::fire); + ADD_OBJECT(map[0],Item::ironIngot); + ADD_OBJECT(map[0],Item::diamond); + ADD_OBJECT(map[0],Item::goldIngot); + + ADD_OBJECT(map[1],Item::helmet_cloth); +// ADD_OBJECT(map[1],Item::helmet_chain); + ADD_OBJECT(map[1],Item::helmet_iron); + ADD_OBJECT(map[1],Item::helmet_diamond); + ADD_OBJECT(map[1],Item::helmet_gold); + + ADD_OBJECT(map[2],Item::chestplate_cloth); +// ADD_OBJECT(map[2],Item::chestplate_chain); + ADD_OBJECT(map[2],Item::chestplate_iron); + ADD_OBJECT(map[2],Item::chestplate_diamond); + ADD_OBJECT(map[2],Item::chestplate_gold); + + ADD_OBJECT(map[3],Item::leggings_cloth); +// ADD_OBJECT(map[3],Item::leggings_chain); + ADD_OBJECT(map[3],Item::leggings_iron); + ADD_OBJECT(map[3],Item::leggings_diamond); + ADD_OBJECT(map[3],Item::leggings_gold); + + ADD_OBJECT(map[4],Item::boots_cloth); +// ADD_OBJECT(map[4],Item::boots_chain); + ADD_OBJECT(map[4],Item::boots_iron); + ADD_OBJECT(map[4],Item::boots_diamond); + ADD_OBJECT(map[4],Item::boots_gold); +} + +// 4J-PB added for quick equip in the inventory +ArmorRecipes::_eArmorType ArmorRecipes::GetArmorType(int iId) +{ + switch(iId) + { + case Item::helmet_cloth_Id: + case Item::helmet_chain_Id: + case Item::helmet_iron_Id: + case Item::helmet_diamond_Id: + case Item::helmet_gold_Id: + return eArmorType_Helmet; + break; + + case Item::chestplate_cloth_Id: + case Item::chestplate_chain_Id: + case Item::chestplate_iron_Id: + case Item::chestplate_diamond_Id: + case Item::chestplate_gold_Id: + return eArmorType_Chestplate; + break; + + case Item::leggings_cloth_Id: + case Item::leggings_chain_Id: + case Item::leggings_iron_Id: + case Item::leggings_diamond_Id: + case Item::leggings_gold_Id: + return eArmorType_Leggings; + break; + + case Item::boots_cloth_Id: + case Item::boots_chain_Id: + case Item::boots_iron_Id: + case Item::boots_diamond_Id: + case Item::boots_gold_Id: + return eArmorType_Boots; + break; + } + + return eArmorType_None; +} + +void ArmorRecipes::addRecipes(Recipes *r) +{ + wchar_t wchTypes[5]; + wchTypes[4]=0; + + for (unsigned int m = 0; m < map[0].size(); m++) + { + Object *pObjMaterial = map[0].at(m); + + for (int t=0; titem; + + wchTypes[0]=L'w'; + wchTypes[1]=L'c'; + wchTypes[3]=L'g'; + if(pObjMaterial->GetType()==eType_TILE) + { + wchTypes[2]=L't'; + r->addShapedRecipy(new ItemInstance(target), + wchTypes, + shapes[t], + + L'X', pObjMaterial->tile, + L'A'); + } + else + { + // must be Item + wchTypes[2]=L'i'; + r->addShapedRecipy(new ItemInstance(target), + wchTypes, + shapes[t], + + L'X', pObjMaterial->item, + L'A'); + } + } + } +} + diff --git a/Minecraft.World/ArmorRecipes.h b/Minecraft.World/ArmorRecipes.h new file mode 100644 index 00000000..b6a69372 --- /dev/null +++ b/Minecraft.World/ArmorRecipes.h @@ -0,0 +1,34 @@ +//package net.minecraft.world.item.crafting; + +//import net.minecraft.world.item.*; +//import net.minecraft.world.level.tile.Tile; +#pragma once + +#define MAX_ARMOUR_RECIPES 5 +class ArmorRecipes +{ +public: + enum _eArmorType + { + eArmorType_None=0, + eArmorType_Helmet, + eArmorType_Chestplate, + eArmorType_Leggings, + eArmorType_Boots, + } + eArmorType; + + // 4J - added for common ctor code + void _init(); + ArmorRecipes() {_init();} +private: + // 4J-PB - this wasn't static in java, so might not be right + static wstring shapes[][4]; + +private: + vector *map; + +public: + void addRecipes(Recipes *r); + static _eArmorType GetArmorType(int iId); +}; diff --git a/Minecraft.World/ArmorSlot.cpp b/Minecraft.World/ArmorSlot.cpp new file mode 100644 index 00000000..1efe0b71 --- /dev/null +++ b/Minecraft.World/ArmorSlot.cpp @@ -0,0 +1,59 @@ +#include "stdafx.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.inventory.h" +#include "net.minecraft.world.item.crafting.h" +#include "ArmorSlot.h" + +ArmorSlot::ArmorSlot(int slotNum, shared_ptr container, int id, int x, int y) + : Slot( container, id, x, y ), + slotNum( slotNum ) +{ +} + +int ArmorSlot::getMaxStackSize() +{ + return 1; +} + +bool ArmorSlot::mayPlace(shared_ptr item) +{ + if ( dynamic_cast( item->getItem() ) != NULL) + { + return dynamic_cast( item->getItem() )->slot == slotNum; + } + if (item->getItem()->id == Tile::pumpkin_Id || item->getItem()->id == Item::skull_Id) + { + return slotNum == 0; + } + return false; +} + +Icon *ArmorSlot::getNoItemIcon() +{ + return ArmorItem::getEmptyIcon(slotNum); +} + +// +//bool ArmorSlot::mayCombine(shared_ptr item) +//{ +// shared_ptr thisItemI = getItem(); +// if(thisItemI == NULL || item == NULL) return false; +// +// ArmorItem *thisItem = (ArmorItem *)thisItemI->getItem(); +// bool thisIsDyableArmor = thisItem->getMaterial() == ArmorItem::ArmorMaterial::CLOTH; +// bool itemIsDye = item->id == Item::dye_powder_Id; +// return thisIsDyableArmor && itemIsDye; +//} +// +//shared_ptr ArmorSlot::combine(shared_ptr item) +//{ +// shared_ptr craftSlots = shared_ptr( new CraftingContainer(NULL, 2, 2) ); +// craftSlots->setItem(0, item); +// craftSlots->setItem(1, getItem()); // Armour item needs to go second +// shared_ptr result = ArmorDyeRecipe::assembleDyedArmor(craftSlots); +// craftSlots->setItem(0, nullptr); +// craftSlots->setItem(1, nullptr); +// return result; +//} \ No newline at end of file diff --git a/Minecraft.World/ArmorSlot.h b/Minecraft.World/ArmorSlot.h new file mode 100644 index 00000000..a9797be2 --- /dev/null +++ b/Minecraft.World/ArmorSlot.h @@ -0,0 +1,24 @@ +#pragma once +// 4J Stu Added +// In InventoryMenu.java they create an anoymous class while creating some slot. I have moved the content +// of that anonymous class to here + +#include "Slot.h" + +class Container; + +class ArmorSlot : public Slot +{ +private: + const int slotNum; + +public: + ArmorSlot(int slotNum, shared_ptr container, int id, int x, int y); + virtual ~ArmorSlot() {} + + virtual int getMaxStackSize(); + virtual bool mayPlace(shared_ptr item); + Icon *getNoItemIcon(); + //virtual bool mayCombine(shared_ptr item); // 4J Added + //virtual shared_ptr combine(shared_ptr item); // 4J Added +}; \ No newline at end of file diff --git a/Minecraft.World/ArrayWithLength.h b/Minecraft.World/ArrayWithLength.h new file mode 100644 index 00000000..4e9f9941 --- /dev/null +++ b/Minecraft.World/ArrayWithLength.h @@ -0,0 +1,115 @@ +#pragma once + +#include + +//Note - this is meant to be a really simple wrapper round a pointer just to be able to add a length value to arrays. +// As such, it shouldn't delete its data in a destructor as shallow copies will be made of this and we don't want to +// free the data just because one of those has gone out of scope +template class arrayWithLength +{ +public: + T *data; + unsigned int length; + arrayWithLength() { data = NULL; length = 0; } + arrayWithLength(unsigned int elements, bool bClearArray=true) { assert(elements!=0); data = new T[elements]; if(bClearArray){ memset( data,0,sizeof(T)*elements); } this->length = elements; } + + // 4J Stu Added this ctor so I static init arrays in the Item derivation tree + arrayWithLength( T data[], unsigned int elements) { this->data = data; this->length = elements; } + + //~arrayWithLength() { delete[] data; } + + void resize( unsigned int elements ) + { + assert( elements > length ); + T *temp = new T[elements]; + memset( temp,0,sizeof(T)*elements); + + if( data != NULL ) + { + std::copy( data, data+length, temp ); + + delete[] data; + } + data = temp; + length = elements; + } + + T& operator[](unsigned int i) { return data[i]; } + T operator[](unsigned int i) const { return data[i]; } +}; + +// TODO 4J Stu - This looks right, but is it? +template class array2DWithLength +{ + typedef arrayWithLength< T >* _parrayWithLength; +public: + _parrayWithLength *data; + unsigned int length; + array2DWithLength() { data = NULL; length = 0; } + array2DWithLength(unsigned int dimA, unsigned int dimB) + { + data = new _parrayWithLength[dimA]; + this->length = dimA; + + for( unsigned int i = 0; i < length; i++ ) + data[i] = new arrayWithLength(dimB); + } + + _parrayWithLength& operator[](unsigned int i) { return data[i]; } + _parrayWithLength operator[](unsigned int i) const { return data[i]; } +}; + + +class Biome; +class LevelChunk; +class Node; +class Item; +class Tile; +class Stat; +class MobCategory; +class File; +class Vertex; +class _Polygon; // Renaming as have conflict with Windows Polygon fn +class ServerLevel; +class MultiPlayerLevel; +class Level; +class LevelRenderer; +class WeighedRandomItem; +class WeighedTreasure; +class Layer; +//class Cube; +class ModelPart; +class Enchantment; +class ClipChunk; + +typedef arrayWithLength doubleArray; +typedef arrayWithLength byteArray; +typedef arrayWithLength charArray; +typedef arrayWithLength shortArray; +typedef arrayWithLength intArray; +typedef arrayWithLength floatArray; +typedef arrayWithLength BiomeArray; +typedef arrayWithLength LevelChunkArray; +typedef array2DWithLength LevelChunk2DArray; +typedef arrayWithLength NodeArray; +typedef arrayWithLength ItemArray; +typedef arrayWithLength TileArray; +typedef arrayWithLength StatArray; +typedef arrayWithLength MobCategoryArray; +typedef arrayWithLength FileArray; +typedef arrayWithLength VertexArray; +typedef arrayWithLength<_Polygon *> PolygonArray; +typedef arrayWithLength ServerLevelArray; +typedef arrayWithLength MultiPlayerLevelArray; +typedef arrayWithLength LevelArray; +typedef arrayWithLength LevelRendererArray; +typedef arrayWithLength WeighedRandomItemArray; +typedef arrayWithLength WeighedTreasureArray; +typedef arrayWithLength< shared_ptr > LayerArray; +//typedef arrayWithLength CubeArray; +typedef arrayWithLength ModelPartArray; +typedef arrayWithLength EnchantmentArray; +typedef arrayWithLength ClipChunkArray; + +#include "ItemInstance.h" +typedef arrayWithLength > ItemInstanceArray; diff --git a/Minecraft.World/Arrays.h b/Minecraft.World/Arrays.h new file mode 100644 index 00000000..6241a984 --- /dev/null +++ b/Minecraft.World/Arrays.h @@ -0,0 +1,23 @@ +#pragma once +using namespace std; + +#include "ArrayWithLength.h" + +class Arrays +{ +public: + static void fill(doubleArray arr, unsigned int from, unsigned int to, double value) + { assert(from >=0); assert( from <= to ); assert( to <= arr.length ); std::fill( arr.data+from, arr.data+to, value ); } + + static void fill(floatArray arr, unsigned int from, unsigned int to, float value) + { assert(from >=0); assert( from <= to ); assert( to <= arr.length ); std::fill( arr.data+from, arr.data+to, value ); } + + static void fill(BiomeArray arr, unsigned int from, unsigned int to, Biome *value) + { assert(from >=0); assert( from <= to ); assert( to <= arr.length ); std::fill( arr.data+from, arr.data+to, value ); } + + static void fill(byteArray arr, unsigned int from, unsigned int to, byte value) + { assert(from >=0); assert( from <= to ); assert( to <= arr.length ); std::fill( arr.data+from, arr.data+to, value ); } + + static void fill(byteArray arr, byte value) + { std::fill( arr.data, arr.data+arr.length, value ); } +}; diff --git a/Minecraft.World/Arrow.cpp b/Minecraft.World/Arrow.cpp new file mode 100644 index 00000000..1b102c65 --- /dev/null +++ b/Minecraft.World/Arrow.cpp @@ -0,0 +1,531 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.item.enchantment.h" +#include "com.mojang.nbt.h" +#include "Arrow.h" + +// 4J : WESTY : Added for other award, kill creeper with arrow. +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.stats.h" +#include "SoundTypes.h" + + + +// base damage, multiplied with velocity +const double Arrow::ARROW_BASE_DAMAGE = 2.0f; + +// 4J - added common ctor code. +void Arrow::_init() +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + xTile = -1; + yTile = -1; + zTile = -1; + lastTile = 0; + lastData = 0; + inGround = false; + pickup = PICKUP_DISALLOWED; + shakeTime = 0; + flightTime = 0; + + owner = nullptr; + life = 0; + + baseDamage = ARROW_BASE_DAMAGE; + knockback = 0; +} + + +Arrow::Arrow(Level *level) : Entity( level ) +{ + _init(); + + this->setSize(0.5f, 0.5f); +} + +Arrow::Arrow(Level *level, shared_ptr mob, shared_ptr target, float power, float uncertainty) : Entity( level ) +{ + _init(); + + this->owner = mob; + if ( dynamic_pointer_cast( mob ) != NULL) pickup = PICKUP_ALLOWED; + + y = mob->y + mob->getHeadHeight() - 0.1f; + + double xd = target->x - mob->x; + double yd = (target->y + target->getHeadHeight() - 0.7f) - y; + double zd = target->z - mob->z; + double sd = sqrt(xd * xd + zd * zd); + if (sd < 0.0000001) return; + + float yRot = (float) (atan2(zd, xd) * 180 / PI) - 90; + float xRot = (float) -(atan2(yd, sd) * 180 / PI); + + double xdn = xd / sd; + double zdn = zd / sd; + moveTo(mob->x + xdn, y, mob->z + zdn, yRot, xRot); + heightOffset = 0; + + float yo = (float) sd * 0.2f; + shoot(xd, yd + yo, zd, power, uncertainty); +} + +Arrow::Arrow(Level *level, double x, double y, double z) : Entity( level ) +{ + _init(); + + this->setSize(0.5f, 0.5f); + + this->setPos(x, y, z); + this->heightOffset = 0; +} + +Arrow::Arrow(Level *level, shared_ptr mob, float power) : Entity( level ) +{ + _init(); + + this->owner = mob; + if ( dynamic_pointer_cast( mob ) != NULL) pickup = PICKUP_ALLOWED; + + setSize(0.5f, 0.5f); + + this->moveTo(mob->x, mob->y + mob->getHeadHeight(), mob->z, mob->yRot, mob->xRot); + + x -= Mth::cos(yRot / 180 * PI) * 0.16f; + y -= 0.1f; + z -= Mth::sin(yRot / 180 * PI) * 0.16f; + this->setPos(x, y, z); + this->heightOffset = 0; + + xd = -Mth::sin(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI); + zd = Mth::cos(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI); + yd = -Mth::sin(xRot / 180 * PI); + + shoot(xd, yd, zd, power * 1.5f, 1); +} + + +void Arrow::defineSynchedData() +{ + entityData->define(ID_FLAGS, (byte) 0); +} + + +void Arrow::shoot(double xd, double yd, double zd, float pow, float uncertainty) +{ + float dist = (float) sqrt(xd * xd + yd * yd + zd * zd); + + xd /= dist; + yd /= dist; + zd /= dist; + + xd += (random->nextGaussian()) * 0.0075f * uncertainty; + yd += (random->nextGaussian()) * 0.0075f * uncertainty; + zd += (random->nextGaussian()) * 0.0075f * uncertainty; + + xd *= pow; + yd *= pow; + zd *= pow; + + this->xd = xd; + this->yd = yd; + this->zd = zd; + + double sd = sqrt(xd * xd + zd * zd); + + yRotO = this->yRot = (float) (atan2(xd, zd) * 180 / PI); + xRotO = this->xRot = (float) (atan2(yd, sd) * 180 / PI); + life = 0; +} + +void Arrow::lerpTo(double x, double y, double z, float yRot, float xRot, int steps) +{ + setPos(x, y, z); + setRot(yRot, xRot); +} + +void Arrow::lerpMotion(double xd, double yd, double zd) +{ + this->xd = xd; + this->yd = yd; + this->zd = zd; + if (xRotO == 0 && yRotO == 0) + { + double sd = sqrt(xd * xd + zd * zd); + yRotO = this->yRot = (float) (atan2( xd, zd) * 180 / PI); + xRotO = this->xRot = (float) (atan2( yd, sd) * 180 / PI); + xRotO = xRot; + yRotO = yRot; + app.DebugPrintf("%f %f : 0x%x\n",xRot,yRot,&yRot); + moveTo(x, y, z, yRot, xRot); + life = 0; + } +} + +void Arrow::tick() +{ + Entity::tick(); + + + if (xRotO == 0 && yRotO == 0) + { + double sd = sqrt(xd * xd + zd * zd); + yRotO = this->yRot = (float) (atan2(xd, zd) * 180 / PI); + xRotO = this->xRot = (float) (atan2(yd, sd) * 180 / PI); + } + + + { + int t = level->getTile(xTile, yTile, zTile); + if (t > 0) + { + Tile::tiles[t]->updateShape(level, xTile, yTile, zTile); + AABB *aabb = Tile::tiles[t]->getAABB(level, xTile, yTile, zTile); + if (aabb != NULL && aabb->contains(Vec3::newTemp(x, y, z))) + { + inGround = true; + } + } + + } + + if (shakeTime > 0) shakeTime--; + + if (inGround) + { + int tile = level->getTile(xTile, yTile, zTile); + int data = level->getData(xTile, yTile, zTile); + if (tile != lastTile || data != lastData) + { + inGround = false; + + xd *= random->nextFloat() * 0.2f; + yd *= random->nextFloat() * 0.2f; + zd *= random->nextFloat() * 0.2f; + life = 0; + flightTime = 0; + return; + } + + else + { + life++; + if (life == 20 * 60) remove(); + return; + } + } + + else + { + flightTime++; + } + + Vec3 *from = Vec3::newTemp(x, y, z); + Vec3 *to = Vec3::newTemp(x + xd, y + yd, z + zd); + HitResult *res = level->clip(from, to, false, true); + + from = Vec3::newTemp(x, y, z); + to = Vec3::newTemp(x + xd, y + yd, z + zd); + if (res != NULL) + { + to = Vec3::newTemp(res->pos->x, res->pos->y, res->pos->z); + } + shared_ptr hitEntity = nullptr; + vector > *objects = level->getEntities(shared_from_this(), this->bb->expand(xd, yd, zd)->grow(1, 1, 1)); + double nearest = 0; + AUTO_VAR(itEnd, objects->end()); + for (AUTO_VAR(it, objects->begin()); it != itEnd; it++) + { + shared_ptr e = *it; //objects->at(i); + if (!e->isPickable() || (e == owner && flightTime < 5)) continue; + + float rr = 0.3f; + AABB *bb = e->bb->grow(rr, rr, rr); + HitResult *p = bb->clip(from, to); + if (p != NULL) + { + double dd = from->distanceTo(p->pos); + if (dd < nearest || nearest == 0) + { + hitEntity = e; + nearest = dd; + } + delete p; + } + } + + if (hitEntity != NULL) + { + delete res; + res = new HitResult(hitEntity); + } + + if (res != NULL) + { + if (res->entity != NULL) + { + float pow = Mth::sqrt(xd * xd + yd * yd + zd * zd); + int dmg = (int) Mth::ceil((float)(pow * baseDamage)); + + if(isCritArrow()) dmg += random->nextInt(dmg / 2 + 2); + + DamageSource *damageSource = NULL; + if (owner == NULL) + { + damageSource = DamageSource::arrow(dynamic_pointer_cast(shared_from_this()), shared_from_this()); + } + else + { + damageSource = DamageSource::arrow(dynamic_pointer_cast(shared_from_this()), owner); + } + + if(res->entity->hurt(damageSource, dmg)) + { + // Firx for #67839 - Customer Encountered: Bows enchanted with "Flame" still set things on fire if pvp/attack animals is turned off + // 4J Stu - We should not set the entity on fire unless we can cause some damage (this doesn't necessarily mean that the arrow hit lowered their health) + // set targets on fire first because we want cooked + // pork/chicken/steak + if (this->isOnFire()) + { + res->entity->setOnFire(5); + } + + shared_ptr mob = dynamic_pointer_cast(res->entity); + if (mob != NULL) + { + mob->arrowCount++; + if (knockback > 0) + { + float pushLen = sqrt(xd * xd + zd * zd); + if (pushLen > 0) + { + res->entity->push(xd * knockback * .6f / pushLen, 0.1, zd * knockback * .6f / pushLen); + } + } + + if (owner != NULL) + { + ThornsEnchantment::doThornsAfterAttack(owner, mob, random); + } + } + + // 4J : WESTY : For award, need to track if creeper was killed by arrow from the player. + if ( (dynamic_pointer_cast(owner) != NULL ) && // arrow owner is a player + ( res->entity->isAlive() == false ) && // target is now dead + ( dynamic_pointer_cast( res->entity ) != NULL ) ) // target is a creeper + + { + dynamic_pointer_cast(owner)->awardStat( + GenericStats::arrowKillCreeper(), + GenericStats::param_arrowKillCreeper() + ); + } + + // 4J - sound change brought forward from 1.2.3 + level->playSound(shared_from_this(), eSoundType_RANDOM_BOW_HIT, 1.0f, 1.2f / (random->nextFloat() * 0.2f + 0.9f)); + remove(); + } + else + { + xd *= -0.1f; + yd *= -0.1f; + zd *= -0.1f; + yRot += 180; + yRotO += 180; + flightTime = 0; + } + + delete damageSource; + } + else + { + xTile = res->x; + yTile = res->y; + zTile = res->z; + lastTile = level->getTile(xTile, yTile, zTile); + lastData = level->getData(xTile, yTile, zTile); + xd = (float) (res->pos->x - x); + yd = (float) (res->pos->y - y); + zd = (float) (res->pos->z - z); + float dd = (float) sqrt(xd * xd + yd * yd + zd * zd); + // 4J added check - zero dd here was creating NaNs + if( dd > 0.0001f ) + { + x -= (xd / dd) * 0.05f; + y -= (yd / dd) * 0.05f; + z -= (zd / dd) * 0.05f; + } + + // 4J - sound change brought forward from 1.2.3 + level->playSound(shared_from_this(), eSoundType_RANDOM_BOW_HIT, 1.0f, 1.2f / (random->nextFloat() * 0.2f + 0.9f)); + inGround = true; + shakeTime = 7; + setCritArrow(false); + } + } + delete res; + + if(isCritArrow()) + { + for (int i = 0; i < 4; i++) + { + level->addParticle(eParticleType_crit, x + xd * i / 4.0f, y + yd * i / 4.0f, z + zd * i / 4.0f, -xd, -yd + 0.2, -zd); + } + } + + x += xd; + y += yd; + z += zd; + + double sd = sqrt(xd * xd + zd * zd); + yRot = (float) (atan2(xd, zd) * 180 / PI); + xRot = (float) (atan2(yd, sd) * 180 / PI); + + while (xRot - xRotO < -180) + xRotO -= 360; + while (xRot - xRotO >= 180) + xRotO += 360; + + while (yRot - yRotO < -180) + yRotO -= 360; + while (yRot - yRotO >= 180) + yRotO += 360; + + xRot = xRotO + (xRot - xRotO) * 0.2f; + yRot = yRotO + (yRot - yRotO) * 0.2f; + + + float inertia = 0.99f; + float gravity = 0.05f; + + if (isInWater()) + { + for (int i = 0; i < 4; i++) + { + float s = 1 / 4.0f; + level->addParticle(eParticleType_bubble, x - xd * s, y - yd * s, z - zd * s, xd, yd, zd); + } + inertia = 0.80f; + } + + xd *= inertia; + yd *= inertia; + zd *= inertia; + yd -= gravity; + + setPos(x, y, z); + + checkInsideTiles(); +} + +void Arrow::addAdditonalSaveData(CompoundTag *tag) +{ + tag->putShort(L"xTile", (short) xTile); + tag->putShort(L"yTile", (short) yTile); + tag->putShort(L"zTile", (short) zTile); + tag->putByte(L"inTile", (byte) lastTile); + tag->putByte(L"inData", (byte) lastData); + tag->putByte(L"shake", (byte) shakeTime); + tag->putByte(L"inGround", (byte) (inGround ? 1 : 0)); + tag->putByte(L"pickup", (byte) pickup); + tag->putDouble(L"damage", baseDamage); +} + +void Arrow::readAdditionalSaveData(CompoundTag *tag) +{ + xTile = tag->getShort(L"xTile"); + yTile = tag->getShort(L"yTile"); + zTile = tag->getShort(L"zTile"); + lastTile = tag->getByte(L"inTile") & 0xff; + lastData = tag->getByte(L"inData") & 0xff; + shakeTime = tag->getByte(L"shake") & 0xff; + inGround = tag->getByte(L"inGround") == 1; + if (tag->contains(L"damage")) + { + baseDamage = tag->getDouble(L"damage"); + } + + if (tag->contains(L"pickup")) + { + pickup = tag->getByte(L"pickup"); + } + else if (tag->contains(L"player")) + { + pickup = tag->getBoolean(L"player") ? PICKUP_ALLOWED : PICKUP_DISALLOWED; + } +} + +void Arrow::playerTouch(shared_ptr player) +{ + if (level->isClientSide || !inGround || shakeTime > 0) return; + + bool bRemove = pickup == PICKUP_ALLOWED || (pickup == PICKUP_CREATIVE_ONLY && player->abilities.instabuild); + + if (pickup == PICKUP_ALLOWED) + { + if (!player->inventory->add( shared_ptr( new ItemInstance(Item::arrow, 1) ) )) + { + bRemove = false; + } + } + + if (bRemove) + { + level->playSound(shared_from_this(), eSoundType_RANDOM_POP, 0.2f, ((random->nextFloat() - random->nextFloat()) * 0.7f + 1.0f) * 2.0f); + player->take(shared_from_this(), 1); + remove(); + } +} + +float Arrow::getShadowHeightOffs() +{ + return 0; +} + +void Arrow::setBaseDamage(double baseDamage) +{ + this->baseDamage = baseDamage; +} + +double Arrow::getBaseDamage() +{ + return baseDamage; +} + +void Arrow::setKnockback(int knockback) +{ + this->knockback = knockback; +} + +bool Arrow::isAttackable() +{ + return false; +} + +void Arrow::setCritArrow(bool critArrow) +{ + byte flags = entityData->getByte(ID_FLAGS); + if (critArrow) + { + entityData->set(ID_FLAGS, (byte) (flags | FLAG_CRIT)); + } + else + { + entityData->set(ID_FLAGS, (byte) (flags & ~FLAG_CRIT)); + } +} + +bool Arrow::isCritArrow() +{ + byte flags = entityData->getByte(ID_FLAGS); + return (flags & FLAG_CRIT) != 0; +} diff --git a/Minecraft.World/Arrow.h b/Minecraft.World/Arrow.h new file mode 100644 index 00000000..b96aa210 --- /dev/null +++ b/Minecraft.World/Arrow.h @@ -0,0 +1,78 @@ +#pragma once +using namespace std; + +#include "Entity.h" + +class Level; +class CompoundTag; + +class Arrow : public Entity +{ +public: + eINSTANCEOF GetType() { return eTYPE_ARROW; } + static Entity *create(Level *level) { return new Arrow(level); } + +private: + // base damage, multiplied with velocity + static const double ARROW_BASE_DAMAGE; + +public: + static const int PICKUP_DISALLOWED = 0; + static const int PICKUP_ALLOWED = 1; + static const int PICKUP_CREATIVE_ONLY = 2; + +private: + static const int ID_FLAGS = 16; + static const int FLAG_CRIT = 1; + +private: + int xTile; + int yTile; + int zTile; + int lastTile; + int lastData; + bool inGround; + +public: + int pickup; + int shakeTime; + shared_ptr owner; + +private: + double baseDamage; + + int knockback; + +private: + int life; + int flightTime; + + // 4J - added common ctor code. + void _init(); + +public: + Arrow(Level *level); + Arrow(Level *level, shared_ptr mob, shared_ptr target, float power, float uncertainty); + Arrow(Level *level, double x, double y, double z); + Arrow(Level *level, shared_ptr mob, float power); + +protected: + virtual void defineSynchedData(); + +public: + void shoot(double xd, double yd, double zd, float pow, float uncertainty); + virtual void lerpTo(double x, double y, double z, float yRot, float xRot, int steps); + virtual void lerpMotion(double xd, double yd, double zd); + virtual void tick(); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual void playerTouch(shared_ptr player); + virtual float getShadowHeightOffs(); + + void setBaseDamage(double baseDamage); + double getBaseDamage(); + void setKnockback(int knockback); + virtual bool isAttackable(); + void setCritArrow(bool critArrow); + bool isCritArrow(); +}; \ No newline at end of file diff --git a/Minecraft.World/ArrowAttackGoal.cpp b/Minecraft.World/ArrowAttackGoal.cpp new file mode 100644 index 00000000..23a2d2ac --- /dev/null +++ b/Minecraft.World/ArrowAttackGoal.cpp @@ -0,0 +1,91 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.sensing.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "SoundTypes.h" +#include "ArrowAttackGoal.h" + +ArrowAttackGoal::ArrowAttackGoal(Mob *mob, float speed, int projectileType, int attackInterval) +{ + // 4J Init + target = weak_ptr(); + attackTime = 0; + seeTime = 0; + + this->mob = mob; + this->level = mob->level; + this->speed = speed; + this->projectileType = projectileType; + this->attackInterval = attackInterval; + setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag); +} + +bool ArrowAttackGoal::canUse() +{ + shared_ptr bestTarget = mob->getTarget(); + if (bestTarget == NULL) return false; + target = weak_ptr(bestTarget); + return true; +} + +bool ArrowAttackGoal::canContinueToUse() +{ + return target.lock() != NULL && (canUse() || !mob->getNavigation()->isDone()); +} + +void ArrowAttackGoal::stop() +{ + target = weak_ptr(); +} + +void ArrowAttackGoal::tick() +{ + double attackRadiusSqr = 10 * 10; + shared_ptr tar = target.lock(); + double targetDistSqr = mob->distanceToSqr(tar->x, tar->bb->y0, tar->z); + bool canSee = mob->getSensing()->canSee(tar); + + if (canSee) + { + ++seeTime; + } + else seeTime = 0; + + if (targetDistSqr > attackRadiusSqr || seeTime < 20) mob->getNavigation()->moveTo(tar, speed); + else mob->getNavigation()->stop(); + + mob->getLookControl()->setLookAt(tar, 30, 30); + + attackTime = max(attackTime - 1, 0); + if (attackTime > 0) return; + if (targetDistSqr > attackRadiusSqr || !canSee) return; + fireAtTarget(); + attackTime = attackInterval; +} + +void ArrowAttackGoal::fireAtTarget() +{ + shared_ptr tar = target.lock(); + if (projectileType == ArrowType) + { + shared_ptr arrow = shared_ptr( new Arrow(level, dynamic_pointer_cast(mob->shared_from_this()), tar, 1.60f, 12) ); + level->playSound(mob->shared_from_this(), eSoundType_RANDOM_BOW, 1.0f, 1 / (mob->getRandom()->nextFloat() * 0.4f + 0.8f)); + level->addEntity(arrow); + } + else if (projectileType == SnowballType) + { + shared_ptr snowball = shared_ptr( new Snowball(level, dynamic_pointer_cast(mob->shared_from_this())) ); + double xd = tar->x - mob->x; + double yd = (tar->y + tar->getHeadHeight() - 1.1f) - snowball->y; + double zd = tar->z - mob->z; + float yo = sqrt(xd * xd + zd * zd) * 0.2f; + snowball->shoot(xd, yd + yo, zd, 1.60f, 12); + + level->playSound(mob->shared_from_this(), eSoundType_RANDOM_BOW, 1.0f, 1 / (mob->getRandom()->nextFloat() * 0.4f + 0.8f)); + level->addEntity(snowball); + } +} \ No newline at end of file diff --git a/Minecraft.World/ArrowAttackGoal.h b/Minecraft.World/ArrowAttackGoal.h new file mode 100644 index 00000000..0dccdb6f --- /dev/null +++ b/Minecraft.World/ArrowAttackGoal.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Goal.h" + +class ArrowAttackGoal : public Goal +{ +public: + static const int ArrowType = 1; + static const int SnowballType = 2; + + Level *level; + Mob *mob; // Owner of this goal + weak_ptr target; + int attackTime; + float speed; + int seeTime; + int projectileType; + int attackInterval; + + ArrowAttackGoal(Mob *mob, float speed, int projectileType, int attackInterval); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void stop(); + virtual void tick(); + +private: + void fireAtTarget(); + +public: + // 4J Added override to update ai elements when loading entity from schematics + virtual void setLevel(Level *level) { this->level = level; } +}; \ No newline at end of file diff --git a/Minecraft.World/ArrowDamageEnchantment.cpp b/Minecraft.World/ArrowDamageEnchantment.cpp new file mode 100644 index 00000000..37872264 --- /dev/null +++ b/Minecraft.World/ArrowDamageEnchantment.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "ArrowDamageEnchantment.h" + +ArrowDamageEnchantment::ArrowDamageEnchantment(int id, int frequency) : Enchantment(id, frequency, EnchantmentCategory::bow) +{ + setDescriptionId(IDS_ENCHANTMENT_ARROW_DAMAGE); +} + +int ArrowDamageEnchantment::getMinCost(int level) +{ + return 1 + (level - 1) * 10; +} + +int ArrowDamageEnchantment::getMaxCost(int level) +{ + return getMinCost(level) + 15; +} + +int ArrowDamageEnchantment::getMaxLevel() +{ + return 5; +} \ No newline at end of file diff --git a/Minecraft.World/ArrowDamageEnchantment.h b/Minecraft.World/ArrowDamageEnchantment.h new file mode 100644 index 00000000..e5f1bb57 --- /dev/null +++ b/Minecraft.World/ArrowDamageEnchantment.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Enchantment.h" + +class ArrowDamageEnchantment : public Enchantment +{ +public: + ArrowDamageEnchantment(int id, int frequency); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); +}; \ No newline at end of file diff --git a/Minecraft.World/ArrowFireEnchantment.cpp b/Minecraft.World/ArrowFireEnchantment.cpp new file mode 100644 index 00000000..27720c4f --- /dev/null +++ b/Minecraft.World/ArrowFireEnchantment.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "ArrowFireEnchantment.h" + +ArrowFireEnchantment::ArrowFireEnchantment(int id, int frequency) : Enchantment(id, frequency, EnchantmentCategory::bow) +{ + setDescriptionId(IDS_ENCHANTMENT_ARROW_FIRE); +} + +int ArrowFireEnchantment::getMinCost(int level) +{ + return 20; +} + +int ArrowFireEnchantment::getMaxCost(int level) +{ + return 50; +} + +int ArrowFireEnchantment::getMaxLevel() +{ + return 1; +} \ No newline at end of file diff --git a/Minecraft.World/ArrowFireEnchantment.h b/Minecraft.World/ArrowFireEnchantment.h new file mode 100644 index 00000000..15d5ea11 --- /dev/null +++ b/Minecraft.World/ArrowFireEnchantment.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Enchantment.h" + +class ArrowFireEnchantment : public Enchantment +{ +public: + ArrowFireEnchantment(int id, int frequency); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); +}; \ No newline at end of file diff --git a/Minecraft.World/ArrowInfiniteEnchantment.cpp b/Minecraft.World/ArrowInfiniteEnchantment.cpp new file mode 100644 index 00000000..ec840031 --- /dev/null +++ b/Minecraft.World/ArrowInfiniteEnchantment.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "ArrowInfiniteEnchantment.h" + +ArrowInfiniteEnchantment::ArrowInfiniteEnchantment(int id, int frequency) : Enchantment(id, frequency, EnchantmentCategory::bow) +{ + setDescriptionId(IDS_ENCHANTMENT_ARROW_INFINITE); +} + +int ArrowInfiniteEnchantment::getMinCost(int level) +{ + return 20; +} + +int ArrowInfiniteEnchantment::getMaxCost(int level) +{ + return 50; +} + +int ArrowInfiniteEnchantment::getMaxLevel() +{ + return 1; +} \ No newline at end of file diff --git a/Minecraft.World/ArrowInfiniteEnchantment.h b/Minecraft.World/ArrowInfiniteEnchantment.h new file mode 100644 index 00000000..906388fe --- /dev/null +++ b/Minecraft.World/ArrowInfiniteEnchantment.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Enchantment.h" + +class ArrowInfiniteEnchantment : public Enchantment +{ +public: + ArrowInfiniteEnchantment(int id, int frequency); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); +}; \ No newline at end of file diff --git a/Minecraft.World/ArrowKnockbackEnchantment.cpp b/Minecraft.World/ArrowKnockbackEnchantment.cpp new file mode 100644 index 00000000..e0164043 --- /dev/null +++ b/Minecraft.World/ArrowKnockbackEnchantment.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "ArrowKnockbackEnchantment.h" + +ArrowKnockbackEnchantment::ArrowKnockbackEnchantment(int id, int frequency) : Enchantment(id, frequency, EnchantmentCategory::bow) +{ + setDescriptionId(IDS_ENCHANTMENT_ARROW_KNOCKBACK); +} + +int ArrowKnockbackEnchantment::getMinCost(int level) +{ + return 12 + (level - 1) * 20; +} + +int ArrowKnockbackEnchantment::getMaxCost(int level) +{ + return getMinCost(level) + 25; +} + +int ArrowKnockbackEnchantment::getMaxLevel() +{ + return 2; +} \ No newline at end of file diff --git a/Minecraft.World/ArrowKnockbackEnchantment.h b/Minecraft.World/ArrowKnockbackEnchantment.h new file mode 100644 index 00000000..ee42b9c3 --- /dev/null +++ b/Minecraft.World/ArrowKnockbackEnchantment.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Enchantment.h" + +class ArrowKnockbackEnchantment : public Enchantment +{ +public: + ArrowKnockbackEnchantment(int id, int frequency); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); +}; \ No newline at end of file diff --git a/Minecraft.World/AuxDataTileItem.cpp b/Minecraft.World/AuxDataTileItem.cpp new file mode 100644 index 00000000..ffe1586e --- /dev/null +++ b/Minecraft.World/AuxDataTileItem.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.tile.h" +#include "AuxDataTileItem.h" + +AuxDataTileItem::AuxDataTileItem(int id, Tile *parentTile) : TileItem(id) +{ + this->parentTile = parentTile; + + setMaxDamage(0); + setStackedByData(true); +} + +Icon *AuxDataTileItem::getIcon(int itemAuxValue) +{ + return parentTile->getTexture(2, itemAuxValue); +} + +int AuxDataTileItem::getLevelDataForAuxValue(int auxValue) +{ + return auxValue; +} \ No newline at end of file diff --git a/Minecraft.World/AuxDataTileItem.h b/Minecraft.World/AuxDataTileItem.h new file mode 100644 index 00000000..cd264e35 --- /dev/null +++ b/Minecraft.World/AuxDataTileItem.h @@ -0,0 +1,17 @@ +#pragma once + +#include "TileItem.h" + +class Tile; + +class AuxDataTileItem : public TileItem +{ +private: + Tile *parentTile; + +public: + AuxDataTileItem(int id, Tile *parentTile); + + Icon *getIcon(int itemAuxValue); + int getLevelDataForAuxValue(int auxValue); +}; \ No newline at end of file diff --git a/Minecraft.World/AvoidPlayerGoal.cpp b/Minecraft.World/AvoidPlayerGoal.cpp new file mode 100644 index 00000000..c94d5cb1 --- /dev/null +++ b/Minecraft.World/AvoidPlayerGoal.cpp @@ -0,0 +1,85 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.sensing.h" +#include "net.minecraft.world.entity.ai.util.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.pathfinder.h" +#include "net.minecraft.world.phys.h" +#include "AvoidPlayerGoal.h" + +AvoidPlayerGoal::AvoidPlayerGoal(PathfinderMob *mob, const type_info& avoidType, float maxDist, float walkSpeed, float sprintSpeed) : avoidType(avoidType) +{ + this->mob = mob; + //this->avoidType = avoidType; + this->maxDist = maxDist; + this->walkSpeed = walkSpeed; + this->sprintSpeed = sprintSpeed; + this->pathNav = mob->getNavigation(); + setRequiredControlFlags(Control::MoveControlFlag); + + toAvoid = weak_ptr(); + path = NULL; +} + +AvoidPlayerGoal::~AvoidPlayerGoal() +{ + if(path != NULL) delete path; +} + +bool AvoidPlayerGoal::canUse() +{ + if (avoidType == typeid(Player)) + { + shared_ptr tamableAnimal = dynamic_pointer_cast(mob->shared_from_this()); + if (tamableAnimal != NULL && tamableAnimal->isTame()) return false; + toAvoid = weak_ptr(mob->level->getNearestPlayer(mob->shared_from_this(), maxDist)); + if (toAvoid.lock() == NULL) return false; + } + else + { + vector > *entities = mob->level->getEntitiesOfClass(avoidType, mob->bb->grow(maxDist, 3, maxDist)); + if (entities->empty()) + { + delete entities; + return false; + } + toAvoid = weak_ptr(entities->at(0)); + delete entities; + } + + if (!mob->getSensing()->canSee(toAvoid.lock())) return false; + + Vec3 *pos = RandomPos::getPosAvoid(dynamic_pointer_cast(mob->shared_from_this()), 16, 7, Vec3::newTemp(toAvoid.lock()->x, toAvoid.lock()->y, toAvoid.lock()->z)); + if (pos == NULL) return false; + if (toAvoid.lock()->distanceToSqr(pos->x, pos->y, pos->z) < toAvoid.lock()->distanceToSqr(mob->shared_from_this())) return false; + delete path; + path = pathNav->createPath(pos->x, pos->y, pos->z); + if (path == NULL) return false; + if (!path->endsInXZ(pos)) return false; + return true; +} + +bool AvoidPlayerGoal::canContinueToUse() +{ + return toAvoid.lock() != NULL && !pathNav->isDone(); +} + +void AvoidPlayerGoal::start() +{ + pathNav->moveTo(path, walkSpeed); + path = NULL; +} + +void AvoidPlayerGoal::stop() +{ + toAvoid = weak_ptr(); +} + +void AvoidPlayerGoal::tick() +{ + if (mob->distanceToSqr(toAvoid.lock()) < 7 * 7) mob->getNavigation()->setSpeed(sprintSpeed); + else mob->getNavigation()->setSpeed(walkSpeed); +} \ No newline at end of file diff --git a/Minecraft.World/AvoidPlayerGoal.h b/Minecraft.World/AvoidPlayerGoal.h new file mode 100644 index 00000000..0b17ee4f --- /dev/null +++ b/Minecraft.World/AvoidPlayerGoal.h @@ -0,0 +1,29 @@ +#pragma once + +#include "Goal.h" + +class PathNavigation; +class PathfinderMob; +class Path; + +class AvoidPlayerGoal : public Goal +{ +private: + PathfinderMob *mob; // Owner of this goal + float walkSpeed, sprintSpeed; + weak_ptr toAvoid; + float maxDist; + Path *path; + PathNavigation *pathNav; + const type_info& avoidType; + +public: + AvoidPlayerGoal(PathfinderMob *mob, const type_info& avoidType, float maxDist, float walkSpeed, float sprintSpeed); + ~AvoidPlayerGoal(); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + virtual void stop(); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/AwardStatPacket.cpp b/Minecraft.World/AwardStatPacket.cpp new file mode 100644 index 00000000..43328794 --- /dev/null +++ b/Minecraft.World/AwardStatPacket.cpp @@ -0,0 +1,89 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "AwardStatPacket.h" + + + +AwardStatPacket::AwardStatPacket() +{ + this->m_paramData.data = NULL; + this->m_paramData.length = 0; +} + +AwardStatPacket::AwardStatPacket(int statId, int count) +{ + this->statId = statId; + + this->m_paramData.data = (byte *) new int(count); + this->m_paramData.length = sizeof(int); +} + +AwardStatPacket::AwardStatPacket(int statId, byteArray paramData) +{ + this->statId = statId; + this->m_paramData = paramData; +} + +AwardStatPacket::~AwardStatPacket() +{ + if (m_paramData.data != NULL) + { + delete [] m_paramData.data; + m_paramData.data = NULL; + } +} + +void AwardStatPacket::handle(PacketListener *listener) +{ + listener->handleAwardStat(shared_from_this()); + m_paramData.data = NULL; +} + +void AwardStatPacket::read(DataInputStream *dis) //throws IOException +{ + statId = dis->readInt(); + + // Read parameter blob. + int length = dis->readInt(); + if(length > 0) + { + m_paramData = byteArray(length); + dis->readFully(m_paramData); + } +} + +void AwardStatPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(statId); + dos->writeInt(m_paramData.length); + if(m_paramData.length > 0) dos->write(m_paramData); +} + +int AwardStatPacket::getEstimatedSize() +{ + return 6; +} + +bool AwardStatPacket::isAync() +{ + return true; +} + +// On most platforms we only store 'count' in an AwardStatPacket. +int AwardStatPacket::getCount() +{ +#ifdef _DURANGO + assert(false); // Method not supported on Durango. + return 0; +#else + return *((int*)this->m_paramData.data); +#endif +} + +// On Durango we store 'Event' parameters here in a blob. +byteArray AwardStatPacket::getParamData() +{ + return m_paramData; +} diff --git a/Minecraft.World/AwardStatPacket.h b/Minecraft.World/AwardStatPacket.h new file mode 100644 index 00000000..6b79c023 --- /dev/null +++ b/Minecraft.World/AwardStatPacket.h @@ -0,0 +1,34 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class AwardStatPacket : public Packet, public enable_shared_from_this +{ +public: + int statId; + + // 4J-JEV: Changed to allow for Durango events. +protected: + byteArray m_paramData; + +public: + AwardStatPacket(); + AwardStatPacket(int statId, int count); + AwardStatPacket(int statId, byteArray paramData); + ~AwardStatPacket(); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + virtual bool isAync(); + + static shared_ptr create() { return shared_ptr(new AwardStatPacket()); } + virtual int getId() { return 200; } + +public: + // 4J-JEV: New getters to help prevent unsafe access + int getCount(); + byteArray getParamData(); +}; diff --git a/Minecraft.World/BasicTree.cpp b/Minecraft.World/BasicTree.cpp new file mode 100644 index 00000000..01f10cb3 --- /dev/null +++ b/Minecraft.World/BasicTree.cpp @@ -0,0 +1,565 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "BasicTree.h" + +byte BasicTree::axisConversionArray[] = { 2, 0, 0, 1, 2, 1 }; + +BasicTree::~BasicTree() +{ + delete rnd; + + for( int i = 0; i < foliageCoordsLength; i++ ) + { + delete [] foliageCoords[i]; + } + delete [] foliageCoords; +} + +BasicTree::BasicTree(bool doUpdate) : Feature(doUpdate) +{ + rnd = new Random(); + origin[0] = 0; + origin[1] = 0; + origin[2] = 0; + // Field to hold the tree height. + height = 0; + // Other important tree information. + trunkHeight = 0; + trunkHeightScale = 0.618; + branchDensity = 1.0; + branchSlope = 0.381; + widthScale = 1.0; + foliageDensity = 1.0; + trunkWidth = 1; + heightVariance = 12; + foliageHeight = 4; + foliageCoords = NULL; + foliageCoordsLength = 0; +} + +void BasicTree::prepare() +{ + // Initialize the instance variables. + // Populate the list of foliage cluster locations. + // Designed to be overridden in child classes to change basic + // tree properties (trunk width, branch angle, foliage density, etc..). + trunkHeight = (int) (height * trunkHeightScale); + if (trunkHeight >= height) trunkHeight = height - 1; + int clustersPerY = (int) (1.382 + pow(foliageDensity * height / 13.0, 2)); + if (clustersPerY < 1) clustersPerY = 1; + // The foliage coordinates are a list of [x,y,z,y of branch base] values for each cluster + int **tempFoliageCoords = new int *[clustersPerY * height]; + for( int i = 0; i < clustersPerY * height; i++ ) + { + tempFoliageCoords[i] = new int[4]; + } + int y = origin[1] + height - foliageHeight; + int clusterCount = 1; + int trunkTop = origin[1] + trunkHeight; + int relativeY = y - origin[1]; + + tempFoliageCoords[0][0] = origin[0]; + tempFoliageCoords[0][1] = y; + tempFoliageCoords[0][2] = origin[2]; + tempFoliageCoords[0][3] = trunkTop; + y--; + + while (relativeY >= 0) + { + int num = 0; + + float shapefac = treeShape(relativeY); + if (shapefac < 0) + { + y--; + relativeY--; + continue; + } + + // The originOffset is to put the value in the middle of the block. + double originOffset = 0.5; + while (num < clustersPerY) + { + double radius = widthScale * (shapefac * (rnd->nextFloat() + 0.328)); + double angle = rnd->nextFloat() * 2.0 * 3.14159; + int x = Mth::floor(radius * sin(angle) + origin[0] + originOffset); + int z = Mth::floor(radius * cos(angle) + origin[2] + originOffset); + int checkStart[] = { x, y, z }; + int checkEnd[] = { x, y + foliageHeight, z }; + // check the center column of the cluster for obstructions. + if (checkLine(checkStart, checkEnd) == -1) { + // If the cluster can be created, check the branch path + // for obstructions. + int checkBranchBase[] = { origin[0], origin[1], origin[2] }; + double distance = sqrt(pow(abs(origin[0] - checkStart[0]), 2.0) + pow(abs(origin[2] - checkStart[2]), 2.0)); + double branchHeight = distance * branchSlope; + if ((checkStart[1] - branchHeight) > trunkTop) + { + checkBranchBase[1] = trunkTop; + } + else + { + checkBranchBase[1] = (int) (checkStart[1] - branchHeight); + } + // Now check the branch path + if (checkLine(checkBranchBase, checkStart) == -1) + { + // If the branch path is clear, add the position to the list + // of foliage positions + tempFoliageCoords[clusterCount][0] = x; + tempFoliageCoords[clusterCount][1] = y; + tempFoliageCoords[clusterCount][2] = z; + tempFoliageCoords[clusterCount][3] = checkBranchBase[1]; + clusterCount++; + } + } + num++; + } + y--; + relativeY--; + } + // 4J Stu - Rather than copying the array, we are storing the number of valid elements in the array + foliageCoordsLength = clusterCount; + foliageCoords = tempFoliageCoords; + // Delete the rest of the array whilst we still know how big it was + for( int i = clusterCount; i < clustersPerY * height; i++ ) + { + delete [] tempFoliageCoords[i]; + tempFoliageCoords[i] = NULL; + } + // 4J - original code for above is the following, it isn't obvious to me why it is doing a copy of the array, so let's not for now +// foliageCoords = new int[clusterCount][4]; +// System.arraycopy(tempFoliageCoords, 0, foliageCoords, 0, clusterCount); + +} + +void BasicTree::crossection(int x, int y, int z, float radius, byte direction, int material) +{ + PIXBeginNamedEvent(0, "BasicTree crossection"); + // Create a circular cross section. + // + // Used to nearly everything in the foliage, branches, and trunk. + // This is a good target for performance optimization. + + // Passed values: + // x,y,z is the center location of the cross section + // radius is the radius of the section from the center + // direction is the direction the cross section is pointed, 0 for x, 1 for y, 2 for z + // material is the index number for the material to use + int rad = (int) (radius + 0.618); + byte secidx1 = axisConversionArray[direction]; + byte secidx2 = axisConversionArray[direction + 3]; + int center[] = { x, y, z }; + int position[] = { 0, 0, 0 }; + int offset1 = -rad; + int offset2 = -rad; + int thismat; + position[direction] = center[direction]; + while (offset1 <= rad) + { + position[secidx1] = center[secidx1] + offset1; + offset2 = -rad; + while (offset2 <= rad) + { + double thisdistance = pow(abs(offset1) + 0.5, 2) + pow(abs(offset2) + 0.5, 2); + if (thisdistance > radius * radius) + { + offset2++; + continue; + } + position[secidx2] = center[secidx2] + offset2; + PIXBeginNamedEvent(0,"BasicTree getting tile"); + thismat = thisLevel->getTile(position[0], position[1], position[2]); + PIXEndNamedEvent(); + if (!((thismat == 0) || (thismat == Tile::leaves_Id))) + { + // If the material of the checked block is anything other than + // air or foliage, skip this tile. + offset2++; + continue; + } + PIXBeginNamedEvent(0,"BasicTree placing block"); + placeBlock(thisLevel, position[0], position[1], position[2], material, 0); + PIXEndNamedEvent(); + offset2++; + } + offset1++; + } + PIXEndNamedEvent(); +} + +float BasicTree::treeShape(int y) +{ + // Take the y position relative to the base of the tree. + // Return the distance the foliage should be from the trunk axis. + // Return a negative number if foliage should not be created at this height. + // This method is intended for overriding in child classes, allowing + // different shaped trees. + // This method should return a consistent value for each y (don't randomize). + if (y < (((float) height) * 0.3)) return (float) -1.618; + float radius = ((float) height) / ((float) 2.0); + float adjacent = (((float) height) / ((float) 2.0)) - y; + float distance; + if (adjacent == 0) distance = radius; + else if (abs(adjacent) >= radius) distance = (float) 0.0; + else distance = (float) sqrt(pow(abs(radius), 2) - pow(abs(adjacent), 2)); + // Alter this factor to change the overall width of the tree. + distance *= (float) 0.5; + return distance; +} + +float BasicTree::foliageShape(int y) +{ + // Take the y position relative to the base of the foliage cluster. + // Return the radius of the cluster at this y + // Return a negative number if no foliage should be created at this level + // this method is intended for overriding in child classes, allowing + // foliage of different sizes and shapes. + if ((y < 0) || (y >= foliageHeight)) return (float) -1; + else if ((y == 0) || (y == (foliageHeight - 1))) return (float) 2; + else return (float) 3; +} + +void BasicTree::foliageCluster(int x, int y, int z) +{ + PIXBeginNamedEvent(0,"BasicTree foliageCluster"); + // Generate a cluster of foliage, with the base at x, y, z. + // The shape of the cluster is derived from foliageShape + // crossection is called to make each level. + int topy = y + foliageHeight; + int cury = topy - 1; + float radius; + // 4J Stu - Generate foliage from the top down so that we don't keep recalculating heightmaps + while (cury >= y) + { + radius = foliageShape(cury - y); + crossection(x, cury, z, radius, (byte) 1, Tile::leaves_Id); + cury--; + } + PIXEndNamedEvent(); +} + +void BasicTree::limb(int *start, int *end, int material) +{ + // Create a limb from the start position to the end position. + // Used for creating the branches and trunk. + + // Populate delta, the difference between start and end for all three axies. + // Set primidx to the index with the largest overall distance traveled. + int delta[] = { 0, 0, 0 }; + byte idx = 0; + byte primidx = 0; + while (idx < 3) + { + delta[idx] = end[idx] - start[idx]; + if (abs(delta[idx]) > abs(delta[primidx])) + { + primidx = idx; + } + idx++; + } + // If the largest distance is zero, don't bother to do anything else. + if (delta[primidx] == 0) return; + // set up the other two axis indices. + byte secidx1 = axisConversionArray[primidx]; + byte secidx2 = axisConversionArray[primidx + 3]; + // primsign is digit 1 or -1 depending on whether the limb is headed + // along the positive or negative primidx axis. + char primsign; + if (delta[primidx] > 0) primsign = 1; + else primsign = -1; + // Initilize the per-step movement for the non-primary axies. + double secfac1 = ((double) delta[secidx1]) / ((double) delta[primidx]); + double secfac2 = ((double) delta[secidx2]) / ((double) delta[primidx]); + // Initialize the coordinates. + int coordinate[] = { 0, 0, 0 }; + // Loop through each crossection along the primary axis, from start to end + int primoffset = 0; + int endoffset = delta[primidx] + primsign; + while (primoffset != endoffset) + { + coordinate[primidx] = Mth::floor(start[primidx] + primoffset + 0.5); + coordinate[secidx1] = Mth::floor(start[secidx1] + (primoffset * secfac1) + 0.5); + coordinate[secidx2] = Mth::floor(start[secidx2] + (primoffset * secfac2) + 0.5); + + int dir = TreeTile::FACING_Y; + int xdiff = abs(coordinate[0] - start[0]); + int zdiff = abs(coordinate[2] - start[2]); + int maxdiff = max(xdiff, zdiff); + + if (maxdiff > 0) + { + if (xdiff == maxdiff) + { + dir = TreeTile::FACING_X; + } + else if (zdiff == maxdiff) + { + dir = TreeTile::FACING_Z; + } + } + placeBlock(thisLevel, coordinate[0], coordinate[1], coordinate[2], material, dir); + primoffset += primsign; + } +} + +void BasicTree::makeFoliage() +{ + // Create the tree foliage. + // Call foliageCluster at the correct locations + int idx = 0; + int finish = foliageCoordsLength; + while (idx < finish) + { + int x = foliageCoords[idx][0]; + int y = foliageCoords[idx][1]; + int z = foliageCoords[idx][2]; + foliageCluster(x, y, z); + idx++; + } +} + +bool BasicTree::trimBranches(int localY) +{ + // For larger trees, randomly "prune" the branches so there + // aren't too many. + // Return true if the branch should be created. + // This method is intended for overriding in child classes, allowing + // decent amounts of branches on very large trees. + // Can also be used to disable branches on some tree types, or + // make branches more sparse. + if (localY < (height * 0.2)) return false; + else return true; +} + +void BasicTree::makeTrunk() +{ + // Create the trunk of the tree. + int x = origin[0]; + int startY = origin[1]; + int topY = origin[1] + trunkHeight; + int z = origin[2]; + int startCoord[] = { x, startY, z }; + int endCoord[] = { x, topY, z }; + limb(startCoord, endCoord, Tile::treeTrunk_Id); + if (trunkWidth == 2) + { + startCoord[0] += 1; + endCoord[0] += 1; + limb(startCoord, endCoord, Tile::treeTrunk_Id); + startCoord[2] += 1; + endCoord[2] += 1; + limb(startCoord, endCoord, Tile::treeTrunk_Id); + startCoord[0] += -1; + endCoord[0] += -1; + limb(startCoord, endCoord, Tile::treeTrunk_Id); + } +} + +void BasicTree::makeBranches() +{ + // Create the tree branches. + // Call trimBranches for each branch to see if you should create it. + // Call taperedLimb to the correct locations + int idx = 0; + int finish = foliageCoordsLength; + int baseCoord[] = { origin[0], origin[1], origin[2] }; + while (idx < finish) + { + int *coordValues = foliageCoords[idx]; + int endCoord[] = { coordValues[0], coordValues[1], coordValues[2] }; + baseCoord[1] = coordValues[3]; + int localY = baseCoord[1] - origin[1]; + if (trimBranches(localY)) + { + limb(baseCoord, endCoord, Tile::treeTrunk_Id); + } + idx++; + } +} + +int BasicTree::checkLine(int *start, int *end) +{ + // Check from coordinates start to end (both inclusive) for blocks other than air and foliage + // If a block other than air and foliage is found, return the number of steps taken. + // If no block other than air and foliage is found, return -1. + // Examples: + // If the third block searched is stone, return 2 + // If the first block searched is lava, return 0 + + int delta[] = { 0, 0, 0 }; + byte idx = 0; + byte primidx = 0; + while (idx < 3) + { + delta[idx] = end[idx] - start[idx]; + if (abs(delta[idx]) > abs(delta[primidx])) + { + primidx = idx; + } + idx++; + } + // If the largest distance is zero, don't bother to do anything else. + if (delta[primidx] == 0) return -1; + // set up the other two axis indices. + byte secidx1 = axisConversionArray[primidx]; + byte secidx2 = axisConversionArray[primidx + 3]; + // primsign is digit 1 or -1 depending on whether the limb is headed + // along the positive or negative primidx axis. + char primsign; // 4J Stu - Was byte, but we use in a sum below and byte=unsigned char so we were setting endoffset incorrectly + if (delta[primidx] > 0) primsign = 1; + else primsign = -1; + // Initilize the per-step movement for the non-primary axies. + double secfac1 = ((double) delta[secidx1]) / ((double) delta[primidx]); + double secfac2 = ((double) delta[secidx2]) / ((double) delta[primidx]); + // Initialize the coordinates. + int coordinate[] = { 0, 0, 0 }; + // Loop through each crossection along the primary axis, from start to end + int primoffset = 0; + int endoffset = delta[primidx] + primsign; + int thismat; + while (primoffset != endoffset) + { + coordinate[primidx] = start[primidx] + primoffset; + coordinate[secidx1] = Mth::floor(start[secidx1] + (primoffset * secfac1)); + coordinate[secidx2] = Mth::floor(start[secidx2] + (primoffset * secfac2)); + thismat = thisLevel->getTile(coordinate[0], coordinate[1], coordinate[2]); + if (!((thismat == 0) || (thismat == Tile::leaves_Id))) + { + // If the material of the checked block is anything other than + // air or foliage, stop looking. + break; + } + primoffset += primsign; + } + // If you reached the end without finding anything, return -1. + if (primoffset == endoffset) + { + return -1; + } + // Otherwise, return the number of steps you took. + else + { + return abs(primoffset); + } +} + +bool BasicTree::checkLocation() +{ + // Return true if the tree can be placed here. + // Return false if the tree can not be placed here. + + // Examine the square under the trunk. Is it grass or dirt? + // If not, return false + // Examine center column for how tall the tree can be. + // If the checked height is shorter than height, but taller + // than 4, set the tree to the maximum height allowed. + // If the space is too short, return false. + int startPosition[] = { origin[0], origin[1], origin[2] }; + int endPosition[] = { origin[0], origin[1] + height - 1, origin[2] }; + + // 4J Stu Added to stop tree features generating areas previously place by game rule generation + if(app.getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + bool intersects = levelGenOptions->checkIntersects(startPosition[0], startPosition[1], startPosition[2], endPosition[0], endPosition[1], endPosition[2]); + if(intersects) + { + //app.DebugPrintf("Skipping reeds feature generation as it overlaps a game rule structure\n"); + return false; + } + } + + // Check the location it is resting on + int baseMaterial = thisLevel->getTile(origin[0], origin[1] - 1, origin[2]); + if (!((baseMaterial == 2) || (baseMaterial == 3))) + { + return false; + } + int allowedHeight = checkLine(startPosition, endPosition); + // If the set height is good, go with that + if (allowedHeight == -1) + { + return true; + } + // If the space is too short, tell the build to abort + else if (allowedHeight < 6) + { + return false; + } + // If the space is shorter than the set height, but not too short + // shorten the height, and tell the build to continue + else + { + height = allowedHeight; + //System.out.println("Shortened the tree"); + return true; + } +} + +void BasicTree::init(double heightInit, double widthInit, double foliageDensityInit) +{ + // all of the parameters should be from 0.0 to 1.0 + // heightInit scales the maximum overall height of the tree (still randomizes height within the possible range) + // widthInit scales the maximum overall width of the tree (keep this above 0.3 or so) + // foliageDensityInit scales how many foliage clusters are created. + // + // Note, you can call "place" without calling "init". + // This is the same as calling init(1.0,1.0,1.0) and then calling place. + heightVariance = (int) (heightInit * 12); + if (heightInit > 0.5) foliageHeight = 5; + widthScale = widthInit; + foliageDensity = foliageDensityInit; +} + +bool BasicTree::place(Level *level, Random *random, int x, int y, int z) +{ + // Note to Markus. + // currently the following fields are set randomly. If you like, make them + // parameters passed into "place". + // + // height: so the map generator can intelligently set the height of the tree, + // and make forests with large trees in the middle and smaller ones on the edges. + + // Initialize the instance fields for the level and the seed. + thisLevel = level; + __int64 seed = random->nextLong(); + rnd->setSeed(seed); + // Initialize the origin of the tree trunk + origin[0] = x; + origin[1] = y; + origin[2] = z; + // Sets the height. Take out this line if height is passed as a parameter + if (height == 0) + { + height = 5 + rnd->nextInt(heightVariance); + } + if (!(checkLocation())) + { + //System.out.println("Tree location failed"); + return false; + } + PIXBeginNamedEvent(0, "Placing BasicTree"); + //System.out.println("The height is"); + //System.out.println(height); + //System.out.println("Trunk Height check done"); + PIXBeginNamedEvent(0, "Preparing tree"); + prepare(); + PIXEndNamedEvent(); + //System.out.println("Prepare done"); + PIXBeginNamedEvent(0, "Making foliage"); + makeFoliage(); + PIXEndNamedEvent(); + //System.out.println("Foliage done"); + PIXBeginNamedEvent(0, "Making trunk"); + makeTrunk(); + PIXEndNamedEvent(); + //System.out.println("Trunk done"); + PIXBeginNamedEvent(0, "Making branches"); + makeBranches(); + PIXEndNamedEvent(); + //System.out.println("Branches done"); + PIXEndNamedEvent(); + return true; +} \ No newline at end of file diff --git a/Minecraft.World/BasicTree.h b/Minecraft.World/BasicTree.h new file mode 100644 index 00000000..feb14581 --- /dev/null +++ b/Minecraft.World/BasicTree.h @@ -0,0 +1,70 @@ +#pragma once +#include "Feature.h" + +class Level; + +class BasicTree : public Feature +{ +private: + // The axisConversionArray, when given a primary index, allows easy + // access to the indices of the other two axies. Access the data at the + // primary index location to get the horizontal secondary axis. + // Access the data at the primary location plus three to get the + // remaining, tertiary, axis. + // All directions are specified by an index, 0, 1, or 2 which + // correspond to x, y, and z. + // The axisConversionArray is used in several places + // notably the crossection and taperedLimb methods. + // Example: + // If the primary axis is z, then the primary index is 2. + // The secondary index is axisConversionArray[2] which is 0, + // the index for the x axis. + // The remaining axis is axisConversionArray[2 + 3] which is 1, + // the index for the y axis. + // Using this method, the secondary axis will always be horizontal (x or z), + // and the tertiary always vertical (y), if possible. + static byte axisConversionArray[]; + + // Set up the pseudorandom number generator + Random *rnd; + + // Make fields to hold the level data and the random seed + Level *thisLevel; + + // Field to hold the tree origin, x y and z. + int origin[3]; + // Field to hold the tree height. + int height; + // Other important tree information. + int trunkHeight; + double trunkHeightScale; + double branchDensity; + double branchSlope; + double widthScale; + double foliageDensity; + int trunkWidth; + int heightVariance; + int foliageHeight; + // The foliage coordinates are a list of [x,y,z,y of branch base] values for each cluster + int **foliageCoords; + int foliageCoordsLength; + void prepare(); + void crossection(int x, int y, int z, float radius, byte direction, int material); + float treeShape(int y); + float foliageShape(int y); + void foliageCluster(int x, int y, int z); + void limb(int *start, int *end, int material); + void makeFoliage(); + bool trimBranches(int localY); + void makeTrunk(); + void makeBranches(); + int checkLine(int *start, int *end); + bool checkLocation(); + +public: + BasicTree(bool doUpdate); + virtual ~BasicTree(); + + virtual void init(double heightInit, double widthInit, double foliageDensityInit); + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; diff --git a/Minecraft.World/BasicTypeContainers.cpp b/Minecraft.World/BasicTypeContainers.cpp new file mode 100644 index 00000000..ea9839c6 --- /dev/null +++ b/Minecraft.World/BasicTypeContainers.cpp @@ -0,0 +1,20 @@ +#include "stdafx.h" +#include "BasicTypeContainers.h" +#include + + +/* 4J Jev TODO, + this is different to Float::MAX_VALUE, javas floats + seem to actually allow values of infinity which c++ does not. +*/ +//A constant holding the positive infinity of type float. It is equal to the value returned by Float.intBitsToFloat(0x7f800000). +const float Float::POSITIVE_INFINITY = 0x7f800000; + +const float Float::MAX_VALUE = FLT_MAX; + +const double Double::MAX_VALUE = DBL_MAX; + +int Integer::parseInt(wstring &str, int radix /* = 10*/) +{ + return wcstol( str.c_str(), NULL, radix ); +} \ No newline at end of file diff --git a/Minecraft.World/BasicTypeContainers.h b/Minecraft.World/BasicTypeContainers.h new file mode 100644 index 00000000..094e9616 --- /dev/null +++ b/Minecraft.World/BasicTypeContainers.h @@ -0,0 +1,75 @@ +#pragma once + + +class Byte +{ +public: + static const char MAX_VALUE = CHAR_MAX; + static const char MIN_VALUE = CHAR_MIN; +}; + +class Short +{ +public: + static const short MAX_VALUE = SHRT_MAX; + static const short MIN_VALUE = SHRT_MIN; +}; + +class Integer +{ +public: + static const int MAX_VALUE = INT_MAX; + static int parseInt(wstring &str, int radix = 10); +}; + +class Float +{ +public: + static const float MAX_VALUE; + static int floatToIntBits( float value ) + { + return *(int *)&value; + } + static int floatToRawIntBits( float value ) + { + return *(int *)&value; + } + + static float intBitsToFloat( int bits ) + { + return *(float *)&bits; + } + + static const float POSITIVE_INFINITY; +}; + +class Double +{ +public: + static const double MAX_VALUE; + + static bool isNaN( double a ) { +#ifdef __PS3__ + return isnan(a); +#else + return ( a != a ); +#endif + } + static bool isInfinite( double a ) { return false; /*4J TODO*/ } + + static double longBitsToDouble( __int64 bits ) + { + return *(double *)&bits; + } + + static __int64 doubleToLongBits( double d ) + { + return *(__int64 *)&d; + } +}; + +// 4J Stu - The String class should only be used if we need to use the BaseClass::class type +// As such I have renamed it so that we don't confuse it with places where we should use std::string +class _String +{ +}; \ No newline at end of file diff --git a/Minecraft.World/BeachBiome.cpp b/Minecraft.World/BeachBiome.cpp new file mode 100644 index 00000000..ba85908a --- /dev/null +++ b/Minecraft.World/BeachBiome.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.tile.h" +#include "BiomeDecorator.h" +#include "BeachBiome.h" + +BeachBiome::BeachBiome(int id) : Biome(id) +{ + // remove default mob spawn settings + friendlies.clear(); + friendlies_chicken.clear(); // 4J added + this->topMaterial = (byte) Tile::sand_Id; + this->material = (byte) Tile::sand_Id; + + decorator->treeCount = -999; + decorator->deadBushCount = 0; + decorator->reedsCount = 0; + decorator->cactusCount = 0; +} \ No newline at end of file diff --git a/Minecraft.World/BeachBiome.h b/Minecraft.World/BeachBiome.h new file mode 100644 index 00000000..d01f63e3 --- /dev/null +++ b/Minecraft.World/BeachBiome.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Biome.h" + +class BeachBiome : public Biome +{ +public: + BeachBiome(int id); +}; \ No newline at end of file diff --git a/Minecraft.World/BedItem.cpp b/Minecraft.World/BedItem.cpp new file mode 100644 index 00000000..1ff50c6c --- /dev/null +++ b/Minecraft.World/BedItem.cpp @@ -0,0 +1,59 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "Facing.h" +#include "GenericStats.h" +#include "BedItem.h" + +BedItem::BedItem(int id) : Item( id ) +{ +} + +bool BedItem::useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + if (face != Facing::UP) + { + return false; + } + + // place on top of tile + y = y + 1; + + BedTile *tile = (BedTile *) Tile::bed; + + int dir = ( Mth::floor(player->yRot * 4 / (360) + 0.5f) ) & 3; + int xra = 0; + int zra = 0; + + if (dir == Direction::SOUTH) zra = 1; + if (dir == Direction::WEST) xra = -1; + if (dir == Direction::NORTH) zra = -1; + if (dir == Direction::EAST) xra = 1; + + if (!player->mayBuild(x, y, z) || !player->mayBuild(x + xra, y, z + zra)) return false; + + if (level->isEmptyTile(x, y, z) && level->isEmptyTile(x + xra, y, z + zra) && level->isTopSolidBlocking(x, y - 1, z) && level->isTopSolidBlocking(x + xra, y - 1, z + zra)) + { + // 4J-PB - Adding a test only version to allow tooltips to be displayed + if(!bTestUseOnOnly) + { + level->setTileAndData(x, y, z, tile->id, dir); + // double-check that the bed was successfully placed + if (level->getTile(x, y, z) == tile->id) + { + // 4J-JEV: Hook for durango 'BlockPlaced' event. + player->awardStat(GenericStats::blocksPlaced(tile->id), GenericStats::param_blocksPlaced(tile->id,itemInstance->getAuxValue(),1)); + + level->setTileAndData(x + xra, y, z + zra, tile->id, dir + BedTile::HEAD_PIECE_DATA); + } + + itemInstance->count--; + } + return true; + } + + return false; +} diff --git a/Minecraft.World/BedItem.h b/Minecraft.World/BedItem.h new file mode 100644 index 00000000..ad0297bd --- /dev/null +++ b/Minecraft.World/BedItem.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Item.h" + +class Player; +class Level; + +class BedItem : public Item +{ + +public: + BedItem(int id); + + virtual bool useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); +}; \ No newline at end of file diff --git a/Minecraft.World/BedTile.cpp b/Minecraft.World/BedTile.cpp new file mode 100644 index 00000000..7d32c6d3 --- /dev/null +++ b/Minecraft.World/BedTile.cpp @@ -0,0 +1,332 @@ +#include "stdafx.h" +#include "Dimension.h" +#include "net.minecraft.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" +#include "BedTile.h" + +int BedTile::HEAD_DIRECTION_OFFSETS[4][2] = +{ + { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 0 } +}; + +BedTile::BedTile(int id) : DirectionalTile(id, Material::cloth, isSolidRender()) +{ + setShape(); + + iconEnd = NULL; + iconSide = NULL; + iconTop = NULL; +} + +// 4J Added override +void BedTile::updateDefaultShape() +{ + setShape(); +} + +// 4J-PB - Adding a TestUse for tooltip display +bool BedTile::TestUse(Level *level, int x, int y, int z, shared_ptr player) +{ + //if (level->isClientSide) return true; + + int data = level->getData(x, y, z); + + if (!BedTile::isHeadPiece(data)) + { + // fetch head piece instead + int direction = getDirection(data); + x += HEAD_DIRECTION_OFFSETS[direction][0]; + z += HEAD_DIRECTION_OFFSETS[direction][1]; + if (level->getTile(x, y, z) != id) + { + return true; + } + data = level->getData(x, y, z); + } + + if (!level->dimension->mayRespawn()) + { + return false; + } + if (BedTile::isOccupied(data)) + { + return false; + } + + Player::BedSleepingResult result = player->startSleepInBed(x, y, z, true); // true to just test the start sleep + if (result == Player::OK) + { + return true; + } + + return false; +} + +bool BedTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if( soundOnly) return false; + if (level->isClientSide) return true; + + int data = level->getData(x, y, z); + + if (!BedTile::isHeadPiece(data)) + { + // fetch head piece instead + int direction = getDirection(data); + x += HEAD_DIRECTION_OFFSETS[direction][0]; + z += HEAD_DIRECTION_OFFSETS[direction][1]; + if (level->getTile(x, y, z) != id) + { + return true; + } + data = level->getData(x, y, z); + } + + if (!level->dimension->mayRespawn()) + { + double xc = x + 0.5; + double yc = y + 0.5; + double zc = z + 0.5; + level->setTile(x, y, z, 0); + int direction = getDirection(data); + x += HEAD_DIRECTION_OFFSETS[direction][0]; + z += HEAD_DIRECTION_OFFSETS[direction][1]; + if (level->getTile(x, y, z) == id) { + level->setTile(x, y, z, 0); + xc = (xc + x + 0.5) / 2; + yc = (yc + y + 0.5) / 2; + zc = (zc + z + 0.5) / 2; + } + level->explode(nullptr, x + 0.5f, y + 0.5f, z + 0.5f, 5, true, true); + return true; + } + + if (BedTile::isOccupied(data)) + { + shared_ptr sleepingPlayer = nullptr; + AUTO_VAR(itEnd, level->players.end()); + for (AUTO_VAR(it, level->players.begin()); it != itEnd; it++ ) + { + shared_ptr p = *it; + if (p->isSleeping()) + { + Pos pos = p->bedPosition; + if (pos.x == x && pos.y == y && pos.z == z) + { + sleepingPlayer = p; + } + } + } + + if (sleepingPlayer == NULL) + { + BedTile::setOccupied(level, x, y, z, false); + } + else + { + player->displayClientMessage(IDS_TILE_BED_OCCUPIED ); + + return true; + } + } + + Player::BedSleepingResult result = player->startSleepInBed(x, y, z); + if (result == Player::OK) + { + BedTile::setOccupied(level, x, y, z, true); + // 4J-PB added + // are there multiple players in the same world as us? + if(level->AllPlayersAreSleeping()==false) + { + player->displayClientMessage(IDS_TILE_BED_PLAYERSLEEP); + } + return true; + } + + if (result == Player::NOT_POSSIBLE_NOW) + { + player->displayClientMessage(IDS_TILE_BED_NO_SLEEP); + } + else if (result == Player::NOT_SAFE) + { + player->displayClientMessage(IDS_TILE_BED_NOTSAFE); + } + + return true; +} + +Icon *BedTile::getTexture(int face, int data) +{ + if (face == Facing::DOWN) + { + return Tile::wood->getTexture(face); + } + + int direction = getDirection(data); + int tileFacing = Direction::RELATIVE_DIRECTION_FACING[direction][face]; + + int part = isHeadPiece(data) ? PART_HEAD : PART_FOOT; + + if ((part == PART_HEAD && tileFacing == Facing::NORTH) || (part == PART_FOOT && tileFacing == Facing::SOUTH)) + { + return iconEnd[part]; + } + if (tileFacing == Facing::EAST || tileFacing == Facing::WEST) + { + return iconSide[part]; + } + return iconTop[part]; +} + +void BedTile::registerIcons(IconRegister *iconRegister) +{ + iconTop = new Icon *[2]; + iconTop[0] = iconRegister->registerIcon(L"bed_feet_top"); + iconTop[1] = iconRegister->registerIcon(L"bed_head_top"); + + iconEnd = new Icon *[2]; + iconEnd[0] = iconRegister->registerIcon(L"bed_feet_end"); + iconEnd[1] = iconRegister->registerIcon(L"bed_head_end"); + + iconSide = new Icon *[2]; + iconSide[0] = iconRegister->registerIcon(L"bed_feet_side"); + iconSide[1] = iconRegister->registerIcon(L"bed_head_side"); +} + +int BedTile::getRenderShape() +{ + return Tile::SHAPE_BED; +} + +bool BedTile::isCubeShaped() +{ + return false; +} + +bool BedTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +void BedTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + setShape(); +} + +void BedTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + int data = level->getData(x, y, z); + int direction = getDirection(data); + + if (isHeadPiece(data)) + { + if (level->getTile(x - HEAD_DIRECTION_OFFSETS[direction][0], y, z - HEAD_DIRECTION_OFFSETS[direction][1]) != id) + { + level->setTile(x, y, z, 0); + } + } else + { + if (level->getTile(x + HEAD_DIRECTION_OFFSETS[direction][0], y, z + HEAD_DIRECTION_OFFSETS[direction][1]) != id) + { + level->setTile(x, y, z, 0); + if (!level->isClientSide) + { + Tile::spawnResources(level, x, y, z, data, 0); // 4J - had to add Tile:: here for C++ since this class doesn't have this overloaded method itself + } + } + } +} + +int BedTile::getResource(int data, Random *random, int playerBonusLevel) +{ + if (isHeadPiece(data)) + { + return 0; + } + return Item::bed->id; +} + +void BedTile::setShape() +{ + Tile::setShape(0, 0, 0, 1, 9 / 16.0f, 1); +} + +bool BedTile::isHeadPiece(int data) +{ + return (data & HEAD_PIECE_DATA) != 0; +} + +bool BedTile::isOccupied(int data) +{ + return (data & OCCUPIED_DATA) != 0; +} + +void BedTile::setOccupied(Level *level, int x, int y, int z, bool occupied) +{ + int data = level->getData(x, y, z); + if (occupied) + { + data = data | OCCUPIED_DATA; + } else + { + data = data & ~OCCUPIED_DATA; + } + level->setData(x, y, z, data); +} + +Pos *BedTile::findStandUpPosition(Level *level, int x, int y, int z, int skipCount) +{ + int data = level->getData(x, y, z); + int direction = DirectionalTile::getDirection(data); + + // try to find a clear location near the bed + for (int step = 0; step <= 1; step++) + { + int startX = x - BedTile::HEAD_DIRECTION_OFFSETS[direction][0] * step - 1; + int startZ = z - BedTile::HEAD_DIRECTION_OFFSETS[direction][1] * step - 1; + int endX = startX + 2; + int endZ = startZ + 2; + + for (int standX = startX; standX <= endX; standX++) + { + for (int standZ = startZ; standZ <= endZ; standZ++) + { + // 4J Stu - Changed to check isSolidBlockingTile rather than isEmpty for the blocks that we wish to place the player + // This allows the player to spawn in blocks with snow, grass etc + if (level->isTopSolidBlocking(standX, y - 1, standZ) && + !level->isSolidBlockingTile(standX, y, standZ) && + !level->isSolidBlockingTile(standX, y + 1, standZ)) + { + if (skipCount > 0) { + skipCount--; + continue; + } + return new Pos(standX, y, standZ); + } + } + } + } + + return NULL; +} + +void BedTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus) +{ + if (!isHeadPiece(data)) + { + Tile::spawnResources(level, x, y, z, data, odds, 0); + } +} + +int BedTile::getPistonPushReaction() +{ + return Material::PUSH_DESTROY; +} + +int BedTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Item::bed_Id; +} \ No newline at end of file diff --git a/Minecraft.World/BedTile.h b/Minecraft.World/BedTile.h new file mode 100644 index 00000000..339080d3 --- /dev/null +++ b/Minecraft.World/BedTile.h @@ -0,0 +1,53 @@ +#pragma once +#include "DirectionalTile.h" + +class Pos; +class Player; +class Random; +class Level; + +class BedTile : public DirectionalTile +{ +private: + static const int PART_FOOT = 0; + static const int PART_HEAD = 1; + + Icon **iconEnd; + Icon **iconSide; + Icon **iconTop; + +public: + static const int HEAD_PIECE_DATA = 0x8; + static const int OCCUPIED_DATA = 0x4; + + static int HEAD_DIRECTION_OFFSETS[4][2]; + + BedTile(int id); + + virtual void updateDefaultShape(); + virtual bool TestUse(Level *level, int x, int y, int z, shared_ptr player); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + virtual Icon *getTexture(int face, int data); + //@Override + void registerIcons(IconRegister *iconRegister); + virtual int getRenderShape(); + virtual bool isCubeShaped(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual int getResource(int data, Random *random,int playerBonusLevel); + +private: + using Tile::setShape; + void setShape(); + +public: + static bool isHeadPiece(int data); + static bool isOccupied(int data); + static void setOccupied(Level *level, int x, int y, int z, bool occupied); + static Pos *findStandUpPosition(Level *level, int x, int y, int z, int skipCount); + + virtual void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus); + virtual int getPistonPushReaction(); + virtual int cloneTileId(Level *level, int x, int y, int z); +}; diff --git a/Minecraft.World/BegGoal.cpp b/Minecraft.World/BegGoal.cpp new file mode 100644 index 00000000..97553928 --- /dev/null +++ b/Minecraft.World/BegGoal.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "BegGoal.h" + +BegGoal::BegGoal(Wolf *wolf, float lookDistance) +{ + player = weak_ptr(); + lookTime = 0; + + this->wolf = wolf; + this->level = wolf->level; + this->lookDistance = lookDistance; + setRequiredControlFlags(Control::LookControlFlag); +} + +bool BegGoal::canUse() +{ + player = weak_ptr(level->getNearestPlayer(wolf->shared_from_this(), lookDistance)); + if (player.lock() == NULL) return false; + wolf->setDespawnProtected(); + return playerHoldingInteresting(player.lock()); +} + +bool BegGoal::canContinueToUse() +{ + if (player.lock() == NULL || !player.lock()->isAlive()) return false; + if (wolf->distanceToSqr(player.lock()) > lookDistance * lookDistance) return false; + wolf->setDespawnProtected(); + return lookTime > 0 && playerHoldingInteresting(player.lock()); +} + +void BegGoal::start() +{ + wolf->setIsInterested(true); + lookTime = 40 + wolf->getRandom()->nextInt(40); +} + +void BegGoal::stop() +{ + wolf->setIsInterested(false); + player = weak_ptr(); +} + +void BegGoal::tick() +{ + wolf->getLookControl()->setLookAt(player.lock()->x, player.lock()->y + player.lock()->getHeadHeight(), player.lock()->z, 10, wolf->getMaxHeadXRot()); + --lookTime; +} + +bool BegGoal::playerHoldingInteresting(shared_ptr player) +{ + shared_ptr item = player->inventory->getSelected(); + if (item == NULL) return false; + if (!wolf->isTame() && item->id == Item::bone_Id) return true; + return wolf->isFood(item); +} \ No newline at end of file diff --git a/Minecraft.World/BegGoal.h b/Minecraft.World/BegGoal.h new file mode 100644 index 00000000..607937a7 --- /dev/null +++ b/Minecraft.World/BegGoal.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Goal.h" + +class Wolf; + +class BegGoal : public Goal +{ +private: + Wolf *wolf; // Owner of this goal + weak_ptr player; + Level *level; + float lookDistance; + int lookTime; + +public: + BegGoal(Wolf *wolf, float lookDistance); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + virtual void stop(); + virtual void tick(); + +private: + bool playerHoldingInteresting(shared_ptr player); + +public: + // 4J Added override to update ai elements when loading entity from schematics + virtual void setLevel(Level *level) { this->level = level; } +}; \ No newline at end of file diff --git a/Minecraft.World/BinaryHeap.cpp b/Minecraft.World/BinaryHeap.cpp new file mode 100644 index 00000000..3a3d1c1e --- /dev/null +++ b/Minecraft.World/BinaryHeap.cpp @@ -0,0 +1,188 @@ +#include "stdafx.h" +#include "Node.h" +#include "System.h" +#include "BasicTypeContainers.h" +#include "BinaryHeap.h" + +// 4J Jev, add common ctor code. +void BinaryHeap::_init() +{ + heap = NodeArray(1024); + sizeVar = 0; +} + +BinaryHeap::BinaryHeap() +{ + _init(); +} + +BinaryHeap::~BinaryHeap() +{ + delete[] heap.data; +} + +Node *BinaryHeap::insert(Node *node) +{ + /* if (node->heapIdx >=0) throw new IllegalStateException("OW KNOWS!"); 4J Jev, removed try/catch */ + + // Expand if necessary. + if (sizeVar == heap.length) + { + NodeArray newHeap = NodeArray(sizeVar << 1); + + System::arraycopy(heap, 0, &newHeap, 0, sizeVar); + + delete[] heap.data; + heap = newHeap; + } + + // Insert at end and bubble up. + heap[sizeVar] = node; + node->heapIdx = sizeVar; + upHeap(sizeVar++); + + return node; +} + +void BinaryHeap::clear() +{ + sizeVar = 0; +} + +Node *BinaryHeap::peek() +{ + return heap[0]; +} + +Node *BinaryHeap::pop() +{ + Node *popped = heap[0]; + heap[0] = heap[--sizeVar]; + heap[sizeVar] = NULL; + if (sizeVar > 0) downHeap(0); + popped->heapIdx=-1; + return popped; +} + +void BinaryHeap::remove(Node *node) +{ + // This is what node.heapIdx is for. + heap[node->heapIdx] = heap[--sizeVar]; + heap[sizeVar] = NULL; + if (sizeVar > node->heapIdx) + { + if (heap[node->heapIdx]->f < node->f) + { + upHeap(node->heapIdx); + } + else + { + downHeap(node->heapIdx); + } + } + // Just as a precaution: should make stuff blow up if the node is abused. + node->heapIdx = -1; +} + +void BinaryHeap::changeCost(Node *node, float newCost) +{ + float oldCost = node->f; + node->f = newCost; + if (newCost < oldCost) + { + upHeap(node->heapIdx); + } + else + { + downHeap(node->heapIdx); + } +} + +int BinaryHeap::size() +{ + return sizeVar; +} + +void BinaryHeap::upHeap(int idx) +{ + Node *node = heap[idx]; + float cost = node->f; + while (idx > 0) + { + int parentIdx = (idx - 1) >> 1; + Node *parent = heap[parentIdx]; + if (cost < parent->f) + { + heap[idx] = parent; + parent->heapIdx = idx; + idx = parentIdx; + } + else break; + } + heap[idx] = node; + node->heapIdx = idx; +} + +void BinaryHeap::downHeap(int idx) +{ + Node *node = heap[idx]; + float cost = node->f; + + while (true) + { + int leftIdx = 1 + (idx << 1); + int rightIdx = leftIdx + 1; + + if (leftIdx >= sizeVar) break; + + // We definitely have a left child. + Node *leftNode = heap[leftIdx]; + float leftCost = leftNode->f; + // We may have a right child. + Node *rightNode; + float rightCost; + + if (rightIdx >= sizeVar) + { + // Only need to compare with left. + rightNode = NULL; + rightCost = Float::POSITIVE_INFINITY; + } + else + { + rightNode = heap[rightIdx]; + rightCost = rightNode->f; + } + + // Find the smallest of the three costs: the corresponding node + // should be the parent. + if (leftCost < rightCost) + { + if (leftCost < cost) + { + heap[idx] = leftNode; + leftNode->heapIdx = idx; + idx = leftIdx; + } + else break; + } + else + { + if (rightCost < cost) + { + heap[idx] = rightNode; + rightNode->heapIdx = idx; + idx = rightIdx; + } + else break; + } + } + + heap[idx] = node; + node->heapIdx = idx; +} + +bool BinaryHeap::isEmpty() +{ + return sizeVar==0; +} \ No newline at end of file diff --git a/Minecraft.World/BinaryHeap.h b/Minecraft.World/BinaryHeap.h new file mode 100644 index 00000000..25b7a9ab --- /dev/null +++ b/Minecraft.World/BinaryHeap.h @@ -0,0 +1,36 @@ +#pragma once +using namespace std; + +#include "stdafx.h" +#include "Node.h" +#include "System.h" +#include "BasicTypeContainers.h" + +class BinaryHeap +{ +private: + NodeArray heap; + int sizeVar; + + // 4J Jev, add common ctor code. + void _init(); + +public: + BinaryHeap(); + ~BinaryHeap(); + + Node *insert(Node *node); + void clear(); + Node *peek(); + Node *pop(); + void remove(Node *node); + void changeCost(Node *node, float newCost); + int size(); + +private: + void upHeap(int idx); + void downHeap(int idx); + +public: + bool isEmpty(); +}; \ No newline at end of file diff --git a/Minecraft.World/Biome.cpp b/Minecraft.World/Biome.cpp new file mode 100644 index 00000000..d2f4cd1c --- /dev/null +++ b/Minecraft.World/Biome.cpp @@ -0,0 +1,308 @@ +#include "stdafx.h" +#include "Color.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.entity.h" +#include "Biome.h" +#include "net.minecraft.world.level.biome.h" + +//public static final Biome[] biomes = new Biome[256]; +Biome *Biome::biomes[256]; + +Biome *Biome::ocean = NULL; +Biome *Biome::plains = NULL; +Biome *Biome::desert = NULL; + +Biome *Biome::extremeHills = NULL; +Biome *Biome::forest = NULL; +Biome *Biome::taiga = NULL; + +Biome *Biome::swampland = NULL; +Biome *Biome::river = NULL; + +Biome *Biome::hell = NULL; +Biome *Biome::sky = NULL; + +Biome *Biome::frozenOcean = NULL; +Biome *Biome::frozenRiver = NULL; +Biome *Biome::iceFlats = NULL; +Biome *Biome::iceMountains = NULL; +Biome *Biome::mushroomIsland = NULL; +Biome *Biome::mushroomIslandShore = NULL; +Biome *Biome::beaches = NULL; +Biome *Biome::desertHills = NULL; +Biome *Biome::forestHills = NULL; +Biome *Biome::taigaHills = NULL; +Biome *Biome::smallerExtremeHills = NULL; + +Biome *Biome::jungle = NULL; +Biome *Biome::jungleHills = NULL; + + +void Biome::staticCtor() +{ + //public static final Biome[] biomes = new Biome[256]; + + Biome::ocean = (new OceanBiome(0))->setColor(0x000070)->setName(L"Ocean")->setDepthAndScale(-1, 0.4f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_Ocean, eMinecraftColour_Foliage_Ocean, eMinecraftColour_Water_Ocean,eMinecraftColour_Sky_Ocean); + Biome::plains = (new PlainsBiome(1))->setColor(0x8db360)->setName(L"Plains")->setTemperatureAndDownfall(0.8f, 0.4f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_Plains, eMinecraftColour_Foliage_Plains, eMinecraftColour_Water_Plains,eMinecraftColour_Sky_Plains); + Biome::desert = (new DesertBiome(2))->setColor(0xFA9418)->setName(L"Desert")->setNoRain()->setTemperatureAndDownfall(2, 0)->setDepthAndScale(0.1f, 0.2f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_Desert, eMinecraftColour_Foliage_Desert, eMinecraftColour_Water_Desert,eMinecraftColour_Sky_Desert); + + Biome::extremeHills = (new ExtremeHillsBiome(3))->setColor(0x606060)->setName(L"Extreme Hills")->setDepthAndScale(0.3f, 1.5f)->setTemperatureAndDownfall(0.2f, 0.3f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_ExtremeHills, eMinecraftColour_Foliage_ExtremeHills, eMinecraftColour_Water_ExtremeHills,eMinecraftColour_Sky_ExtremeHills); + Biome::forest = (new ForestBiome(4))->setColor(0x056621)->setName(L"Forest")->setLeafColor(0x4EBA31)->setTemperatureAndDownfall(0.7f, 0.8f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_Forest, eMinecraftColour_Foliage_Forest, eMinecraftColour_Water_Forest,eMinecraftColour_Sky_Forest); + // 4J - brought forward Taiga temperature change from 0.3f to 0.05f, from 1.2.3 + Biome::taiga = (new TaigaBiome(5))->setColor(0x0b6659)->setName(L"Taiga")->setLeafColor(0x4EBA31)->setSnowCovered()->setTemperatureAndDownfall(0.05f, 0.8f)->setDepthAndScale(0.1f, 0.4f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_Taiga, eMinecraftColour_Foliage_Taiga, eMinecraftColour_Water_Taiga,eMinecraftColour_Sky_Taiga); + + Biome::swampland = (new SwampBiome(6))->setColor(0x07F9B2)->setName(L"Swampland")->setLeafColor(0x8BAF48)->setDepthAndScale(-0.2f, 0.1f)->setTemperatureAndDownfall(0.8f, 0.9f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_Swampland, eMinecraftColour_Foliage_Swampland, eMinecraftColour_Water_Swampland,eMinecraftColour_Sky_Swampland); + Biome::river = (new RiverBiome(7))->setColor(0x0000ff)->setName(L"River")->setDepthAndScale(-0.5f, 0)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_River, eMinecraftColour_Foliage_River, eMinecraftColour_Water_River,eMinecraftColour_Sky_River); + + Biome::hell = (new HellBiome(8))->setColor(0xff0000)->setName(L"Hell")->setNoRain()->setTemperatureAndDownfall(2, 0)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_Hell, eMinecraftColour_Foliage_Hell, eMinecraftColour_Water_Hell,eMinecraftColour_Sky_Hell); + Biome::sky = (new TheEndBiome(9))->setColor(0x8080ff)->setName(L"Sky")->setNoRain()->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_Sky, eMinecraftColour_Foliage_Sky, eMinecraftColour_Water_Sky,eMinecraftColour_Sky_Sky); + + Biome::frozenOcean = (new OceanBiome(10))->setColor(0x9090a0)->setName(L"FrozenOcean")->setSnowCovered()->setDepthAndScale(-1, 0.5f)->setTemperatureAndDownfall(0, 0.5f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_FrozenOcean, eMinecraftColour_Foliage_FrozenOcean, eMinecraftColour_Water_FrozenOcean,eMinecraftColour_Sky_FrozenOcean); + Biome::frozenRiver = (new RiverBiome(11))->setColor(0xa0a0ff)->setName(L"FrozenRiver")->setSnowCovered()->setDepthAndScale(-0.5f, 0)->setTemperatureAndDownfall(0, 0.5f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_FrozenRiver, eMinecraftColour_Foliage_FrozenRiver, eMinecraftColour_Water_FrozenRiver,eMinecraftColour_Sky_FrozenRiver); + Biome::iceFlats = (new IceBiome(12))->setColor(0xffffff)->setName(L"Ice Plains")->setSnowCovered()->setTemperatureAndDownfall(0, 0.5f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_IcePlains, eMinecraftColour_Foliage_IcePlains, eMinecraftColour_Water_IcePlains,eMinecraftColour_Sky_IcePlains); + Biome::iceMountains = (new IceBiome(13))->setColor(0xa0a0a0)->setName(L"Ice Mountains")->setSnowCovered()->setDepthAndScale(0.3f, 1.3f)->setTemperatureAndDownfall(0, 0.5f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_IceMountains, eMinecraftColour_Foliage_IceMountains, eMinecraftColour_Water_IceMountains,eMinecraftColour_Sky_IceMountains); + + Biome::mushroomIsland = (new MushroomIslandBiome(14))->setColor(0xff00ff)->setName(L"MushroomIsland")->setTemperatureAndDownfall(0.9f, 1.0f)->setDepthAndScale(0.2f, 1.0f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_MushroomIsland, eMinecraftColour_Foliage_MushroomIsland, eMinecraftColour_Water_MushroomIsland,eMinecraftColour_Sky_MushroomIsland); + Biome::mushroomIslandShore = (new MushroomIslandBiome(15))->setColor(0xa000ff)->setName(L"MushroomIslandShore")->setTemperatureAndDownfall(0.9f, 1.0f)->setDepthAndScale(-1, 0.1f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_MushroomIslandShore, eMinecraftColour_Foliage_MushroomIslandShore, eMinecraftColour_Water_MushroomIslandShore,eMinecraftColour_Sky_MushroomIslandShore); + + Biome::beaches = (new BeachBiome(16))->setColor(0xfade55)->setName(L"Beach")->setTemperatureAndDownfall(0.8f, 0.4f)->setDepthAndScale(0.0f, 0.1f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_Beach, eMinecraftColour_Foliage_Beach, eMinecraftColour_Water_Beach,eMinecraftColour_Sky_Beach); + Biome::desertHills = (new DesertBiome(17))->setColor(0xd25f12)->setName(L"DesertHills")->setNoRain()->setTemperatureAndDownfall(2, 0)->setDepthAndScale(0.3f, 0.8f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_DesertHills, eMinecraftColour_Foliage_DesertHills, eMinecraftColour_Water_DesertHills,eMinecraftColour_Sky_DesertHills); + Biome::forestHills = (new ForestBiome(18))->setColor(0x22551c)->setName(L"ForestHills")->setLeafColor(0x4EBA31)->setTemperatureAndDownfall(0.7f, 0.8f)->setDepthAndScale(0.3f, 0.7f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_ForestHills, eMinecraftColour_Foliage_ForestHills, eMinecraftColour_Water_ForestHills,eMinecraftColour_Sky_ForestHills); + Biome::taigaHills = (new TaigaBiome(19))->setColor(0x163933)->setName(L"TaigaHills")->setSnowCovered()->setLeafColor(0x4EBA31)->setTemperatureAndDownfall(0.05f, 0.8f)->setDepthAndScale(0.3f, 0.8f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_TaigaHills, eMinecraftColour_Foliage_TaigaHills, eMinecraftColour_Water_TaigaHills,eMinecraftColour_Sky_TaigaHills); + Biome::smallerExtremeHills = (new ExtremeHillsBiome(20))->setColor(0x72789a)->setName(L"Extreme Hills Edge")->setDepthAndScale(0.2f, 0.8f)->setTemperatureAndDownfall(0.2f, 0.3f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_ExtremeHillsEdge, eMinecraftColour_Foliage_ExtremeHillsEdge, eMinecraftColour_Water_ExtremeHillsEdge,eMinecraftColour_Sky_ExtremeHillsEdge); + + Biome::jungle = (new JungleBiome(21))->setColor(0x537b09)->setName(L"Jungle")->setLeafColor(0x537b09)->setTemperatureAndDownfall(1.2f, 0.9f)->setDepthAndScale(0.2f, 0.4f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_Jungle, eMinecraftColour_Foliage_Jungle, eMinecraftColour_Water_Jungle,eMinecraftColour_Sky_Jungle); + Biome::jungleHills = (new JungleBiome(22))->setColor(0x2c4205)->setName(L"JungleHills")->setLeafColor(0x537b09)->setTemperatureAndDownfall(1.2f, 0.9f)->setDepthAndScale(1.8f, 0.5f)->setLeafFoliageWaterSkyColor(eMinecraftColour_Grass_JungleHills, eMinecraftColour_Foliage_JungleHills, eMinecraftColour_Water_JungleHills,eMinecraftColour_Sky_JungleHills); +} + +Biome::Biome(int id) : id(id) +{ + // 4J Stu Default inits + color = 0; +// snowCovered = false; // 4J - this isn't set by the java game any more so removing to save confusion + + topMaterial = (byte) Tile::grass_Id; + material = (byte) Tile::dirt_Id; + leafColor = 0x4EE031; + _hasRain = true; + depth = 0.1f; + scale = 0.3f; + temperature = 0.5f; + downfall = 0.5f; + //waterColor = 0xffffff; // 4J Stu - Not used + decorator = NULL; + + m_grassColor = eMinecraftColour_NOT_SET; + m_foliageColor = eMinecraftColour_NOT_SET; + m_waterColor = eMinecraftColour_NOT_SET; + + /* 4J - removing these so that we can consistently return newly created trees via getTreeFeature, and let the calling function be resposible for deleting the returned tree + normalTree = new TreeFeature(); + fancyTree = new BasicTree(); + birchTree = new BirchFeature(); + swampTree = new SwampTreeFeature(); + */ + + biomes[id] = this; + decorator = createDecorator(); + + friendlies.push_back(new MobSpawnerData(eTYPE_SHEEP, 12, 4, 4)); + friendlies.push_back(new MobSpawnerData(eTYPE_PIG, 10, 4, 4)); + friendlies_chicken.push_back(new MobSpawnerData(eTYPE_CHICKEN, 10, 4, 4)); // 4J - moved chickens to their own category + friendlies.push_back(new MobSpawnerData(eTYPE_COW, 8, 4, 4)); + + enemies.push_back(new MobSpawnerData(eTYPE_SPIDER, 10, 4, 4)); + enemies.push_back(new MobSpawnerData(eTYPE_ZOMBIE, 10, 4, 4)); + enemies.push_back(new MobSpawnerData(eTYPE_SKELETON, 10, 4, 4)); + enemies.push_back(new MobSpawnerData(eTYPE_CREEPER, 10, 4, 4)); + enemies.push_back(new MobSpawnerData(eTYPE_SLIME, 10, 4, 4)); + enemies.push_back(new MobSpawnerData(eTYPE_ENDERMAN, 1, 1, 4)); + + // wolves are added to forests and taigas + + waterFriendlies.push_back(new MobSpawnerData(eTYPE_SQUID, 10, 4, 4)); +} + +Biome::~Biome() +{ + if(decorator != NULL) delete decorator; +} + +BiomeDecorator *Biome::createDecorator() +{ + return new BiomeDecorator(this); +} + +// 4J Added +Biome *Biome::setLeafFoliageWaterSkyColor(eMinecraftColour grassColor, eMinecraftColour foliageColor, eMinecraftColour waterColour, eMinecraftColour skyColour) +{ + m_grassColor = grassColor; + m_foliageColor = foliageColor; + m_waterColor = waterColour; + m_skyColor = skyColour; + return this; +} + +Biome *Biome::setTemperatureAndDownfall(float temp, float downfall) +{ + this->temperature = temp; + this->downfall = downfall; + return this; +} + +Biome *Biome::setDepthAndScale(float depth, float scale) +{ + this->depth = depth; + this->scale = scale; + return this; +} + +Biome *Biome::setNoRain() +{ + _hasRain = false; + return this; +} + +Feature *Biome::getTreeFeature(Random *random) +{ + if (random->nextInt(10) == 0) + { + return new BasicTree(false); // 4J used to return member fancyTree, now returning newly created object so that caller can be consistently resposible for cleanup + } + return new TreeFeature(false); // 4J used to return member normalTree, now returning newly created object so that caller can be consistently resposible for cleanup +} + +Feature *Biome::getGrassFeature(Random *random) +{ + return new TallGrassFeature(Tile::tallgrass_Id, TallGrass::TALL_GRASS); +} + +Biome *Biome::setSnowCovered() +{ + this->snowCovered = true; + return this; +} + +Biome *Biome::setName(const wstring &name) +{ + this->m_name = name; + return this; +} + +Biome *Biome::setLeafColor(int leafColor) +{ + this->leafColor = leafColor; + return this; +} + +Biome *Biome::setColor(int color) +{ + this->color = color; + return this; +} + +int Biome::getSkyColor(float temp) +{ + //temp /= 3.0f; + //if (temp < -1) temp = -1; + //if (temp > 1) temp = 1; + //return Color::getHSBColor(224 / 360.0f - temp * 0.05f, 0.50f + temp * 0.1f, 1.0f).getRGB(); + + // 4J Stu - Load colour from texture pack + return Minecraft::GetInstance()->getColourTable()->getColor( m_skyColor ); +} + +vector *Biome::getMobs(MobCategory *category) +{ + if (category == MobCategory::monster) return &enemies; + if (category == MobCategory::creature) return &friendlies; + if (category == MobCategory::waterCreature) return &waterFriendlies; + if (category == MobCategory::creature_chicken) return &friendlies_chicken; + if (category == MobCategory::creature_wolf) return &friendlies_wolf; + if (category == MobCategory::creature_mushroomcow) return &friendlies_mushroomcow; + return NULL; +} + +bool Biome::hasSnow() +{ + // 4J - snowCovered flag removed as it wasn't being set by the game anymore - snow is now temperature dependent to match code in rain rendering, shouldFreeze functions etc. + if( !_hasRain ) return false; + + if( getTemperature() >= 0.15f ) return false; + + return true; +} + +bool Biome::hasRain() +{ + // 4J - snowCovered flag removed as it wasn't being set by the game anymore, replaced by call to hasSnow() + if( hasSnow() ) return false; +// if (snowCovered) return false; + return _hasRain; +} + +bool Biome::isHumid() +{ + return downfall > .85f; +} + +float Biome::getCreatureProbability() +{ + return 0.1f; +} + + int Biome::getDownfallInt() + { + return (int) (downfall * 65536); +} + +int Biome::getTemperatureInt() +{ + return (int) (temperature * 65536); +} + +// 4J - brought forward from 1.2.3 +float Biome::getDownfall() +{ + return downfall; +} + +// 4J - brought forward from 1.2.3 +float Biome::getTemperature() +{ + return temperature; +} + +void Biome::decorate(Level *level, Random *random, int xo, int zo) +{ + decorator->decorate(level, random, xo, zo); +} + +int Biome::getGrassColor() +{ + //double temp = Mth::clamp(getTemperature(), 0.0f, 1.0f); + //double rain = Mth::clamp(getDownfall(), 0.0f, 1.0f); + + //return GrassColor::get(temp, rain); + return Minecraft::GetInstance()->getColourTable()->getColor( m_grassColor ); +} + +int Biome::getFolageColor() +{ + //double temp = Mth::clamp(getTemperature(), 0.0f, 1.0f); + //double rain = Mth::clamp(getDownfall(), 0.0f, 1.0f); + + //return FoliageColor::get(temp, rain); + return Minecraft::GetInstance()->getColourTable()->getColor( m_foliageColor ); +} + +// 4J Added +int Biome::getWaterColor() +{ + return Minecraft::GetInstance()->getColourTable()->getColor( m_waterColor ); +} \ No newline at end of file diff --git a/Minecraft.World/Biome.h b/Minecraft.World/Biome.h new file mode 100644 index 00000000..1026bae6 --- /dev/null +++ b/Minecraft.World/Biome.h @@ -0,0 +1,152 @@ +#pragma once +using namespace std; + +#include "LevelSource.h" +#include "Mob.h" +#include "WeighedRandom.h" + +class Feature; +class MobCategory; +class BiomeDecorator; +class TreeFeature; +class BasicTree; +class BirchFeature; +class SwampTreeFeature; +class ChunkRebuildData; + +class Biome +{ + friend class ChunkRebuildData; +public: + // 4J JEV, replaces the static blocks. + static void staticCtor(); + + static Biome *biomes[256]; + + static Biome *ocean; + static Biome *plains; + static Biome *desert; + static Biome *extremeHills; + static Biome *forest; + static Biome *taiga; + static Biome *swampland; + static Biome *river; + static Biome *hell; + static Biome *sky; + static Biome *frozenOcean; + static Biome *frozenRiver; + static Biome *iceFlats; + static Biome *iceMountains; + static Biome *mushroomIsland; + static Biome *mushroomIslandShore ; + static Biome *beaches; + static Biome *desertHills; + static Biome *forestHills; + static Biome *taigaHills; + static Biome *smallerExtremeHills; + static Biome *jungle; + static Biome *jungleHills; + + static const int BIOME_COUNT = 23; // 4J Stu added + +public: + wstring m_name; + int color; + byte topMaterial; + byte material; + int leafColor; + float depth; + float scale; + float temperature; + float downfall; + //int waterColor; // 4J Stu removed + + BiomeDecorator *decorator; + + const int id; + + class MobSpawnerData : public WeighedRandomItem + { + public: + eINSTANCEOF mobClass; + int minCount; + int maxCount; + + MobSpawnerData(eINSTANCEOF mobClass, int probabilityWeight, int minCount, int maxCount) : WeighedRandomItem(probabilityWeight) + { + this->mobClass = mobClass; + this->minCount = minCount; + this->maxCount = maxCount; + } + }; + +protected: + vector enemies; + vector friendlies; + vector waterFriendlies; + vector friendlies_chicken; + vector friendlies_wolf; + vector friendlies_mushroomcow; + + Biome(int id); + ~Biome(); + + BiomeDecorator *createDecorator(); + +private: + Biome *setTemperatureAndDownfall(float temp, float downfall); + Biome *setDepthAndScale(float depth, float scale); + + bool snowCovered; + bool _hasRain; + + // 4J Added + eMinecraftColour m_grassColor; + eMinecraftColour m_foliageColor; + eMinecraftColour m_waterColor; + eMinecraftColour m_skyColor; + + Biome *setNoRain(); + +protected: + /* removing these so that we can consistently return newly created trees via getTreeFeature, and let the calling function be resposible for deleting the returned tree + TreeFeature *normalTree; + BasicTree *fancyTree; + BirchFeature *birchTree; + SwampTreeFeature *swampTree; + */ + +public: + virtual Feature *getTreeFeature(Random *random); + virtual Feature *getGrassFeature(Random *random); + +protected: + Biome *setSnowCovered(); + Biome *setName(const wstring &name); + Biome *setLeafColor(int leafColor); + Biome *setColor(int color); + + // 4J Added + Biome *setLeafFoliageWaterSkyColor(eMinecraftColour grassColor, eMinecraftColour foliageColor, eMinecraftColour waterColour, eMinecraftColour skyColour); + +public: + virtual int getSkyColor(float temp); + + vector *getMobs(MobCategory *category); + + virtual bool hasSnow(); + virtual bool hasRain(); + virtual bool isHumid(); + + virtual float getCreatureProbability(); + virtual int getDownfallInt(); + virtual int getTemperatureInt(); + virtual float getDownfall(); // 4J - brought forward from 1.2.3 + virtual float getTemperature(); // 4J - brought forward from 1.2.3 + + virtual void decorate(Level *level, Random *random, int xo, int zo); + + virtual int getGrassColor(); + virtual int getFolageColor(); + virtual int getWaterColor(); // 4J Added +}; \ No newline at end of file diff --git a/Minecraft.World/BiomeCache.cpp b/Minecraft.World/BiomeCache.cpp new file mode 100644 index 00000000..0d9c017c --- /dev/null +++ b/Minecraft.World/BiomeCache.cpp @@ -0,0 +1,165 @@ +#include "stdafx.h" + +#include "Biome.h" +#include "BiomeSource.h" +#include "BiomeCache.h" +#include "System.h" + +BiomeCache::Block::Block(int x, int z, BiomeCache *parent) +{ +// temps = floatArray(ZONE_SIZE * ZONE_SIZE, false); // MGH - added "no clear" flag to arrayWithLength +// downfall = floatArray(ZONE_SIZE * ZONE_SIZE, false); +// biomes = BiomeArray(ZONE_SIZE * ZONE_SIZE, false); + biomeIndices = byteArray(ZONE_SIZE * ZONE_SIZE, false); + + lastUse = 0; + this->x = x; + this->z = z; +// parent->source->getTemperatureBlock(temps, x << ZONE_SIZE_BITS, z << ZONE_SIZE_BITS, ZONE_SIZE, ZONE_SIZE); +// parent->source->getDownfallBlock(downfall, x << ZONE_SIZE_BITS, z << ZONE_SIZE_BITS, ZONE_SIZE, ZONE_SIZE); +// parent->source->getBiomeBlock(biomes, x << ZONE_SIZE_BITS, z << ZONE_SIZE_BITS, ZONE_SIZE, ZONE_SIZE, false); + parent->source->getBiomeIndexBlock(biomeIndices, x << ZONE_SIZE_BITS, z << ZONE_SIZE_BITS, ZONE_SIZE, ZONE_SIZE, false); + +} + +BiomeCache::Block::~Block() +{ +// delete [] temps.data; +// delete [] downfall.data; +// delete [] biomes.data; + delete [] biomeIndices.data; +} + +Biome *BiomeCache::Block::getBiome(int x, int z) +{ +// return biomes[(x & ZONE_SIZE_MASK) | ((z & ZONE_SIZE_MASK) << ZONE_SIZE_BITS)]; + + int biomeIndex = biomeIndices[(x & ZONE_SIZE_MASK) | ((z & ZONE_SIZE_MASK) << ZONE_SIZE_BITS)]; + return Biome::biomes[biomeIndex]; +} + +float BiomeCache::Block::getTemperature(int x, int z) +{ +// return temps[(x & ZONE_SIZE_MASK) | ((z & ZONE_SIZE_MASK) << ZONE_SIZE_BITS)]; + + int biomeIndex = biomeIndices[(x & ZONE_SIZE_MASK) | ((z & ZONE_SIZE_MASK) << ZONE_SIZE_BITS)]; + return Biome::biomes[biomeIndex]->getTemperature(); + +} + +float BiomeCache::Block::getDownfall(int x, int z) +{ +// return downfall[(x & ZONE_SIZE_MASK) | ((z & ZONE_SIZE_MASK) << ZONE_SIZE_BITS)]; + + int biomeIndex = biomeIndices[(x & ZONE_SIZE_MASK) | ((z & ZONE_SIZE_MASK) << ZONE_SIZE_BITS)]; + return Biome::biomes[biomeIndex]->getDownfall(); + +} + +BiomeCache::BiomeCache(BiomeSource *source) +{ + // 4J Initialisors + lastUpdateTime = 0; + + this->source = source; + + InitializeCriticalSection(&m_CS); + +} + +BiomeCache::~BiomeCache() +{ + // 4J Stu - Delete source? + // delete source; + + for(AUTO_VAR(it, all.begin()); it != all.end(); ++it) + { + delete (*it); + } + DeleteCriticalSection(&m_CS); +} + + +BiomeCache::Block *BiomeCache::getBlockAt(int x, int z) +{ + EnterCriticalSection(&m_CS); + x >>= ZONE_SIZE_BITS; + z >>= ZONE_SIZE_BITS; + __int64 slot = (((__int64) x) & 0xffffffffl) | ((((__int64) z) & 0xffffffffl) << 32l); + AUTO_VAR(it, cached.find(slot)); + Block *block = NULL; + if (it == cached.end()) + { + MemSect(48); + block = new Block(x, z, this); + cached[slot] = block; + all.push_back(block); + MemSect(0); + } + else + { + block = it->second; + } + block->lastUse = app.getAppTime(); + LeaveCriticalSection(&m_CS); + return block; +} + + +Biome *BiomeCache::getBiome(int x, int z) +{ + return getBlockAt(x, z)->getBiome(x, z); +} + +float BiomeCache::getTemperature(int x, int z) +{ + return getBlockAt(x, z)->getTemperature(x, z); +} + +float BiomeCache::getDownfall(int x, int z) +{ + return getBlockAt(x, z)->getDownfall(x, z); +} + +void BiomeCache::update() +{ + EnterCriticalSection(&m_CS); + __int64 now = app.getAppTime(); + __int64 utime = now - lastUpdateTime; + if (utime > DECAY_TIME / 4 || utime < 0) + { + lastUpdateTime = now; + + for (AUTO_VAR(it, all.begin()); it != all.end();) + { + Block *block = *it; + __int64 time = now - block->lastUse; + if (time > DECAY_TIME || time < 0) + { + it = all.erase(it); + __int64 slot = (((__int64) block->x) & 0xffffffffl) | ((((__int64) block->z) & 0xffffffffl) << 32l); + cached.erase(slot); + delete block; + } + else + { + ++it; + } + } + } + LeaveCriticalSection(&m_CS); +} + +BiomeArray BiomeCache::getBiomeBlockAt(int x, int z) +{ + byteArray indices = getBlockAt(x, z)->biomeIndices; + BiomeArray biomes(indices.length); + for(int i=0;ibiomeIndices; +} \ No newline at end of file diff --git a/Minecraft.World/BiomeCache.h b/Minecraft.World/BiomeCache.h new file mode 100644 index 00000000..bf509f54 --- /dev/null +++ b/Minecraft.World/BiomeCache.h @@ -0,0 +1,52 @@ +#pragma once +#include "..\Minecraft.World\JavaIntHash.h" + +class BiomeCache +{ +private: + static const int DECAY_TIME = 1000 * 30; + static const int ZONE_SIZE_BITS = 4; + static const int ZONE_SIZE = 1 << ZONE_SIZE_BITS; + static const int ZONE_SIZE_MASK = ZONE_SIZE - 1; + + const BiomeSource *source; + __int64 lastUpdateTime; + +public: + class Block + { + public: + // MGH - changed this to just cache biome indices, as we have direct access to the data if we know the index. +// floatArray temps; +// floatArray downfall; +// BiomeArray biomes; + byteArray biomeIndices; + int x, z; + __int64 lastUse; + + Block(int x, int z, BiomeCache *parent); + ~Block(); + Biome *getBiome(int x, int z); + float getTemperature(int x, int z); + float getDownfall(int x, int z); + }; + +private: + unordered_map<__int64,Block *,LongKeyHash,LongKeyEq> cached; // 4J - was LongHashMap + vector all; // was ArrayList + +public: + BiomeCache(BiomeSource *source); + ~BiomeCache(); + + Block *getBlockAt(int x, int z); + Biome *getBiome(int x, int z); + float getTemperature(int x, int z); + float getDownfall(int x, int z); + void update(); + BiomeArray getBiomeBlockAt(int x, int z); + byteArray getBiomeIndexBlockAt(int x, int z); + +private: + CRITICAL_SECTION m_CS; +}; \ No newline at end of file diff --git a/Minecraft.World/BiomeDecorator.cpp b/Minecraft.World/BiomeDecorator.cpp new file mode 100644 index 00000000..6ca7386c --- /dev/null +++ b/Minecraft.World/BiomeDecorator.cpp @@ -0,0 +1,328 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.level.biome.h" + +BiomeDecorator::BiomeDecorator(Biome *biome) +{ + _init(); + + // 4J inits + level = NULL; + random = NULL; + xo = 0; + zo = 0; + + this->biome = biome; +} + +void BiomeDecorator::decorate(Level *level, Random *random, int xo, int zo) +{ + if (this->level != NULL) + { + app.DebugPrintf("BiomeDecorator::decorate - Already decorating!!\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); + //throw new RuntimeException("Already decorating!!"); +#endif + } + this->level = level; + this->random = random; + this->xo = xo; + this->zo = zo; + + decorate(); + + this->level = NULL; + this->random = NULL; +} + + + +void BiomeDecorator::_init() +{ + clayFeature = new ClayFeature(4); + sandFeature = new SandFeature(7, Tile::sand_Id); + gravelFeature = new SandFeature(6, Tile::gravel_Id); + dirtOreFeature = new OreFeature(Tile::dirt_Id, 32); + gravelOreFeature = new OreFeature(Tile::gravel_Id, 32); + coalOreFeature = new OreFeature(Tile::coalOre_Id, 16); + ironOreFeature = new OreFeature(Tile::ironOre_Id, 8); + goldOreFeature = new OreFeature(Tile::goldOre_Id, 8); + redStoneOreFeature = new OreFeature(Tile::redStoneOre_Id, 7); + diamondOreFeature = new OreFeature(Tile::diamondOre_Id, 7); + lapisOreFeature = new OreFeature(Tile::lapisOre_Id, 6); + yellowFlowerFeature = new FlowerFeature(Tile::flower_Id); + roseFlowerFeature = new FlowerFeature(Tile::rose_Id); + brownMushroomFeature = new FlowerFeature(Tile::mushroom1_Id); + redMushroomFeature = new FlowerFeature(Tile::mushroom2_Id); + hugeMushroomFeature = new HugeMushroomFeature(); + reedsFeature = new ReedsFeature(); + cactusFeature = new CactusFeature(); + waterlilyFeature = new WaterlilyFeature(); + + waterlilyCount = 0; + treeCount = 0; + flowerCount = 2; + grassCount = 1; + deadBushCount = 0; + mushroomCount = 0; + reedsCount = 0; + cactusCount = 0; + gravelCount = 1; + sandCount = 3; + clayCount = 1; + hugeMushrooms = 0; + liquids = true; +} + + +void BiomeDecorator::decorate() +{ + PIXBeginNamedEvent(0,"Decorate ores"); + decorateOres(); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Decorate sand/clay/gravel"); + for (int i = 0; i < sandCount; i++) + { + int x = xo + random->nextInt(16) + 8; + int z = zo + random->nextInt(16) + 8; + sandFeature->place(level, random, x, level->getTopSolidBlock(x, z), z); + } + + for (int i = 0; i < clayCount; i++) + { + int x = xo + random->nextInt(16) + 8; + int z = zo + random->nextInt(16) + 8; + clayFeature->place(level, random, x, level->getTopSolidBlock(x, z), z); + } + + for (int i = 0; i < gravelCount; i++) + { + int x = xo + random->nextInt(16) + 8; + int z = zo + random->nextInt(16) + 8; + sandFeature->place(level, random, x, level->getTopSolidBlock(x, z), z); + } + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0, "Decorate forests"); + int forests = treeCount; + if (random->nextInt(10) == 0) forests += 1; + + for (int i = 0; i < forests; i++) + { + int x = xo + random->nextInt(16) + 8; + int z = zo + random->nextInt(16) + 8; + Feature *tree = biome->getTreeFeature(random); + tree->init(1, 1, 1); + tree->place(level, random, x, level->getHeightmap(x, z), z); + delete tree; + } + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Decorate mushrooms/flowers/grass"); + for (int i = 0; i < hugeMushrooms; i++) + { + int x = xo + random->nextInt(16) + 8; + int z = zo + random->nextInt(16) + 8; + hugeMushroomFeature->place(level, random, x, level->getHeightmap(x, z), z); + } + + for (int i = 0; i < flowerCount; i++) + { + int x = xo + random->nextInt(16) + 8; + int y = random->nextInt(Level::genDepth); + int z = zo + random->nextInt(16) + 8; + yellowFlowerFeature->place(level, random, x, y, z); + + if (random->nextInt(4) == 0) + { + x = xo + random->nextInt(16) + 8; + y = random->nextInt(Level::genDepth); + z = zo + random->nextInt(16) + 8; + roseFlowerFeature->place(level, random, x, y, z); + } + } + + for (int i = 0; i < grassCount; i++) + { + //int grassType = TallGrass::TALL_GRASS; + + int x = xo + random->nextInt(16) + 8; + int y = random->nextInt(Level::genDepth); + int z = zo + random->nextInt(16) + 8; + MemSect(50); + Feature *grassFeature = biome->getGrassFeature(random); + MemSect(0); + grassFeature->place(level, random, x, y, z); + delete grassFeature; + } + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Decorate bush/waterlily/mushroom/reeds/pumpkins/cactuses"); + + // 4J Stu - For some reason this was created each time round in the loop + // I assume there is a case where deadBushCount could be 0 + DeadBushFeature *deadBushFeature = NULL; + if(deadBushCount > 0) deadBushFeature = new DeadBushFeature(Tile::deadBush_Id); + for (int i = 0; i < deadBushCount; i++) + { + int x = xo + random->nextInt(16) + 8; + int y = random->nextInt(Level::genDepth); + int z = zo + random->nextInt(16) + 8; + //new DeadBushFeature(Tile::deadBush_Id)->place(level, random, x, y, z); + deadBushFeature->place(level, random, x, y, z); + } + if(deadBushFeature != NULL)delete deadBushFeature; + + for (int i = 0; i < waterlilyCount; i++) + { + int x = xo + random->nextInt(16) + 8; + int z = zo + random->nextInt(16) + 8; + int y = random->nextInt(Level::genDepth); + while (y > 0 && level->getTile(x, y - 1, z) == 0) + y--; + waterlilyFeature->place(level, random, x, y, z); + } + + for (int i = 0; i < mushroomCount; i++) + { + if (random->nextInt(4) == 0) + { + int x = xo + random->nextInt(16) + 8; + int z = zo + random->nextInt(16) + 8; + int y = level->getHeightmap(x, z); + brownMushroomFeature->place(level, random, x, y, z); + } + + if (random->nextInt(8) == 0) + { + int x = xo + random->nextInt(16) + 8; + int z = zo + random->nextInt(16) + 8; + int y = random->nextInt(Level::genDepth); + redMushroomFeature->place(level, random, x, y, z); + } + } + + if (random->nextInt(4) == 0) + { + int x = xo + random->nextInt(16) + 8; + int y = random->nextInt(Level::genDepth); + int z = zo + random->nextInt(16) + 8; + brownMushroomFeature->place(level, random, x, y, z); + } + + if (random->nextInt(8) == 0) + { + int x = xo + random->nextInt(16) + 8; + int y = random->nextInt(Level::genDepth); + int z = zo + random->nextInt(16) + 8; + redMushroomFeature->place(level, random, x, y, z); + } + + for (int i = 0; i < reedsCount; i++) + { + int x = xo + random->nextInt(16) + 8; + int z = zo + random->nextInt(16) + 8; + int y = random->nextInt(Level::genDepth); + reedsFeature->place(level, random, x, y, z); + } + + for (int i = 0; i < 10; i++) + { + int x = xo + random->nextInt(16) + 8; + int y = random->nextInt(Level::genDepth); + int z = zo + random->nextInt(16) + 8; + reedsFeature->place(level, random, x, y, z); + } + + if (random->nextInt(32) == 0) + { + int x = xo + random->nextInt(16) + 8; + int y = random->nextInt(Level::genDepth); + int z = zo + random->nextInt(16) + 8; + PumpkinFeature *pumpkinFeature = new PumpkinFeature(); + pumpkinFeature->place(level, random, x, y, z); + delete pumpkinFeature; + } + + for (int i = 0; i < cactusCount; i++) + { + int x = xo + random->nextInt(16) + 8; + int y = random->nextInt(Level::genDepth); + int z = zo + random->nextInt(16) + 8; + cactusFeature->place(level, random, x, y, z); + } + + + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Decorate liquids"); + + if( liquids ) + { + // 4J Stu - For some reason this was created each time round in the loop + SpringFeature *waterSpringFeature = new SpringFeature(Tile::water_Id); + for (int i = 0; i < 50; i++) + { + int x = xo + random->nextInt(16) + 8; + int y = random->nextInt(random->nextInt(Level::genDepth - 8) + 8); + int z = zo + random->nextInt(16) + 8; + waterSpringFeature->place(level, random, x, y, z); + } + delete waterSpringFeature; + + // 4J Stu - For some reason this was created each time round in the loop + SpringFeature *lavaSpringFeature = new SpringFeature(Tile::lava_Id); + for (int i = 0; i < 20; i++) + { + int x = xo + random->nextInt(16) + 8; + int y = random->nextInt(random->nextInt(random->nextInt(Level::genDepth - 16) + 8) + 8); + int z = zo + random->nextInt(16) + 8; + lavaSpringFeature->place(level, random, x, y, z); + } + delete lavaSpringFeature; + } + PIXEndNamedEvent(); +} + +void BiomeDecorator::decorate(int count, Feature *feature) +{ + decorateDepthSpan(count, feature, 0, Level::genDepth); +} + +void BiomeDecorator::decorateDepthSpan(int count, Feature *feature, int y0, int y1) +{ + for (int i = 0; i < count; i++) + { + int x = xo + random->nextInt(16); + int y = random->nextInt(y1 - y0) + y0; + int z = zo + random->nextInt(16); + feature->place(level, random, x, y, z); + } +} + +void BiomeDecorator::decorateDepthAverage(int count, Feature *feature, int yMid, int ySpan) +{ + for (int i = 0; i < count; i++) + { + int x = xo + random->nextInt(16); + int y = random->nextInt(ySpan) + random->nextInt(ySpan) + (yMid - ySpan); + int z = zo + random->nextInt(16); + feature->place(level, random, x, y, z); + } +} + +void BiomeDecorator::decorateOres() +{ + level->setInstaTick(true); // 4J - optimisation + decorateDepthSpan(20, dirtOreFeature, 0, Level::genDepth); + decorateDepthSpan(10, gravelOreFeature, 0, Level::genDepth); + decorateDepthSpan(20, coalOreFeature, 0, Level::genDepth); + decorateDepthSpan(20, ironOreFeature, 0, Level::genDepth / 2); + decorateDepthSpan(2, goldOreFeature, 0, Level::genDepth / 4); + decorateDepthSpan(8, redStoneOreFeature, 0, Level::genDepth / 8); + decorateDepthSpan(1, diamondOreFeature, 0, Level::genDepth / 8); + decorateDepthAverage(1, lapisOreFeature, Level::genDepth / 8, Level::genDepth / 8); + level->setInstaTick(false); +} diff --git a/Minecraft.World/BiomeDecorator.h b/Minecraft.World/BiomeDecorator.h new file mode 100644 index 00000000..01560045 --- /dev/null +++ b/Minecraft.World/BiomeDecorator.h @@ -0,0 +1,75 @@ +#pragma once + +class Level; +class Random; +class Biome; +class Feature; + +class BiomeDecorator +{ + friend class DesertBiome; + friend class ForestBiome; + friend class PlainsBiome; + friend class SwampBiome; + friend class TaigaBiome; + friend class MushroomIslandBiome; + friend class BeachBiome; + friend class JungleBiome; +protected: + Level *level; + Random *random; + int xo; + int zo; + Biome *biome; + +public: + BiomeDecorator(Biome *biome); + + void decorate(Level *level, Random *random, int xo, int zo); + +protected: + Feature *clayFeature; + Feature *sandFeature; + Feature *gravelFeature; + Feature *dirtOreFeature; + Feature *gravelOreFeature; + Feature *coalOreFeature; + Feature *ironOreFeature; + Feature *goldOreFeature; + Feature *redStoneOreFeature; + Feature *diamondOreFeature; + Feature *lapisOreFeature; + Feature *yellowFlowerFeature; + Feature *roseFlowerFeature; + Feature *brownMushroomFeature; + Feature *redMushroomFeature; + Feature *hugeMushroomFeature; + Feature *reedsFeature; + Feature *cactusFeature; + Feature *waterlilyFeature; + + int waterlilyCount; + int treeCount; + int flowerCount; + int grassCount; + int deadBushCount; + int mushroomCount; + int reedsCount; + int cactusCount; + int gravelCount; + int sandCount; + int clayCount; + int hugeMushrooms; + bool liquids; + + void _init(); + +protected: + virtual void decorate(); + + + void decorate(int count, Feature *feature); + void decorateDepthSpan(int count, Feature *feature, int y0, int y1); + void decorateDepthAverage(int count, Feature *feature, int yMid, int ySpan); + void decorateOres(); +}; \ No newline at end of file diff --git a/Minecraft.World/BiomeInitLayer.cpp b/Minecraft.World/BiomeInitLayer.cpp new file mode 100644 index 00000000..ead63eee --- /dev/null +++ b/Minecraft.World/BiomeInitLayer.cpp @@ -0,0 +1,79 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.newbiome.layer.h" +#include "net.minecraft.world.level.h" +#include "BiomeInitLayer.h" + +BiomeInitLayer::BiomeInitLayer(__int64 seed, shared_ptrparent, LevelType *levelType) : Layer(seed) +{ + this->parent = parent; + + + if(levelType == LevelType::lvl_normal_1_1) + { + startBiomes = BiomeArray(6); + startBiomes[0] = Biome::desert; + startBiomes[1] = Biome::forest; + startBiomes[2] = Biome::extremeHills; + startBiomes[3] = Biome::swampland; + startBiomes[4] = Biome::plains; + startBiomes[5] = Biome::taiga; + } + else + { + startBiomes = BiomeArray(7); + startBiomes[0] = Biome::desert; + startBiomes[1] = Biome::forest; + startBiomes[2] = Biome::extremeHills; + startBiomes[3] = Biome::swampland; + startBiomes[4] = Biome::plains; + startBiomes[5] = Biome::taiga; + startBiomes[6] = Biome::jungle; + } +} + +BiomeInitLayer::~BiomeInitLayer() +{ + delete [] startBiomes.data; +} + +intArray BiomeInitLayer::getArea(int xo, int yo, int w, int h) +{ + intArray b = parent->getArea(xo, yo, w, h); + + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + initRandom(x + xo, y + yo); + int old = b[x + y * w]; + if (old == 0) + { + result[x + y * w] = 0; + } + else if (old == Biome::mushroomIsland->id) + { + result[x + y * w] = old; + } + else if (old == 1) + { + result[x + y * w] = startBiomes[nextRandom(startBiomes.length)]->id; + } + else + { + int isTaiga = startBiomes[nextRandom(startBiomes.length)]->id; + if (isTaiga == Biome::taiga->id) + { + result[x + y * w] = isTaiga; + } + else + { + result[x + y * w] = Biome::iceFlats->id; + } + } + } + } + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/BiomeInitLayer.h b/Minecraft.World/BiomeInitLayer.h new file mode 100644 index 00000000..e645a379 --- /dev/null +++ b/Minecraft.World/BiomeInitLayer.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Layer.h" + +class LevelType; + +class BiomeInitLayer : public Layer +{ +private: + BiomeArray startBiomes; + +public: + BiomeInitLayer(__int64 seed, shared_ptr parent, LevelType *levelType); + virtual ~BiomeInitLayer(); + intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/BiomeOverrideLayer.cpp b/Minecraft.World/BiomeOverrideLayer.cpp new file mode 100644 index 00000000..a44295dc --- /dev/null +++ b/Minecraft.World/BiomeOverrideLayer.cpp @@ -0,0 +1,81 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.newbiome.layer.h" +#include "net.minecraft.world.level.h" +#include "BiomeOverrideLayer.h" + + +BiomeOverrideLayer::BiomeOverrideLayer(int seedMixup) : Layer(seedMixup) +{ + m_biomeOverride = byteArray( width * height ); + +#ifdef _UNICODE + wstring path = L"GAME:\\GameRules\\biomemap.bin"; + HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#else +#ifdef _WINDOWS64 + string path = "GameRules\\biomemap.bin"; +#else + string path = "GAME:\\GameRules\\biomemap.bin"; +#endif + HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); +#endif + if( file == INVALID_HANDLE_VALUE ) + { + DWORD error = GetLastError(); + //assert(false); + app.DebugPrintf("Biome override not found, using plains as default\n"); + + memset(m_biomeOverride.data,Biome::plains->id,m_biomeOverride.length); + } + else + { + +#ifdef _DURANGO + __debugbreak(); // TODO + DWORD bytesRead,dwFileSize = 0; +#else + DWORD bytesRead,dwFileSize = GetFileSize(file,NULL); +#endif + if(dwFileSize > m_biomeOverride.length) + { + app.DebugPrintf("Biomemap binary is too large!!\n"); + __debugbreak(); + } + BOOL bSuccess = ReadFile(file,m_biomeOverride.data,dwFileSize,&bytesRead,NULL); + + if(bSuccess==FALSE) + { + app.FatalLoadError(); + } + + CloseHandle(file); + } +} + +intArray BiomeOverrideLayer::getArea(int xo, int yo, int w, int h) +{ + intArray result = IntCache::allocate(w * h); + + int xOrigin = xo + width/2; + int yOrigin = yo + height/2; + if(xOrigin < 0 ) xOrigin = 0; + if(xOrigin >= width) xOrigin = width - 1; + if(yOrigin < 0 ) yOrigin = 0; + if(yOrigin >= height) yOrigin = height - 1; + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + int curX = xOrigin + x; + int curY = yOrigin + y; + if(curX >= width) curX = width - 1; + if(curY >= height) curY = height - 1; + int index = curX + curY * width; + + unsigned char headerValue = m_biomeOverride[index]; + result[x + y * w] = headerValue; + } + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/BiomeOverrideLayer.h b/Minecraft.World/BiomeOverrideLayer.h new file mode 100644 index 00000000..cfa689ef --- /dev/null +++ b/Minecraft.World/BiomeOverrideLayer.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Layer.h" + +class LevelType; + +class BiomeOverrideLayer : public Layer +{ +private: + static const unsigned int width = 216; + static const unsigned int height = 216; + + byteArray m_biomeOverride; + +public: + BiomeOverrideLayer(int seedMixup); + intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/BiomeSource.cpp b/Minecraft.World/BiomeSource.cpp new file mode 100644 index 00000000..b16efd31 --- /dev/null +++ b/Minecraft.World/BiomeSource.cpp @@ -0,0 +1,638 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.storage.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.newbiome.layer.h" +#include "System.h" +#include "BiomeSource.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\ProgressRenderer.h" + +// 4J - removal of separate temperature & downfall layers brought forward from 1.2.3 +void BiomeSource::_init() +{ + layer = nullptr; + zoomedLayer = nullptr; + + cache = new BiomeCache(this); + + playerSpawnBiomes.push_back(Biome::forest); + playerSpawnBiomes.push_back(Biome::taiga); + // 4J-PB - Moving forward plains as a spawnable biome (mainly for the Superflat world) + playerSpawnBiomes.push_back(Biome::plains); + playerSpawnBiomes.push_back(Biome::taigaHills); + playerSpawnBiomes.push_back(Biome::forestHills); + playerSpawnBiomes.push_back(Biome::jungle); + playerSpawnBiomes.push_back(Biome::jungleHills); +} + +void BiomeSource::_init(__int64 seed, LevelType *generator) +{ + _init(); + + LayerArray layers = Layer::getDefaultLayers(seed, generator); + layer = layers[0]; + zoomedLayer = layers[1]; + + delete [] layers.data; +} + +BiomeSource::BiomeSource() +{ + _init(); +} + +// 4J added +BiomeSource::BiomeSource(__int64 seed, LevelType *generator) +{ + _init(seed, generator); +} + +// 4J - removal of separate temperature & downfall layers brought forward from 1.2.3 +BiomeSource::BiomeSource(Level *level) +{ + _init(level->getSeed(), level->getLevelData()->getGenerator()); +} + +BiomeSource::~BiomeSource() +{ + delete cache; +} + +Biome *BiomeSource::getBiome(ChunkPos *cp) +{ + return getBiome(cp->x << 4, cp->z << 4); +} + +Biome *BiomeSource::getBiome(int x, int z) +{ + return cache->getBiome(x, z); +} + +float BiomeSource::getDownfall(int x, int z) const +{ + return cache->getDownfall(x, z); +} + +// 4J - note that caller is responsible for deleting returned array. temperatures array is for output only. +floatArray BiomeSource::getDownfallBlock(int x, int z, int w, int h) const +{ + floatArray downfalls; + getDownfallBlock(downfalls, x, z, w, h); + return downfalls; +} + +// 4J - note that caller is responsible for deleting returned array. temperatures array is for output only. +// 4J - removal of separate temperature & downfall layers brought forward from 1.2.3 +void BiomeSource::getDownfallBlock(floatArray &downfalls, int x, int z, int w, int h) const +{ + IntCache::releaseAll(); + //if (downfalls == NULL || downfalls->length < w * h) + if (downfalls.data == NULL || downfalls.length < w * h) + { + if(downfalls.data != NULL) delete [] downfalls.data; + downfalls = floatArray(w * h); + } + + intArray result = zoomedLayer->getArea(x, z, w, h); + for (int i = 0; i < w * h; i++) + { + float d = (float) Biome::biomes[result[i]]->getDownfallInt() / 65536.0f; + if (d > 1) d = 1; + downfalls[i] = d; + } +} + +BiomeCache::Block *BiomeSource::getBlockAt(int x, int y) +{ + return cache->getBlockAt(x, y); +} + +float BiomeSource::getTemperature(int x, int y, int z) const +{ + return scaleTemp(cache->getTemperature(x, z), y); +} + +// 4J - brought forward from 1.2.3 +float BiomeSource::scaleTemp(float temp, int y ) const +{ + return temp; +} + +floatArray BiomeSource::getTemperatureBlock(int x, int z, int w, int h) const +{ + floatArray temperatures; + getTemperatureBlock(temperatures, x, z, w, h); + return temperatures; +} + +// 4J - note that caller is responsible for deleting returned array. temperatures array is for output only. +// 4J - removal of separate temperature & downfall layers brought forward from 1.2.3 +void BiomeSource::getTemperatureBlock(floatArray& temperatures, int x, int z, int w, int h) const +{ + IntCache::releaseAll(); + //if (temperatures == null || temperatures.length < w * h) { + if (temperatures.data == NULL || temperatures.length < w * h) + { + if( temperatures.data != NULL ) delete [] temperatures.data; + temperatures = floatArray(w * h); + } + + intArray result = zoomedLayer->getArea(x, z, w, h); + for (int i = 0; i < w * h; i++) + { + float t = (float) Biome::biomes[result[i]]->getTemperatureInt() / 65536.0f; + if (t > 1) t = 1; + temperatures[i] = t; + } +} + +BiomeArray BiomeSource::getRawBiomeBlock(int x, int z, int w, int h) const +{ + BiomeArray biomes; + getRawBiomeBlock(biomes, x, z, w, h); + return biomes; +} + +// 4J added +void BiomeSource::getRawBiomeIndices(intArray &biomes, int x, int z, int w, int h) const +{ + IntCache::releaseAll(); + + intArray result = layer->getArea(x, z, w, h); + for (int i = 0; i < w * h; i++) + { + biomes[i] = result[i]; + } +} + +void BiomeSource::getRawBiomeBlock(BiomeArray &biomes, int x, int z, int w, int h) const +{ + IntCache::releaseAll(); + //if (biomes == null || biomes.length < w * h) + if (biomes.data == NULL || biomes.length < w * h) + { + if(biomes.data != NULL) delete [] biomes.data; + biomes = BiomeArray(w * h); + } + + intArray result = layer->getArea(x, z, w, h); + for (int i = 0; i < w * h; i++) + { + biomes[i] = Biome::biomes[result[i]]; +#ifndef _CONTENT_PACKAGE + if(biomes[i] == NULL) + { + app.DebugPrintf("Tried to assign null biome %d\n", result[i]); + __debugbreak(); + } +#endif + } +} + + + +BiomeArray BiomeSource::getBiomeBlock(int x, int z, int w, int h) const +{ + if (w == 16 && h == 16 && (x & 0xf) == 0 && (z & 0xf) == 0) + { + return cache->getBiomeBlockAt(x, z); + } + BiomeArray biomes; + getBiomeBlock(biomes, x, z, w, h, true); + return biomes; +} + +// 4J - caller is responsible for deleting biomes array +void BiomeSource::getBiomeBlock(BiomeArray& biomes, int x, int z, int w, int h, bool useCache) const +{ + IntCache::releaseAll(); + //if (biomes == null || biomes.length < w * h) + if (biomes.data == NULL || biomes.length < w * h) + { + if(biomes.data != NULL) delete [] biomes.data; + biomes = BiomeArray(w * h); + } + + if (useCache && w == 16 && h == 16 && (x & 0xf) == 0 && (z & 0xf) == 0) + { + BiomeArray tmp = cache->getBiomeBlockAt(x, z); + System::arraycopy(tmp, 0, &biomes, 0, w * h); + delete tmp.data; // MGH - added, the caching creates this array from the indices now. + //return biomes; + } + + intArray result = zoomedLayer->getArea(x, z, w, h); + for (int i = 0; i < w * h; i++) + { + biomes[i] = Biome::biomes[result[i]]; + } +} + + + + +byteArray BiomeSource::getBiomeIndexBlock(int x, int z, int w, int h) const +{ + if (w == 16 && h == 16 && (x & 0xf) == 0 && (z & 0xf) == 0) + { + return cache->getBiomeIndexBlockAt(x, z); + } + byteArray biomeIndices; + getBiomeIndexBlock(biomeIndices, x, z, w, h, true); + return biomeIndices; +} + +// 4J - caller is responsible for deleting biomes array +void BiomeSource::getBiomeIndexBlock(byteArray& biomeIndices, int x, int z, int w, int h, bool useCache) const +{ + IntCache::releaseAll(); + //if (biomes == null || biomes.length < w * h) + if (biomeIndices.data == NULL || biomeIndices.length < w * h) + { + if(biomeIndices.data != NULL) delete [] biomeIndices.data; + biomeIndices = byteArray(w * h); + } + + if (useCache && w == 16 && h == 16 && (x & 0xf) == 0 && (z & 0xf) == 0) + { + byteArray tmp = cache->getBiomeIndexBlockAt(x, z); + System::arraycopy(tmp, 0, &biomeIndices, 0, w * h); + //return biomes; + } + + intArray result = zoomedLayer->getArea(x, z, w, h); + for (int i = 0; i < w * h; i++) + { + biomeIndices[i] = (byte)result[i]; + } +} + +/** +* Checks if an area around a block contains only the specified biomes. +* Useful for placing elements like towns. +* +* This is a bit of a rough check, to make it as fast as possible. To ensure +* NO other biomes, add a margin of at least four blocks to the radius +*/ +bool BiomeSource::containsOnly(int x, int z, int r, vector allowed) +{ + int x0 = ((x - r) >> 2); + int z0 = ((z - r) >> 2); + int x1 = ((x + r) >> 2); + int z1 = ((z + r) >> 2); + + int w = x1 - x0 + 1; + int h = z1 - z0 + 1; + + intArray biomes = layer->getArea(x0, z0, w, h); + for (int i = 0; i < w * h; i++) + { + Biome *b = Biome::biomes[biomes[i]]; + if (find(allowed.begin(), allowed.end(), b) == allowed.end()) return false; + } + + return true; +} + +/** +* Checks if an area around a block contains only the specified biome. +* Useful for placing elements like towns. +* +* This is a bit of a rough check, to make it as fast as possible. To ensure +* NO other biomes, add a margin of at least four blocks to the radius +*/ +bool BiomeSource::containsOnly(int x, int z, int r, Biome *allowed) +{ + int x0 = ((x - r) >> 2); + int z0 = ((z - r) >> 2); + int x1 = ((x + r) >> 2); + int z1 = ((z + r) >> 2); + + int w = x1 - x0; + int h = z1 - z0; + int biomesCount = w*h; + intArray biomes = layer->getArea(x0, z0, w, h); + for (unsigned int i = 0; i < biomesCount; i++) + { + Biome *b = Biome::biomes[biomes[i]]; + if (allowed != b) return false; + } + + return true; +} + +/** +* Finds the specified biome within the radius. This will return a random +* position if several are found. This test is fairly rough. +* +* Returns null if the biome wasn't found +*/ +TilePos *BiomeSource::findBiome(int x, int z, int r, Biome *toFind, Random *random) +{ + int x0 = ((x - r) >> 2); + int z0 = ((z - r) >> 2); + int x1 = ((x + r) >> 2); + int z1 = ((z + r) >> 2); + + int w = x1 - x0 + 1; + int h = z1 - z0 + 1; + intArray biomes = layer->getArea(x0, z0, w, h); + TilePos *res = NULL; + int found = 0; + int biomesCount = w*h; + for (unsigned int i = 0; i < biomesCount; i++) + { + int xx = x0 + i % w; + int zz = z0 + i / w; + Biome *b = Biome::biomes[biomes[i]]; + if (b == toFind) + { + if (res == NULL || random->nextInt(found + 1) == 0) + { + res = new TilePos(xx, 0, zz); + found++; + } + } + } + + return res; +} + +/** +* Finds one of the specified biomes within the radius. This will return a +* random position if several are found. This test is fairly rough. +* +* Returns null if the biome wasn't found +*/ +TilePos *BiomeSource::findBiome(int x, int z, int r, vector allowed, Random *random) +{ + int x0 = ((x - r) >> 2); + int z0 = ((z - r) >> 2); + int x1 = ((x + r) >> 2); + int z1 = ((z + r) >> 2); + + int w = x1 - x0 + 1; + int h = z1 - z0 + 1; + MemSect(50); + intArray biomes = layer->getArea(x0, z0, w, h); + TilePos *res = NULL; + int found = 0; + int biomesCount = w*h; + for (unsigned int i = 0; i < biomesCount; i++) + { + int xx = (x0 + i % w) << 2; + int zz = (z0 + i / w) << 2; + Biome *b = Biome::biomes[biomes[i]]; + if (find(allowed.begin(), allowed.end(), b) != allowed.end()) + { + if (res == NULL || random->nextInt(found + 1) == 0) + { + delete res; + res = new TilePos(xx, 0, zz); + found++; + } + } + } + MemSect(0); + + return res; +} + +void BiomeSource::update() +{ + cache->update(); +} + +//#define DEBUG_SEEDS 50 + +// 4J added - find a seed for this biomesource that matches certain criteria +#ifdef __PSVITA__ +__int64 BiomeSource::findSeed(LevelType *generator, bool* pServerRunning) // MGH - added pRunning, so we can early out of this on Vita as it can take up to 60 secs +#else +__int64 BiomeSource::findSeed(LevelType *generator) +#endif +{ + + __int64 bestSeed = 0; + + ProgressRenderer *mcprogress = Minecraft::GetInstance()->progressRenderer; + mcprogress->progressStage(IDS_PROGRESS_NEW_WORLD_SEED); + +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<nextLong(); + BiomeSource *biomeSource = new BiomeSource(seed,generator); + + biomeSource->getRawBiomeIndices(indices, biomeOffset, biomeOffset, biomeWidth, biomeWidth); + getFracs(indices, toCompare); + + matchFound = getIsMatch( toCompare ); + + if( matchFound ) bestSeed = seed; + + delete biomeSource; + tryCount++; + + mcprogress->progressStagePercentage( tryCount % 100 ); +#ifdef __PSVITA__ + } while (!matchFound && *pServerRunning); +#else + } while (!matchFound); +#endif + + // Clean up + delete pr; + delete indices.data; + +#ifdef DEBUG_SEEDS + app.DebugPrintf("%d: %d tries taken, seed used is %lld\n", k, tryCount, bestSeed); + + BiomeSource *biomeSource = new BiomeSource(bestSeed); + BiomeArray biomes = biomeSource->getBiomeBlock(-27 * 16, -27 * 16, 54 * 16, 54 * 16); + + unsigned int *pixels = new unsigned int[54 * 16 * 54 * 16]; + for(int i = 0; i < 54 * 16 * 54 * 16; i++ ) + { + int id = biomes[i]->id; + + // Create following colours: + // 0 ocean 0000 black + // 1 plains 0001 pastel cyan + // 2 desert 0010 green + // 3 extreme hills 0011 yellow + // 4 forest 0100 blue + // 5 taiga 0101 magenta + // 6 swamps 0110 cyan + // 7 river 0111 white + // 8 hell 1000 grey + // 9 end biome 1001 white + // 10 frozen ocean 1010 pastel green + // 11 frozen river 1011 pastel yellow + // 12 ice flats 1100 pastel blue + // 13 ice mountains 1101 pastel magenta + // 14 mushroom island 1110 red + // 15 mushroom shore 1111 pastel red + + if( id == 1 ) id = 14; + else if ( id == 14 ) id = 1; + else if( id == 9 ) id = 15; + else if( id == 15 ) id = 9; + pixels[i] = 0xff000000; + if( id & 1 ) pixels[i] |= 0x00ff0000; + if( id & 2 ) pixels[i] |= 0x0000ff00; + if( id & 4 ) pixels[i] |= 0x000000ff; + if( id & 8 ) pixels[i] |= 0x00808080; + } + D3DXIMAGE_INFO srcInfo; + srcInfo.Format = D3DFMT_LIN_A8R8G8B8; + srcInfo.ImageFileFormat = D3DXIFF_BMP; + srcInfo.Width = 54 * 16; + srcInfo.Height = 54 * 16; + + char buf[256]; + sprintf(buf,"GAME:\\BiomeTest%d.bmp",k); + RenderManager.SaveTextureData(buf, &srcInfo, (int *)pixels); + + delete [] pixels; + delete biomes.data; + delete biomeSource; +#endif + } + } + + return bestSeed; +} + +// 4J added - get the fractional amounts of each biome type in the given indices +void BiomeSource::getFracs(intArray indices, float *fracs) +{ + for( int i = 0; i < Biome::BIOME_COUNT; i++ ) + { + fracs[i] = 0.0f; + } + + for( int i = 0; i < indices.length; i++ ) + { + fracs[indices[i]] += 1.0f; + } + + for( int i = 0; i < Biome::BIOME_COUNT; i++ ) + { + fracs[i] /= (float)(indices.length); + } +} + + + +// 4J added - determine if this particular set of fractional amounts of biome types matches are requirements +bool BiomeSource::getIsMatch(float *frac) +{ + // A true for a particular biome type here marks it as one that *has* to be present + static const bool critical[Biome::BIOME_COUNT] = { + true, // ocean + true, // plains + true, // desert + false, // extreme hills + true, // forest + true, // taiga + true, // swamps + false, // river + false, // hell + false, // end biome + false, // frozen ocean + false, // frozen river + false, // ice flats + false, // ice mountains + true, // mushroom island / shore + false, // mushroom shore (combined with above) + false, // beach + false, // desert hills (combined with desert) + false, // forest hills (combined with forest) + false, // taiga hills (combined with taga) + false, // small extreme hills + true, // jungle + false, // jungle hills (combined with jungle) + }; + + + // Don't want more than 15% ocean + if( frac[0] > 0.15f ) + { + return false; + } + + // Consider mushroom shore & islands as the same by finding max + frac[14] = ( ( frac[15] > frac[14] ) ? frac[15] : frac[14] ); + + // Merge desert and desert hills + frac[2] = ( ( frac[17] > frac[2] ) ? frac[17] : frac[2] ); + + // Merge forest and forest hills + frac[4] = ( ( frac[18] > frac[4] ) ? frac[18] : frac[4] ); + + // Merge taiga and taiga hills + frac[5] = ( ( frac[19] > frac[5] ) ? frac[19] : frac[5] ); + + // Merge jungle and jungle hills + frac[21] = ( ( frac[22] > frac[21] ) ? frac[22] : frac[21] ); + + // Loop through all biome types, and: + // (1) count them + // (2) give up if one of the critical ones is missing + + int typeCount = 0; + for( int i = 0; i < Biome::BIOME_COUNT; i++ ) + { + // We want to skip some where we have merged with another type + if(i == 15 || i == 17 || i == 18 || i == 19 || i == 22) continue; + + // Consider 0.1% as being "present" - this equates an area of about 3 chunks + if( frac[i] > 0.001f ) + { + typeCount++; + } + else + { + // If a critical biome is missing, just give up + if( critical[i] ) + { + return false; + } + } + } + + // Consider as suitable if we've got all the critical ones, and in total 9 or more - currently there's 8 critical so this just forces at least 1 more others + return ( typeCount >= 9 ); +} \ No newline at end of file diff --git a/Minecraft.World/BiomeSource.h b/Minecraft.World/BiomeSource.h new file mode 100644 index 00000000..bef09db0 --- /dev/null +++ b/Minecraft.World/BiomeSource.h @@ -0,0 +1,106 @@ +#pragma once +#include "Biome.h" +#include "BiomeSource.h" +#include "BiomeCache.h" +#include "net.minecraft.world.level.levelgen.synth.h" + +class ChunkPos; +class Level; +class Layer; +class TilePos; +class LevelType; + +class BiomeSource +{ +private: + shared_ptr layer; + shared_ptr zoomedLayer; +public: + static const int CACHE_DIAMETER = 256; + +private: + BiomeCache *cache; + + vector playerSpawnBiomes; + +protected: + void _init(); + void _init(__int64 seed, LevelType *generator); + BiomeSource(); + +public: + BiomeSource(__int64 seed, LevelType *generator); + BiomeSource(Level *level); +private: + static bool getIsMatch(float *frac); // 4J added + static void getFracs(intArray indices, float *fracs); // 4J added +public: +#ifdef __PSVITA__ + static __int64 findSeed(LevelType *generator, bool* pServerRunning); // MGH - added pRunning, so we can early out of this on Vita as it can take up to 60 secs // 4J added +#else + static __int64 findSeed(LevelType *generator); // 4J added +#endif + ~BiomeSource(); + +public: + vector getPlayerSpawnBiomes() { return playerSpawnBiomes; } + virtual Biome *getBiome(ChunkPos *cp); + virtual Biome *getBiome(int x, int z); + + // 4J - changed the interface for these methods, mainly for thread safety + virtual float getDownfall(int x, int z) const; + virtual floatArray getDownfallBlock(int x, int z, int w, int h) const; + virtual void getDownfallBlock(floatArray &downfalls, int x, int z, int w, int h) const; + + // 4J - changed the interface for these methods, mainly for thread safety + virtual BiomeCache::Block *getBlockAt(int x, int y); + virtual float getTemperature(int x, int y, int z) const; + float scaleTemp(float temp, int y ) const; // 4J - brought forward from 1.2.3 + virtual floatArray getTemperatureBlock(int x, int z, int w, int h) const; + virtual void getTemperatureBlock(floatArray& temperatures, int x, int z, int w, int h) const; + + virtual BiomeArray getRawBiomeBlock(int x, int z, int w, int h) const; + virtual void getRawBiomeBlock(BiomeArray &biomes, int x, int z, int w, int h) const; + virtual void getRawBiomeIndices(intArray &biomes, int x, int z, int w, int h) const; // 4J added + virtual BiomeArray getBiomeBlock(int x, int z, int w, int h) const; + virtual void getBiomeBlock(BiomeArray& biomes, int x, int z, int w, int h, bool useCache) const; + + virtual byteArray getBiomeIndexBlock(int x, int z, int w, int h) const; + virtual void getBiomeIndexBlock(byteArray& biomeIndices, int x, int z, int w, int h, bool useCache) const; + + /** + * Checks if an area around a block contains only the specified biomes. + * Useful for placing elements like towns. + * + * This is a bit of a rough check, to make it as fast as possible. To ensure + * NO other biomes, add a margin of at least four blocks to the radius + */ + virtual bool containsOnly(int x, int z, int r, vector allowed); + + /** + * Checks if an area around a block contains only the specified biome. + * Useful for placing elements like towns. + * + * This is a bit of a rough check, to make it as fast as possible. To ensure + * NO other biomes, add a margin of at least four blocks to the radius + */ + virtual bool containsOnly(int x, int z, int r, Biome *allowed); + + /** + * Finds the specified biome within the radius. This will return a random + * position if several are found. This test is fairly rough. + * + * Returns null if the biome wasn't found + */ + virtual TilePos *findBiome(int x, int z, int r, Biome *toFind, Random *random); + + /** + * Finds one of the specified biomes within the radius. This will return a + * random position if several are found. This test is fairly rough. + * + * Returns null if the biome wasn't found + */ + virtual TilePos *findBiome(int x, int z, int r, vector allowed, Random *random); + + void update(); +}; diff --git a/Minecraft.World/BirchFeature.cpp b/Minecraft.World/BirchFeature.cpp new file mode 100644 index 00000000..22aa944c --- /dev/null +++ b/Minecraft.World/BirchFeature.cpp @@ -0,0 +1,81 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "BirchFeature.h" +#include "net.minecraft.world.level.tile.h" + +BirchFeature::BirchFeature(bool doUpdate) : Feature(doUpdate) +{ +} + +bool BirchFeature::place(Level *level, Random *random, int x, int y, int z) +{ + int treeHeight = random->nextInt(3) + 5; + + bool free = true; + if (y < 1 || y + treeHeight + 1 > Level::maxBuildHeight) return false; + + for (int yy = y; yy <= y + 1 + treeHeight; yy++) + { + int r = 1; + if (yy == y) r = 0; + if (yy >= y + 1 + treeHeight - 2) r = 2; + for (int xx = x - r; xx <= x + r && free; xx++) + { + for (int zz = z - r; zz <= z + r && free; zz++) + { + if (yy >= 0 && yy < Level::maxBuildHeight) + { + int tt = level->getTile(xx, yy, zz); + if (tt != 0 && tt != Tile::leaves_Id) free = false; + } + else + { + free = false; + } + } + } + } + + if (!free) return false; + + int belowTile = level->getTile(x, y - 1, z); + if ((belowTile != Tile::grass_Id && belowTile != Tile::dirt_Id) || y >= Level::maxBuildHeight - treeHeight - 1) return false; + + // 4J Stu Added to stop tree features generating areas previously place by game rule generation + if(app.getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + int radius = 3; + bool intersects = levelGenOptions->checkIntersects(x - radius, y - 1, z - radius, x + radius, y + treeHeight, z + radius); + if(intersects) + { + //app.DebugPrintf("Skipping reeds feature generation as it overlaps a game rule structure\n"); + return false; + } + } + + level->setTileNoUpdate(x, y - 1, z, Tile::dirt_Id); + + for (int yy = y - 3 + treeHeight; yy <= y + treeHeight; yy++) + { + int yo = yy - (y + treeHeight); + int offs = 1 - yo / 2; + for (int xx = x - offs; xx <= x + offs; xx++) + { + int xo = xx - (x); + for (int zz = z - offs; zz <= z + offs; zz++) + { + int zo = zz - (z); + if (abs(xo) == offs && abs(zo) == offs && (random->nextInt(2) == 0 || yo == 0)) continue; + if (!Tile::solid[level->getTile(xx, yy, zz)]) placeBlock(level, xx, yy, zz, Tile::leaves_Id, LeafTile::BIRCH_LEAF); + } + } + } + for (int hh = 0; hh < treeHeight; hh++) + { + int t = level->getTile(x, y + hh, z); + if (t == 0 || t == Tile::leaves_Id) placeBlock(level, x, y + hh, z, Tile::treeTrunk_Id, TreeTile::BIRCH_TRUNK); + } + return true; + +} \ No newline at end of file diff --git a/Minecraft.World/BirchFeature.h b/Minecraft.World/BirchFeature.h new file mode 100644 index 00000000..6fabd122 --- /dev/null +++ b/Minecraft.World/BirchFeature.h @@ -0,0 +1,11 @@ +#pragma once +#include "Feature.h" + +class Level; + +class BirchFeature : public Feature +{ +public: + BirchFeature(bool doUpdate); + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; diff --git a/Minecraft.World/Blaze.cpp b/Minecraft.World/Blaze.cpp new file mode 100644 index 00000000..57726363 --- /dev/null +++ b/Minecraft.World/Blaze.cpp @@ -0,0 +1,227 @@ +#include "stdafx.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.projectile.h" +#include "SharedConstants.h" +#include "..\Minecraft.Client\Textures.h" +#include "Blaze.h" +#include "SoundTypes.h" + + + +Blaze::Blaze(Level *level) : Monster(level) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_BLAZE; // 4J Was "/mob/fire.png"; + + fireImmune = true; + attackDamage = 6; + xpReward = XP_REWARD_LARGE; + // this.setSize(1.2f, 1.8f); + + // 4J Default inits + allowedHeightOffset = 0.5f; + nextHeightOffsetChangeTick = 0; + attackCounter = 0; +} + +int Blaze::getMaxHealth() +{ + return 20; +} + +void Blaze::defineSynchedData() +{ + Monster::defineSynchedData(); + + entityData->define(DATA_FLAGS_ID, (byte) 0); +} + +int Blaze::getAmbientSound() +{ + return eSoundType_MOB_BLAZE_BREATHE; +} + +int Blaze::getHurtSound() +{ + return eSoundType_MOB_BLAZE_HURT; +} + +int Blaze::getDeathSound() +{ + return eSoundType_MOB_BLAZE_DEATH; +} + +int Blaze::getLightColor(float a) +{ + return SharedConstants::FULLBRIGHT_LIGHTVALUE; +} + +float Blaze::getBrightness(float a) +{ + return 1.0f; +} + +void Blaze::aiStep() +{ + if (!level->isClientSide) + { + + if (isInWaterOrRain()) + { + hurt(DamageSource::drown, 1); + } + + nextHeightOffsetChangeTick--; + if (nextHeightOffsetChangeTick <= 0) + { + nextHeightOffsetChangeTick = SharedConstants::TICKS_PER_SECOND * 5; + allowedHeightOffset = .5f + (float) random->nextGaussian() * 3; + } + + if (getAttackTarget() != NULL && (getAttackTarget()->y + getAttackTarget()->getHeadHeight()) > (this->y + getHeadHeight() + allowedHeightOffset)) + { + yd = yd + (.3f - yd) * .3f; + } + + } + + if (random->nextInt(24) == 0) + { + level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_FIRE_FIRE, 1 + random->nextFloat(), random->nextFloat() * 0.7f + 0.3f); + } + + // slow falling, like chicken + if (!onGround && yd < 0) + { + yd *= 0.6; + } + + for (int i = 0; i < 2; i++) + { + level->addParticle(eParticleType_largesmoke, x + (random->nextDouble() - 0.5) * bbWidth, y + random->nextDouble() * bbHeight, z + (random->nextDouble() - 0.5) * bbWidth, 0, 0, 0); + } + + Monster::aiStep(); +} + +void Blaze::checkHurtTarget(shared_ptr target, float d) +{ + if (attackTime <= 0 && d < 2.0f && target->bb->y1 > bb->y0 && target->bb->y0 < bb->y1) + { + attackTime = 20; + doHurtTarget(target); + } + else if (d < 30) + { + double xd = target->x - x; + double yd = (target->bb->y0 + target->bbHeight / 2) - (y + bbHeight / 2); + double zd = target->z - z; + + if (attackTime == 0) + { + attackCounter++; + if (attackCounter == 1) + { + attackTime = SharedConstants::TICKS_PER_SECOND * 3; + setCharged(true); + } + else if (attackCounter <= 4) + { + attackTime = SharedConstants::TICKS_PER_SECOND / 3; + } + else + { + attackTime = SharedConstants::TICKS_PER_SECOND * 5; + attackCounter = 0; + setCharged(false); + } + + if (attackCounter > 1) + { + float sqd = sqrt(d) * .5f; + + level->levelEvent(nullptr, LevelEvent::SOUND_BLAZE_FIREBALL, (int) x, (int) y, (int) z, 0); + // level.playSound(this, "mob.ghast.fireball", getSoundVolume(), (random.nextFloat() - random.nextFloat()) * 0.2f + 1.0f); + for (int i = 0; i < 1; i++) { + shared_ptr ie = shared_ptr( new SmallFireball(level, dynamic_pointer_cast( shared_from_this() ), xd + random->nextGaussian() * sqd, yd, zd + random->nextGaussian() * sqd) ); + // Vec3 v = getViewVector(1); + // ie.x = x + v.x * 1.5; + ie->y = y + bbHeight / 2 + 0.5f; + // ie.z = z + v.z * 1.5; + level->addEntity(ie); + } + } + + } + yRot = (float) (atan2(zd, xd) * 180 / PI) - 90; + + holdGround = true; + } +} + +void Blaze::causeFallDamage(float distance) +{ +} + +int Blaze::getDeathLoot() +{ + return Item::blazeRod_Id; +} + +bool Blaze::isOnFire() +{ + return isCharged(); +} + +void Blaze::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + if (wasKilledByPlayer) + { + int count = random->nextInt(2 + playerBonusLevel); + for (int i = 0; i < count; i++) + { + spawnAtLocation(Item::blazeRod_Id, 1); + } + // 4J-PB - added to the XBLA version due to our limited amount of glowstone in the Nether - drop 0-2 glowstone dust + count = random->nextInt(3 + playerBonusLevel); + for (int i = 0; i < count; i++) + { + spawnAtLocation(Item::yellowDust_Id, 1); + } + } +} + +bool Blaze::isCharged() +{ + return (entityData->getByte(DATA_FLAGS_ID) & 0x1) != 0; +} + +void Blaze::setCharged(bool value) +{ + byte flags = entityData->getByte(DATA_FLAGS_ID); + if (value) + { + flags |= 0x1; + } + else + { + flags &= ~0x1; + } + entityData->set(DATA_FLAGS_ID, flags); +} + +bool Blaze::isDarkEnoughToSpawn() +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/Blaze.h b/Minecraft.World/Blaze.h new file mode 100644 index 00000000..4f60a4a4 --- /dev/null +++ b/Minecraft.World/Blaze.h @@ -0,0 +1,51 @@ +#pragma once + +#include "Monster.h" + +class Blaze : public Monster +{ +public: + eINSTANCEOF GetType() { return eTYPE_BLAZE; } + static Entity *create(Level *level) { return new Blaze(level); } + + // private int nextBurnNeighborsTick; +private: + float allowedHeightOffset; + int nextHeightOffsetChangeTick; + int attackCounter; + + static const int DATA_FLAGS_ID = 16; + +public: + Blaze(Level *level); + virtual int getMaxHealth(); + +protected: + virtual void defineSynchedData(); + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + +public: + virtual int getLightColor(float a); + virtual float getBrightness(float a); + virtual void aiStep(); + +protected: + virtual void checkHurtTarget(shared_ptr target, float d); + virtual void causeFallDamage(float distance); + virtual int getDeathLoot(); + +public: + virtual bool isOnFire(); + +protected: + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + +public: + bool isCharged(); + void setCharged(bool value); + +protected: + bool isDarkEnoughToSpawn(); +}; \ No newline at end of file diff --git a/Minecraft.World/BlockDestructionProgress.cpp b/Minecraft.World/BlockDestructionProgress.cpp new file mode 100644 index 00000000..a06132a3 --- /dev/null +++ b/Minecraft.World/BlockDestructionProgress.cpp @@ -0,0 +1,57 @@ +#include "stdafx.h" +#include "BlockDestructionProgress.h" + +BlockDestructionProgress::BlockDestructionProgress(int id, int x, int y, int z) +{ + this->id = id; + this->x = x; + this->y = y; + this->z = z; + + progress = 0; + updatedRenderTick = 0; +} + +int BlockDestructionProgress::getId() +{ + return id; +} + +int BlockDestructionProgress::getX() +{ + return x; +} + +int BlockDestructionProgress::getY() +{ + return y; +} + +int BlockDestructionProgress::getZ() +{ + return z; +} + +void BlockDestructionProgress::setProgress(int progress) +{ + if (progress > 10) + { + progress = 10; + } + this->progress = progress; +} + +int BlockDestructionProgress::getProgress() +{ + return progress; +} + +void BlockDestructionProgress::updateTick(int tick) +{ + this->updatedRenderTick = tick; +} + +int BlockDestructionProgress::getUpdatedRenderTick() +{ + return updatedRenderTick; +} \ No newline at end of file diff --git a/Minecraft.World/BlockDestructionProgress.h b/Minecraft.World/BlockDestructionProgress.h new file mode 100644 index 00000000..4c599e83 --- /dev/null +++ b/Minecraft.World/BlockDestructionProgress.h @@ -0,0 +1,24 @@ +#pragma once + +class BlockDestructionProgress +{ +private: + int id; + int x; + int y; + int z; + int progress; + int updatedRenderTick; + +public: + BlockDestructionProgress(int id, int x, int y, int z); + + int getId(); + int getX(); + int getY(); + int getZ(); + void setProgress(int progress); + int getProgress(); + void updateTick(int tick); + int getUpdatedRenderTick(); +}; \ No newline at end of file diff --git a/Minecraft.World/BlockGenMethods.cpp b/Minecraft.World/BlockGenMethods.cpp new file mode 100644 index 00000000..d6c6730e --- /dev/null +++ b/Minecraft.World/BlockGenMethods.cpp @@ -0,0 +1,224 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.level.h" +#include "BlockGenMethods.h" + +void BlockGenMethods::generateBox(Level *level, byteArray blocks, int sx, int sy, int sz, int ex, int ey, int ez, BYTE edge, BYTE filling) +{ + + sx = Mth::clamp(sx, 0, 15); + sy = Mth::clamp(sy, 0, Level::genDepthMinusOne); + sz = Mth::clamp(sz, 0, 15); + ex = Mth::clamp(ex, 0, 15); + ey = Mth::clamp(ey, 0, Level::genDepthMinusOne); + ez = Mth::clamp(ez, 0, 15); + + for (int x = sx; x <= ex; x++) + { + for (int y = sy; y <= ey; y++) + { + for (int z = sz; z <= ez; z++) + { + int p = (x * 16 + z) * Level::genDepth + y; + + if (x == sx || x == ex || y == sy || y == ey || z == sz || z == ez) + { + blocks[p] = edge; + } + else + { + blocks[p] = filling; + } + } + } + } +} + +void BlockGenMethods::generateFrame(Level *level, byteArray blocks, int sx, int sy, int ex, int ey, int flatZ, int direction, BYTE edge, BYTE filling) +{ + + sx = Mth::clamp(sx, 0, 15); + sy = Mth::clamp(sy, 0, Level::genDepthMinusOne); + ex = Mth::clamp(ex, 0, 15); + ey = Mth::clamp(ey, 0, Level::genDepthMinusOne); + int sz = Mth::clamp(flatZ, 0, 15); + int ez = sz; + + bool alongX = true; + + switch (direction) + { + case Direction::WEST: + { + // rotate counter-clockwise + int temp = sz; + sz = 15 - ex; + ez = 15 - sx; + sx = ex = temp; + alongX = false; + } + break; + case Direction::EAST: + { + // rotate clockwise + int temp = sz; + sz = sx; + ez = ex; + sx = ex = 15 - temp; + alongX = false; + } + break; + case Direction::SOUTH: + { + // rotate 180 + sz = ez = 15 - sz; + int temp = sx; + sx = 15 - ex; + ex = 15 - temp; + } + break; + } + + for (int x = sx; x <= ex; x++) + { + for (int y = sy; y <= ey; y++) + { + for (int z = sz; z <= ez; z++) + { + int p = (x * 16 + z) * Level::genDepth + y; + + if (y == sy || y == ey || (alongX && (x == sx || x == ex)) || (!alongX && (z == sz || z == ez))) + { + blocks[p] = edge; + } + else + { + blocks[p] = filling; + } + } + } + } +} + +void BlockGenMethods::generateDirectionLine(Level *level, byteArray blocks, int sx, int sy, int sz, int ex, int ey, int ez, int startDirection, int endDirection, BYTE block) +{ + + sx = Mth::clamp(sx, 0, 15); + sy = Mth::clamp(sy, 0, Level::genDepthMinusOne); + sz = Mth::clamp(sz, 0, 15); + ex = Mth::clamp(ex, 0, 15); + ey = Mth::clamp(ey, 0, Level::genDepthMinusOne); + ez = Mth::clamp(ez, 0, 15); + + switch (startDirection) + { + case Direction::WEST: + { + // rotate counter-clockwise + int temp = sz; + sz = 15 - sx; + sx = temp; + } + break; + case Direction::EAST: + { + // rotate clockwise + int temp = sz; + sz = sx; + sx = 15 - temp; + } + break; + case Direction::SOUTH: + { + // rotate 180 + sz = 15 - sz; + sx = 15 - sx; + } + break; + } + + switch (endDirection) + { + case Direction::WEST: + { + // rotate counter-clockwise + int temp = ez; + ez = 15 - ex; + ex = temp; + } + break; + case Direction::EAST: + { + // rotate clockwise + int temp = ez; + ez = ex; + ex = 15 - temp; + } + break; + case Direction::SOUTH: + { + // rotate 180 + ez = 15 - ez; + ex = 15 - ex; + } + break; + } + + int dx = Mth::abs(ex - sx); + int dz = Mth::abs(ez - sz); + int dy = Mth::abs(ey - sy); + + int slopeX = sx < ex ? 1 : -1; + int slopeZ = sz < ez ? 1 : -1; + int slopeY = sy < ey ? 1 : -1; + + int err = dx - dz; + int yOppositeDelta = (dz > dx) ? dz : dx; + int yErr = dy - yOppositeDelta; + bool doYMovement = true; + + while (true) + { + blocks[(sx * 16 + sz) * Level::genDepth + sy] = block; + + if (sx == ex && sz == ez) + { + break; + } + if (sy == ey) + { + doYMovement = false; + } + int e2 = 2 * err; + if (e2 > -dz) + { + err = err - dz; + sx = sx + slopeX; + } + if (e2 < dx) + { + err = err + dx; + sz = sz + slopeZ; + } + + if (doYMovement) + { + e2 = 2 * yErr; + if (e2 > -yOppositeDelta) + { + yErr = yErr - yOppositeDelta; + sy = sy + slopeY; + } + if (e2 < dy) + { + yErr = yErr + dy; + // don't modify sz here, let the plane decide + } + } + } +} + +void BlockGenMethods::generateLine(Level *level, byteArray blocks, int sx, int sy, int sz, int ex, int ey, int ez, BYTE block) +{ + generateDirectionLine(level, blocks, sx, sy, sz, ex, ey, ez, 0, 0, block); +} \ No newline at end of file diff --git a/Minecraft.World/BlockGenMethods.h b/Minecraft.World/BlockGenMethods.h new file mode 100644 index 00000000..deed0396 --- /dev/null +++ b/Minecraft.World/BlockGenMethods.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ArrayWithLength.h" + +class BlockGenMethods +{ +public: + static void generateBox(Level *level, byteArray blocks, int sx, int sy, int sz, int ex, int ey, int ez, BYTE edge, BYTE filling); + static void generateFrame(Level *level, byteArray blocks, int sx, int sy, int ex, int ey, int flatZ, int direction, BYTE edge, BYTE filling); + static void generateDirectionLine(Level *level, byteArray blocks, int sx, int sy, int sz, int ex, int ey, int ez, int startDirection, int endDirection, BYTE block); + static void generateLine(Level *level, byteArray blocks, int sx, int sy, int sz, int ex, int ey, int ez, BYTE block); +}; \ No newline at end of file diff --git a/Minecraft.World/BlockRegionUpdatePacket.cpp b/Minecraft.World/BlockRegionUpdatePacket.cpp new file mode 100644 index 00000000..d85321a5 --- /dev/null +++ b/Minecraft.World/BlockRegionUpdatePacket.cpp @@ -0,0 +1,157 @@ +#include "stdafx.h" +#include +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.level.h" +#include "compression.h" +#include "PacketListener.h" +#include "BlockRegionUpdatePacket.h" +#include "LevelChunk.h" +#include "DataLayer.h" +#include "Dimension.h" + + + +BlockRegionUpdatePacket::~BlockRegionUpdatePacket() +{ + delete [] buffer.data; +} + +BlockRegionUpdatePacket::BlockRegionUpdatePacket() +{ + shouldDelay = true; + x = 0; + y = 0; + z = 0; + xs = 0; + ys = 0; + zs = 0; + bIsFullChunk = false; +} + +BlockRegionUpdatePacket::BlockRegionUpdatePacket(int x, int y, int z, int xs, int ys, int zs, Level *level) +{ + + shouldDelay = true; + this->x = x; + this->y = y; + this->z = z; + this->xs = xs; + this->ys = ys; + this->zs = zs; + bIsFullChunk = false; + levelIdx = ( ( level->dimension->id == 0 ) ? 0 : ( (level->dimension->id == -1) ? 1 : 2 ) ); + + // 4J - if we are compressing a full chunk, re-order the blocks so that they compress better + // TODO - we should be using compressed data directly here rather than decompressing first and then recompressing... + byteArray rawBuffer; + + if( xs == 16 && ys == Level::maxBuildHeight && zs == 16 && ( ( x & 15 ) == 0 ) && ( y == 0 ) && ( ( z & 15 ) == 0 ) ) + { + bIsFullChunk = true; + + LevelChunk *lc = level->getChunkAt(x,z); + rawBuffer = lc->getReorderedBlocksAndData(x&0xF, y, z&0xF, xs, this->ys, zs); + } + else + { + MemSect(50); + rawBuffer = level->getBlocksAndData(x, y, z, xs, ys, zs, false); + MemSect(0); + } + + if(rawBuffer.length == 0) + { + size = 0; + buffer = byteArray(); + } + else + { + // We don't know how this will compress - just make a fixed length buffer to initially decompress into + // Some small sets of blocks can end up compressing into something bigger than their source + unsigned char *ucTemp = new unsigned char[(256 * 16 * 16 * 5)/2]; + unsigned int inputSize = (256 * 16 * 16 * 5)/2; + + Compression::getCompression()->CompressLZXRLE(ucTemp, &inputSize, rawBuffer.data, (unsigned int) rawBuffer.length); + //app.DebugPrintf("Chunk (%d,%d) compressed from %d to size %d\n", x>>4, z>>4, rawBuffer.length, inputSize); + unsigned char *ucTemp2 = new unsigned char[inputSize]; + memcpy(ucTemp2,ucTemp,inputSize); + delete [] ucTemp; + buffer = byteArray(ucTemp2,inputSize); + delete [] rawBuffer.data; + size = inputSize; + } +} + +void BlockRegionUpdatePacket::read(DataInputStream *dis) //throws IOException +{ + bIsFullChunk = dis->readBoolean(); + x = dis->readInt(); + y = dis->readShort(); + z = dis->readInt(); + xs = dis->read() + 1; + ys = dis->read() + 1; + zs = dis->read() + 1; + + size = dis->readInt(); + levelIdx = ( size >> 30 ) & 3; + size &= 0x3fffffff; + + if(size == 0) + { + buffer = byteArray(); + } + else + { + byteArray compressedBuffer(size); + bool success = dis->readFully(compressedBuffer); + + int bufferSize = xs * ys * zs * 5/2; + // Add the size of the biome data if it's a full chunk + if(bIsFullChunk) bufferSize += (16*16); + buffer = byteArray(bufferSize); + unsigned int outputSize = buffer.length; + + if( success ) + { + Compression::getCompression()->DecompressLZXRLE( buffer.data, &outputSize, compressedBuffer.data, size); + } + else + { + app.DebugPrintf("Not decompressing packet that wasn't fully read\n"); + } + + // printf("Block (%d %d %d), (%d %d %d) coming in decomp from %d to %d\n",x,y,z,xs,ys,zs,size,outputSize); + + + delete [] compressedBuffer.data; + assert(buffer.length == outputSize); + } +} + +void BlockRegionUpdatePacket::write(DataOutputStream *dos) // throws IOException +{ + dos->writeBoolean(bIsFullChunk); + dos->writeInt(x); + dos->writeShort(y); + dos->writeInt(z); + dos->write(xs - 1); + dos->write(ys - 1); + dos->write(zs - 1); + + int sizeAndLevel = size; + sizeAndLevel |= ( levelIdx << 30 ); + dos->writeInt(sizeAndLevel); + dos->write(buffer, 0, size); +} + +void BlockRegionUpdatePacket::handle(PacketListener *listener) +{ + listener->handleBlockRegionUpdate(shared_from_this()); +} + +int BlockRegionUpdatePacket::getEstimatedSize() +{ + return 17 + size; +} + diff --git a/Minecraft.World/BlockRegionUpdatePacket.h b/Minecraft.World/BlockRegionUpdatePacket.h new file mode 100644 index 00000000..54dfea4e --- /dev/null +++ b/Minecraft.World/BlockRegionUpdatePacket.h @@ -0,0 +1,33 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class Level; + +class BlockRegionUpdatePacket : public Packet, public enable_shared_from_this +{ +public: + int x, y, z; + int xs, ys, zs; + byteArray buffer; + int levelIdx; + bool bIsFullChunk; // 4J Added + +private: + int size; + +public: + BlockRegionUpdatePacket(); + ~BlockRegionUpdatePacket(); + BlockRegionUpdatePacket(int x, int y, int z, int xs, int ys, int zs, Level *level); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new BlockRegionUpdatePacket()); } + virtual int getId() { return 51; } +}; diff --git a/Minecraft.World/BlockReplacements.cpp b/Minecraft.World/BlockReplacements.cpp new file mode 100644 index 00000000..7b8aee51 --- /dev/null +++ b/Minecraft.World/BlockReplacements.cpp @@ -0,0 +1,26 @@ +#include "stdafx.h" +#include "BlockReplacements.h" +#include "net.minecraft.world.level.tile.h" + +byteArray BlockReplacements::replacements = byteArray(256); + +void BlockReplacements::staticCtor() +{ + for (int i = 0; i < 256; i++) + { + byte b = (byte) i; + if (b != 0 && Tile::tiles[b & 0xff] == NULL) + { + b = 0; + } + BlockReplacements::replacements[i] = b; + } +} + +void BlockReplacements::replace(byteArray blocks) +{ + for (unsigned int i = 0; i < blocks.length; i++) + { + blocks[i] = replacements[blocks[i] & 0xff]; + } +} \ No newline at end of file diff --git a/Minecraft.World/BlockReplacements.h b/Minecraft.World/BlockReplacements.h new file mode 100644 index 00000000..c8701520 --- /dev/null +++ b/Minecraft.World/BlockReplacements.h @@ -0,0 +1,13 @@ +#pragma once + +class BlockReplacements +{ +public: + static void staticCtor(); + +private: + static byteArray replacements; + +public: + static void replace(byteArray blocks); +}; \ No newline at end of file diff --git a/Minecraft.World/Boat.cpp b/Minecraft.World/Boat.cpp new file mode 100644 index 00000000..39211e44 --- /dev/null +++ b/Minecraft.World/Boat.cpp @@ -0,0 +1,519 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.damagesource.h" +#include "Boat.h" + +const double Boat::MAX_SPEED = 0.35; +const double Boat::MAX_COLLISION_SPEED = MAX_SPEED * 0.75; +const double Boat::MIN_ACCELERATION = 0.07; +const double Boat::MAX_ACCELERATION = 0.35; + +// 4J - added for common ctor code +void Boat::_init() +{ + doLerp = true; + acceleration = MIN_ACCELERATION; + + lSteps = 0; + lx = ly = lz = lyr = lxr = 0.0; + lxd = lyd = lzd = 0.0; + + blocksBuilding = true; + setSize(1.5f, 0.6f); + heightOffset = bbHeight / 2.0f; + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); +} + + + +Boat::Boat(Level *level) : Entity( level ) +{ + + _init(); +} + + +bool Boat::makeStepSound() +{ + return false; +} + +void Boat::defineSynchedData() +{ + entityData->define(DATA_ID_HURT, 0); + entityData->define(DATA_ID_HURTDIR, 1); + entityData->define(DATA_ID_DAMAGE, 0); +} + + +AABB *Boat::getCollideAgainstBox(shared_ptr entity) +{ + return entity->bb; +} + +AABB *Boat::getCollideBox() +{ + return bb; +} + +bool Boat::isPushable() +{ + return true; +} + +Boat::Boat(Level *level, double x, double y, double z) : Entity( level ) +{ + _init(); + setPos(x, y + heightOffset, z); + + xd = 0; + yd = 0; + zd = 0; + + xo = x; + yo = y; + zo = z; +} + +double Boat::getRideHeight() +{ + return bbHeight * 0.0f - 0.3f; +} + +bool Boat::hurt(DamageSource *source, int hurtDamage) +{ + if (level->isClientSide || removed) return true; + + // 4J-JEV: Fix for #88212, + // Untrusted players shouldn't be able to damage minecarts or boats. + if (dynamic_cast(source) != NULL) + { + shared_ptr attacker = source->getDirectEntity(); + + if (dynamic_pointer_cast(attacker) != NULL && + !dynamic_pointer_cast(attacker)->isAllowedToHurtEntity( shared_from_this() )) + return false; + } + + setHurtDir(-getHurtDir()); + setHurtTime(10); + + // 4J Stu - If someone is riding in this, then it can tick multiple times which causes the damage to + // decrease too quickly. So just make the damage a bit higher to start with for similar behaviour + // to an unridden one. Only do this change if the riding player is attacking it. + if( rider.lock() != NULL && rider.lock() == source->getEntity() ) hurtDamage += 1; + + setDamage(getDamage() + hurtDamage * 10); + markHurt(); + + // 4J Stu - Brought froward from 12w36 to fix #46611 - TU5: Gameplay: Minecarts and boat requires more hits than one to be destroyed in creative mode + shared_ptr player = dynamic_pointer_cast(source->getEntity()); + if (player != NULL && player->abilities.instabuild) setDamage(100); + + if (getDamage() > 20 * 2) + { + if (rider.lock() != NULL) rider.lock()->ride( shared_from_this() ); + spawnAtLocation(Item::boat_Id, 1, 0); + remove(); + } + return true; +} + +void Boat::animateHurt() +{ + setHurtDir(-getHurtDir()); + setHurtTime(10); + setDamage(getDamage() * 11); +} + +bool Boat::isPickable() +{ + return !removed; +} + + +void Boat::lerpTo(double x, double y, double z, float yRot, float xRot, int steps) +{ + if (doLerp) + { + lSteps = steps + 5; + } + else + { + double xdiff = x - this->x; + double ydiff = y - this->y; + double zdiff = z - this->z; + double diff = xdiff * xdiff + ydiff * ydiff + zdiff * zdiff; + + if (diff > 1) + { + lSteps = 3; + } + else + { + return; + } + } + + lx = x; + ly = y; + lz = z; + lyr = yRot; + lxr = xRot; + + this->xd = lxd; + this->yd = lyd; + this->zd = lzd; +} + +void Boat::lerpMotion(double xd, double yd, double zd) +{ + lxd = this->xd = xd; + lyd = this->yd = yd; + lzd = this->zd = zd; +} + +void Boat::tick() +{ + Entity::tick(); + if (getHurtTime() > 0) setHurtTime(getHurtTime() - 1); + if (getDamage() > 0) setDamage(getDamage() - 1); + xo = x; + yo = y; + zo = z; + + + int steps = 5; + double waterPercentage = 0; + for (int i = 0; i < steps; i++) + { + double y0 = bb->y0 + (bb->y1 - bb->y0) * (i + 0) / steps - 2 / 16.0f; + double y1 = bb->y0 + (bb->y1 - bb->y0) * (i + 1) / steps - 2 / 16.0f; + AABB *bb2 = AABB::newTemp(bb->x0, y0, bb->z0, bb->x1, y1, bb->z1); + if (level->containsLiquid(bb2, Material::water)) + { + waterPercentage += 1.0 / steps; + } + } + + double lastSpeed = sqrt(xd * xd + zd * zd); + if (lastSpeed > MAX_COLLISION_SPEED) + { + double xa = cos(yRot * PI / 180); + double za = sin(yRot * PI / 180); + + for (int i = 0; i < 1 + lastSpeed * 60; i++) + { + + double side = (random->nextFloat() * 2 - 1); + + double side2 = (random->nextInt(2) * 2 - 1) * 0.7; + if (random->nextBoolean()) + { + double xx = x - xa * side * 0.8 + za * side2; + double zz = z - za * side * 0.8 - xa * side2; + level->addParticle(eParticleType_splash, xx, y - 2 / 16.0f, zz, +xd, yd, +zd); + } + else + { + double xx = x + xa + za * side * 0.7; + double zz = z + za - xa * side * 0.7; + level->addParticle(eParticleType_splash, xx, y - 2 / 16.0f, zz, +xd, yd, +zd); + } + } + } + + if (level->isClientSide && doLerp) + { + if (lSteps > 0) + { + double xt = x + (lx - x) / lSteps; + double yt = y + (ly - y) / lSteps; + double zt = z + (lz - z) / lSteps; + + double yrd = Mth::wrapDegrees(lyr - yRot); + + yRot += (float) ( (yrd) / lSteps ); + xRot += (float) ( (lxr - xRot) / lSteps ); + + lSteps--; + this->setPos(xt, yt, zt); + this->setRot(yRot, xRot); + } + else + { +#if 1 + // Original + //double xt = x + xd; + //double yt = y + yd; + //double zt = z + zd; + //this->setPos(xt, yt, zt); + + // 4J Stu - Fix for various boat bugs, ensure that we check collision on client-side movement + this->move(xd,yd,zd); + + if (onGround) + { + xd *= 0.5f; + yd *= 0.5f; + zd *= 0.5f; + } + xd *= 0.99f; + yd *= 0.95f; + zd *= 0.99f; +#else + // 4J Stu - Fix for #8280 - Gameplay : Boats behave erratically when exited next to land. + // The client shouldn't change the position of the boat + double xt = x;// + xd; + double yt = y + yd; + double zt = z;// + zd; + this->setPos(xt, yt, zt); + + // 4J Stu - Fix for #9579 - GAMEPLAY: Boats with a player in them slowly sink under the water over time, and with no player in them they float into the sky. + // Just make the boats bob up and down rather than any other client-side movement when not receiving packets from server + if (waterPercentage < 1) + { + double bob = waterPercentage * 2 - 1; + yd += 0.04f * bob; + } + else + { + if (yd < 0) yd /= 2; + yd += 0.007f; + } + //if (onGround) + //{ + xd *= 0.5f; + yd *= 0.5f; + zd *= 0.5f; + //} + //xd *= 0.99f; + //yd *= 0.95f; + //zd *= 0.99f; +#endif + } + return; + } + + if (waterPercentage < 1) + { + double bob = waterPercentage * 2 - 1; + yd += 0.04f * bob; + } + else + { + if (yd < 0) yd /= 2; + yd += 0.007f; + } + + + if (rider.lock() != NULL) + { + xd += rider.lock()->xd * acceleration; + zd += rider.lock()->zd * acceleration; + } + + double curSpeed = sqrt(xd * xd + zd * zd); + + if (curSpeed > MAX_SPEED) + { + double ratio = MAX_SPEED / curSpeed; + + xd *= ratio; + zd *= ratio; + curSpeed = MAX_SPEED; + } + + if (curSpeed > lastSpeed && acceleration < MAX_ACCELERATION) + { + acceleration += (MAX_ACCELERATION - acceleration) / 35; + if (acceleration > MAX_ACCELERATION) acceleration = MAX_ACCELERATION; + } + else + { + acceleration -= (acceleration - MIN_ACCELERATION) / 35; + if (acceleration < MIN_ACCELERATION) acceleration = MIN_ACCELERATION; + } + + if (onGround) + { + xd *= 0.5f; + yd *= 0.5f; + zd *= 0.5f; + } + move(xd, yd, zd); + + if ((horizontalCollision && lastSpeed > 0.20)) + { + if (!level->isClientSide) + { + remove(); + for (int i = 0; i < 3; i++) + { + spawnAtLocation(Tile::wood_Id, 1, 0); + } + for (int i = 0; i < 2; i++) + { + spawnAtLocation(Item::stick->id, 1, 0); + } + } + } + else + { + xd *= 0.99f; + yd *= 0.95f; + zd *= 0.99f; + } + + xRot = 0; + double yRotT = yRot; + double xDiff = xo - x; + double zDiff = zo - z; + if (xDiff * xDiff + zDiff * zDiff > 0.001) + { + yRotT = (float) (atan2(zDiff, xDiff) * 180 / PI); + } + + double rotDiff = Mth::wrapDegrees(yRotT - yRot); + + if (rotDiff > 20) rotDiff = 20; + if (rotDiff < -20) rotDiff = -20; + + yRot += (float) rotDiff; + setRot(yRot, xRot); + + if(level->isClientSide) return; + + vector > *entities = level->getEntities(shared_from_this(), this->bb->grow(0.2f, 0, 0.2f)); + if (entities != NULL && !entities->empty()) + { + AUTO_VAR(itEnd, entities->end()); + for (AUTO_VAR(it, entities->begin()); it != itEnd; it++) + { + shared_ptr e = (*it); // entities->at(i); + if (e != rider.lock() && e->isPushable() && e->GetType() == eTYPE_BOAT) + { + e->push(shared_from_this()); + } + } + } + + for (int i = 0; i < 4; i++) + { + int xx = Mth::floor(x + ((i % 2) - 0.5) * 0.8); + int zz = Mth::floor(z + ((i / 2) - 0.5) * 0.8); + + for (int j = 0; j < 2; j++) + { + int yy = Mth::floor(y) + j; + int tile = level->getTile(xx, yy, zz); + int data = level->getData(xx, yy, zz); + + if (tile == Tile::topSnow_Id) + { + level->setTile(xx, yy, zz, 0); + } + else if (tile == Tile::waterLily_Id) + { + Tile::waterLily->spawnResources(level, xx, yy, zz, data, 0.3f, 0); + level->setTile(xx, yy, zz, 0); + } + } + + } + + if (rider.lock() != NULL) + { + if (rider.lock()->removed) rider = weak_ptr(); + } +} + +void Boat::positionRider() +{ + if (rider.lock() == NULL) return; + + double xa = cos(yRot * PI / 180) * 0.4; + double za = sin(yRot * PI / 180) * 0.4; + rider.lock()->setPos(x + xa, y + getRideHeight() + rider.lock()->getRidingHeight(), z + za); +} + + +void Boat::addAdditonalSaveData(CompoundTag *base) +{ +} + +void Boat::readAdditionalSaveData(CompoundTag *base) +{ +} + + +float Boat::getShadowHeightOffs() +{ + return 0; +} + +wstring Boat::getName() +{ + return L"Boat"; +} + +bool Boat::interact(shared_ptr player) +{ + if (rider.lock() != NULL && dynamic_pointer_cast(rider.lock())!=NULL && rider.lock() != player) return true; + if (!level->isClientSide) + { + // 4J HEG - Fixed issue with player not being able to dismount boat (issue #4446) + player->ride( rider.lock() == player ? nullptr : shared_from_this() ); + } + return true; +} + +void Boat::setDamage(int damage) +{ + entityData->set(DATA_ID_DAMAGE, damage); +} + +int Boat::getDamage() +{ + return entityData->getInteger(DATA_ID_DAMAGE); +} + +void Boat::setHurtTime(int hurtTime) +{ + entityData->set(DATA_ID_HURT, hurtTime); +} + +int Boat::getHurtTime() +{ + return entityData->getInteger(DATA_ID_HURT); +} + +void Boat::setHurtDir(int hurtDir) +{ + entityData->set(DATA_ID_HURTDIR, hurtDir); +} + +int Boat::getHurtDir() +{ + return entityData->getInteger(DATA_ID_HURTDIR); +} + +bool Boat::getDoLerp() +{ + return doLerp; +} + +void Boat::setDoLerp(bool doLerp) +{ + this->doLerp = doLerp; +} diff --git a/Minecraft.World/Boat.h b/Minecraft.World/Boat.h new file mode 100644 index 00000000..1039e8f9 --- /dev/null +++ b/Minecraft.World/Boat.h @@ -0,0 +1,83 @@ +#pragma once +using namespace std; + +#include "Entity.h" + +class Player; +class Level; +class CompoundTag; +class DamageSource; + +class Boat : public Entity +{ +public: + eINSTANCEOF GetType() { return eTYPE_BOAT; }; + static Entity *create(Level *level) { return new Boat(level); } + +private: + // 4J - added for common ctor code + void _init(); +public: + static const int serialVersionUID = 0; + +private: + static const int DATA_ID_HURT = 17; + static const int DATA_ID_HURTDIR = 18; + static const int DATA_ID_DAMAGE = 19; + static const double MAX_SPEED; + static const double MAX_COLLISION_SPEED; + static const double MIN_ACCELERATION; + static const double MAX_ACCELERATION; + + bool doLerp; + double acceleration; + +public: + Boat(Level *level); + +protected: + virtual bool makeStepSound(); + virtual void defineSynchedData(); + +public: + virtual AABB *getCollideAgainstBox(shared_ptr entity); + virtual AABB *getCollideBox(); + virtual bool isPushable(); + + Boat(Level *level, double x, double y, double z); + + virtual double getRideHeight(); + virtual bool hurt(DamageSource *source, int damage); + virtual void animateHurt(); + virtual bool isPickable(); + +private: + int lSteps; + double lx, ly, lz, lyr, lxr; + double lxd, lyd, lzd; + +public: + virtual void lerpTo(double x, double y, double z, float yRot, float xRot, int steps); + virtual void lerpMotion(double xd, double yd, double zd); + virtual void tick(); + virtual void positionRider(); + +protected: + virtual void addAdditonalSaveData(CompoundTag *base); + virtual void readAdditionalSaveData(CompoundTag *base); + +public: + virtual float getShadowHeightOffs(); + wstring getName(); + virtual bool interact(shared_ptr player); + + virtual void setDamage(int damage); + virtual int getDamage(); + virtual void setHurtTime(int hurtTime); + virtual int getHurtTime(); + virtual void setHurtDir(int hurtDir); + virtual int getHurtDir(); + + bool getDoLerp(); + void setDoLerp(bool doLerp); +}; diff --git a/Minecraft.World/BoatItem.cpp b/Minecraft.World/BoatItem.cpp new file mode 100644 index 00000000..3499bc02 --- /dev/null +++ b/Minecraft.World/BoatItem.cpp @@ -0,0 +1,129 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "ItemInstance.h" +#include "BoatItem.h" + +BoatItem::BoatItem(int id) : Item( id ) +{ + this->maxStackSize = 1; +} + +bool BoatItem::TestUse(Level *level, shared_ptr player) +{ + // 4J-PB - added for tooltips to test use + // 4J TODO really we should have the crosshair hitresult telling us if it hit water, and at what distance, so we don't need to do this again + // if the player happens to have a boat in their hand + + float xRot = player->xRotO + (player->xRot - player->xRotO); + float yRot = player->yRotO + (player->yRot - player->yRotO); + + double x = player->xo + (player->x - player->xo); + double y = player->yo + (player->y - player->yo) + 1.62 - player->heightOffset; + double z = player->zo + (player->z - player->zo); + + Vec3 *from = Vec3::newTemp(x, y, z); + + float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI); + float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI); + float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD); + float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD); + + float xa = ySin * xCos; + float ya = xSin; + float za = yCos * xCos; + + double range = 5; + Vec3 *to = from->add(xa * range, ya * range, za * range); + HitResult *hr = level->clip(from, to, true); + if (hr == NULL) return false; + + if (hr->type == HitResult::TILE) + { + delete hr; + return true; + } + delete hr; + return false; +} +shared_ptr BoatItem::use(shared_ptr itemInstance, Level *level, shared_ptr player) +{ + float a = 1; + + float xRot = player->xRotO + (player->xRot - player->xRotO) * a; + float yRot = player->yRotO + (player->yRot - player->yRotO) * a; + + double x = player->xo + (player->x - player->xo) * a; + double y = player->yo + (player->y - player->yo) * a + 1.62 - player->heightOffset; + double z = player->zo + (player->z - player->zo) * a; + + Vec3 *from = Vec3::newTemp(x, y, z); + + float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI); + float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI); + float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD); + float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD); + + float xa = ySin * xCos; + float ya = xSin; + float za = yCos * xCos; + + double range = 5; + Vec3 *to = from->add(xa * range, ya * range, za * range); + HitResult *hr = level->clip(from, to, true); + if (hr == NULL) return itemInstance; + + // check entity collision + Vec3 *b = player->getViewVector(a); + bool hitEntity = false; + float overlap = 1; + vector > *objects = level->getEntities(player, player->bb->expand(b->x * (range), b->y * (range), b->z * (range))->grow(overlap, overlap, overlap)); + //for (int i = 0; i < objects.size(); i++) { + for(AUTO_VAR(it, objects->begin()); it != objects->end(); ++it) + { + shared_ptr e = *it; //objects.get(i); + if (!e->isPickable()) continue; + + float rr = e->getPickRadius(); + AABB *bb = e->bb->grow(rr, rr, rr); + if (bb->contains(from)) + { + hitEntity = true; + } + } + if (hitEntity) + { + return itemInstance; + } + + if (hr->type == HitResult::TILE) + { + int xt = hr->x; + int yt = hr->y; + int zt = hr->z; + + if (!level->isClientSide) + { + if (level->getTile(xt, yt, zt) == Tile::topSnow_Id) yt--; + if( level->countInstanceOf(eTYPE_BOAT, true) < Level::MAX_XBOX_BOATS ) // 4J - added limit + { + level->addEntity( shared_ptr( new Boat(level, xt + 0.5f, yt + 1.0f, zt + 0.5f) ) ); + if (!player->abilities.instabuild) + { + itemInstance->count--; + } + } + else + { + // display a message to say max boats has been hit + player->displayClientMessage(IDS_MAX_BOATS ); + } + } + } + delete hr; + + return itemInstance; +} \ No newline at end of file diff --git a/Minecraft.World/BoatItem.h b/Minecraft.World/BoatItem.h new file mode 100644 index 00000000..81bc4e18 --- /dev/null +++ b/Minecraft.World/BoatItem.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Item.h" + +class Player; +class Level; + +class BoatItem : public Item +{ +public: + + BoatItem(int id); + + virtual bool TestUse(Level *level, shared_ptr player); + virtual shared_ptr use(shared_ptr itemInstance, Level *level, shared_ptr player); + + /* + * public boolean useOn(ItemInstance instance, Player player, Level level, + * int x, int y, int z, int face) { // if (face != 1) return false; int + * targetType = level.getTile(x, y, z); if (targetType == Tile.rail.id) { + * level.addEntity(new Minecart(level, x + 0.5f, y + 0.5f, z + 0.5f)); + * instance.count--; return true; } return false; } + */ +}; \ No newline at end of file diff --git a/Minecraft.World/BodyControl.cpp b/Minecraft.World/BodyControl.cpp new file mode 100644 index 00000000..b74cc4dd --- /dev/null +++ b/Minecraft.World/BodyControl.cpp @@ -0,0 +1,54 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "BodyControl.h" + +const float BodyControl::maxClampAngle = 75.0f; + +BodyControl::BodyControl(Mob *mob) +{ + this->mob = mob; + + timeStill = 0; + lastHeadY = 0.0f; +} + +void BodyControl::clientTick() +{ + double xd = mob->x - mob->xo; + double zd = mob->z - mob->zo; + + if (xd * xd + zd * zd > MoveControl::MIN_SPEED_SQR) + { + // we are moving. + mob->yBodyRot = mob->yRot; + mob->yHeadRot = clamp(mob->yBodyRot, mob->yHeadRot, maxClampAngle); + lastHeadY = mob->yHeadRot; + timeStill = 0; + return; + } + + // Body will align to head after looking long enough in a direction + float clampAngle = maxClampAngle; + if (abs(mob->yHeadRot - lastHeadY) > 15) + { + timeStill = 0; + lastHeadY = mob->yHeadRot; + } + else + { + ++timeStill; + static const int timeStillBeforeTurn = 10; + if (timeStill > timeStillBeforeTurn) clampAngle = max(1 - (timeStill - timeStillBeforeTurn) / 10.f, 0.0f) * maxClampAngle; + } + + mob->yBodyRot = clamp(mob->yHeadRot, mob->yBodyRot, clampAngle); +} + +float BodyControl::clamp(float clampTo, float clampFrom, float clampAngle) +{ + float headDiffBody = Mth::wrapDegrees(clampTo - clampFrom); + if (headDiffBody < -clampAngle) headDiffBody = -clampAngle; + if (headDiffBody >= clampAngle) headDiffBody = +clampAngle; + return clampTo - headDiffBody; +} \ No newline at end of file diff --git a/Minecraft.World/BodyControl.h b/Minecraft.World/BodyControl.h new file mode 100644 index 00000000..951b2197 --- /dev/null +++ b/Minecraft.World/BodyControl.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Control.h" + +class BodyControl : public Control +{ +private: + Mob *mob; + static const float maxClampAngle; + int timeStill; + float lastHeadY; + +public: + BodyControl(Mob *mob); + + void clientTick(); + +private: + float clamp(float clampTo, float clampFrom, float clampAngle); +}; \ No newline at end of file diff --git a/Minecraft.World/BonusChestFeature.cpp b/Minecraft.World/BonusChestFeature.cpp new file mode 100644 index 00000000..2d7690ee --- /dev/null +++ b/Minecraft.World/BonusChestFeature.cpp @@ -0,0 +1,88 @@ +#include "stdafx.h" +#include "BonusChestFeature.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "WeighedTreasure.h" +#include "StructurePiece.h" + +BonusChestFeature::BonusChestFeature(WeighedTreasureArray treasureList, int numRolls) : treasureList(treasureList), numRolls(numRolls) +{ +} + +// 4J - original virtual method +bool BonusChestFeature::place(Level *level, Random *random, int x, int y, int z) +{ + return place(level, random, x, y, z, false ); +} + +// 4J - added force parameter - trying to keep this as similar as possible to the original algorithm, but would also like it to definitely place a +// chest as it doesn't necessarily find somewhere in the original java. This method is called multple times for different x,y,z round the +// spawn point and force signifies that this is the last time this will be called. In this case, just place the chest exactly where the input +// parameters requested we place it (we know this will be one tile above the top solid block of a randomn column), and then do our best to place +// any surrounding torches where appropriate. + +bool BonusChestFeature::place(Level *level, Random *random, int x, int y, int z, bool force) +{ + if( !force ) + { + int t = 0; + while (((t = level->getTile(x, y, z)) == 0 || t == Tile::leaves_Id) && y > 1) + y--; + + if (y < 1) + { + return false; + } + y++; + } + + for (int i = 0; i < 4; i++) + { + int x2, y2, z2; + + if( force ) + { + x2 = x; + y2 = y - 1; // 4J - the position passed in is actually two above the top solid block, as the calling function adds 1 to getTopSolidBlock, and that actually returns the block above anyway. + // this would explain why there is a while loop above here (not used in force mode) to move the y back down again, shouldn't really be needed if 1 wasn't added to the getTopSolidBlock return value. + z2 = z; + } + else + { + x2 = x + random->nextInt(4) - random->nextInt(4); + y2 = y + random->nextInt(3) - random->nextInt(3); + z2 = z + random->nextInt(4) - random->nextInt(4); + } + + if (force || ( level->isEmptyTile(x2, y2, z2) && level->isTopSolidBlocking(x2, y2 - 1, z2))) + { + level->setTile(x2, y2, z2, Tile::chest_Id); + shared_ptr chest = dynamic_pointer_cast(level->getTileEntity(x2, y2, z2)); + if (chest != NULL) + { + WeighedTreasure::addChestItems(random, treasureList, chest, numRolls); + chest->isBonusChest = true; // 4J added + } + if (level->isEmptyTile(x2 - 1, y2, z2) && level->isTopSolidBlocking(x2 - 1, y2 - 1, z2)) + { + level->setTile(x2 - 1, y2, z2, Tile::torch_Id); + } + if (level->isEmptyTile(x2 + 1, y2, z2) && level->isTopSolidBlocking(x2 - 1, y2 - 1, z2)) + { + level->setTile(x2 + 1, y2, z2, Tile::torch_Id); + } + if (level->isEmptyTile(x2, y2, z2 - 1) && level->isTopSolidBlocking(x2 - 1, y2 - 1, z2)) + { + level->setTile(x2, y2, z2 - 1, Tile::torch_Id); + } + if (level->isEmptyTile(x2, y2, z2 + 1) && level->isTopSolidBlocking(x2 - 1, y2 - 1, z2)) + { + level->setTile(x2, y2, z2 + 1, Tile::torch_Id); + } + return true; + } + } + + return false; +} diff --git a/Minecraft.World/BonusChestFeature.h b/Minecraft.World/BonusChestFeature.h new file mode 100644 index 00000000..cd4aca3e --- /dev/null +++ b/Minecraft.World/BonusChestFeature.h @@ -0,0 +1,19 @@ +#pragma once +#include "Feature.h" + +class Random; +class Level; + +class BonusChestFeature : public Feature +{ + +private: + const WeighedTreasureArray treasureList; + const int numRolls; + +public: + BonusChestFeature(WeighedTreasureArray treasureList, int numRolls); + + virtual bool place(Level *level, Random *random, int x, int y, int z); + bool place(Level *level, Random *random, int x, int y, int z, bool force); // 4J added this method with extra force parameter +}; diff --git a/Minecraft.World/BookItem.cpp b/Minecraft.World/BookItem.cpp new file mode 100644 index 00000000..8e4098bb --- /dev/null +++ b/Minecraft.World/BookItem.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" + +#include "BookItem.h" + +BookItem::BookItem(int id) : Item(id) +{ +} + +bool BookItem::isEnchantable(shared_ptr itemInstance) +{ + return itemInstance->count == 1; +} + +int BookItem::getEnchantmentValue() +{ + return 1; +} \ No newline at end of file diff --git a/Minecraft.World/BookItem.h b/Minecraft.World/BookItem.h new file mode 100644 index 00000000..f60fc417 --- /dev/null +++ b/Minecraft.World/BookItem.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Item.h" + +class BookItem : public Item +{ +public: + BookItem(int id); + + bool isEnchantable(shared_ptr itemInstance); + int getEnchantmentValue(); +}; \ No newline at end of file diff --git a/Minecraft.World/BookshelfTile.cpp b/Minecraft.World/BookshelfTile.cpp new file mode 100644 index 00000000..d8c03fe1 --- /dev/null +++ b/Minecraft.World/BookshelfTile.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" +#include "BookshelfTile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.h" + +BookshelfTile::BookshelfTile(int id) : Tile(id, Material::wood) +{ +} + +Icon *BookshelfTile::getTexture(int face, int data) +{ + if (face == Facing::UP || face == Facing::DOWN) return Tile::wood->getTexture(face); + return Tile::getTexture(face, data); +} + +int BookshelfTile::getResourceCount(Random *random) +{ + return 3; +} + +int BookshelfTile::getResource(int data, Random *random,int playerBonusLevel) +{ + return Item::book_Id; +} \ No newline at end of file diff --git a/Minecraft.World/BookshelfTile.h b/Minecraft.World/BookshelfTile.h new file mode 100644 index 00000000..5b1bc21e --- /dev/null +++ b/Minecraft.World/BookshelfTile.h @@ -0,0 +1,15 @@ +#pragma once +#include "Tile.h" +#include "Material.h" + +class Random; + +class BookshelfTile : public Tile +{ +public: + BookshelfTile(int id); + + virtual Icon *getTexture(int face, int data); + virtual int getResourceCount(Random *random); + virtual int getResource(int data, Random *random,int playerBonusLevel); +}; \ No newline at end of file diff --git a/Minecraft.World/BossMob.cpp b/Minecraft.World/BossMob.cpp new file mode 100644 index 00000000..52b88230 --- /dev/null +++ b/Minecraft.World/BossMob.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" +#include "BossMobPart.h" +#include "BossMob.h" + + + +BossMob::BossMob(Level *level) : Mob( level ) +{ + + maxHealth = 100; + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); +} + +int BossMob::getMaxHealth() +{ + return maxHealth; +} + +bool BossMob::hurt(shared_ptr bossMobPart, DamageSource *source, int damage) +{ + return hurt(source, damage); +} + +bool BossMob::hurt(DamageSource *source, int damage) +{ + return false; +} + +bool BossMob::reallyHurt(DamageSource *source, int damage) +{ + + return Mob::hurt(source, damage); + +} \ No newline at end of file diff --git a/Minecraft.World/BossMob.h b/Minecraft.World/BossMob.h new file mode 100644 index 00000000..50a6d3ef --- /dev/null +++ b/Minecraft.World/BossMob.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Mob.h" + +class Level; +class BossMobPart; + +class BossMob : public Mob +{ +protected: + int maxHealth; + +public: + BossMob(Level *level); + + virtual int getMaxHealth(); + virtual bool hurt(shared_ptr bossMobPart, DamageSource *source, int damage); + virtual bool hurt(DamageSource *source, int damage); + +protected: + virtual bool reallyHurt(DamageSource *source, int damage); +}; \ No newline at end of file diff --git a/Minecraft.World/BossMobPart.cpp b/Minecraft.World/BossMobPart.cpp new file mode 100644 index 00000000..923972ad --- /dev/null +++ b/Minecraft.World/BossMobPart.cpp @@ -0,0 +1,43 @@ +#include "stdafx.h" +#include "BossMob.h" +#include "BossMobPart.h" + + + +BossMobPart::BossMobPart(BossMob *bossMob, const wstring &id, float w, float h) : Entity(bossMob->level), bossMob( bossMob ), id( id ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + setSize(w, h); +} + + +void BossMobPart::defineSynchedData() +{ +} + +void BossMobPart::readAdditionalSaveData(CompoundTag *tag) +{ +} + +void BossMobPart::addAdditonalSaveData(CompoundTag *tag) +{ +} + + +bool BossMobPart::isPickable() +{ + return true; +} + +bool BossMobPart::hurt(DamageSource *source, int damage) +{ + return bossMob->hurt( dynamic_pointer_cast( shared_from_this() ), source, damage); +} + +bool BossMobPart::is(shared_ptr other) +{ + return shared_from_this() == other || bossMob == other.get(); +} \ No newline at end of file diff --git a/Minecraft.World/BossMobPart.h b/Minecraft.World/BossMobPart.h new file mode 100644 index 00000000..55af809f --- /dev/null +++ b/Minecraft.World/BossMobPart.h @@ -0,0 +1,27 @@ +#pragma once +using namespace std; +#include "Entity.h" + +class Level; +class BossMob; + +class BossMobPart : public Entity +{ +public: + eINSTANCEOF GetType() { return eTYPE_BOSS_MOB_PART; }; +public: + BossMob *bossMob; + const wstring id; + + BossMobPart(BossMob *bossMob, const wstring &id, float w, float h); + +protected: + virtual void defineSynchedData(); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual void addAdditonalSaveData(CompoundTag *tag); + +public: + virtual bool isPickable(); + virtual bool hurt(DamageSource *source, int damage); + virtual bool is(shared_ptr other); +}; \ No newline at end of file diff --git a/Minecraft.World/BottleItem.cpp b/Minecraft.World/BottleItem.cpp new file mode 100644 index 00000000..3aecafee --- /dev/null +++ b/Minecraft.World/BottleItem.cpp @@ -0,0 +1,99 @@ +#include "stdafx.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.item.h" +#include "BottleItem.h" + +BottleItem::BottleItem(int id) : Item(id) +{ +} + +Icon *BottleItem::getIcon(int auxValue) +{ + return Item::potion->getIcon(0); +} + +shared_ptr BottleItem::use(shared_ptr itemInstance, Level *level, shared_ptr player) +{ + HitResult *hr = getPlayerPOVHitResult(level, player, true); + if (hr == NULL) return itemInstance; + + if (hr->type == HitResult::TILE) + { + int xt = hr->x; + int yt = hr->y; + int zt = hr->z; + delete hr; + + if (!level->mayInteract(player, xt, yt, zt, 0)) + { + return itemInstance; + } + if (!player->mayBuild(xt, yt, zt)) + { + return itemInstance; + } + if (level->getMaterial(xt, yt, zt) == Material::water) + { + itemInstance->count--; + if (itemInstance->count <= 0) + { + return shared_ptr( new ItemInstance( (Item *)Item::potion) ); + } + else + { + if (!player->inventory->add(shared_ptr( new ItemInstance( (Item *)Item::potion) ))) + { + player->drop( shared_ptr( new ItemInstance(Item::potion_Id, 1, 0) )); + } + } + } + } + else + { + delete hr; + } + + return itemInstance; +} + +// 4J-PB - added to allow tooltips +bool BottleItem::TestUse(Level *level, shared_ptr player) +{ + HitResult *hr = getPlayerPOVHitResult(level, player, true); + if (hr == NULL) return false; + + if (hr->type == HitResult::TILE) + { + int xt = hr->x; + int yt = hr->y; + int zt = hr->z; + delete hr; + + if (!level->mayInteract(player, xt, yt, zt, 0)) + { + return false; + } + if (!player->mayBuild(xt, yt, zt)) + { + return false; + } + if (level->getMaterial(xt, yt, zt) == Material::water) + { + return true; + } + } + else + { + delete hr; + } + + return false; +} + +void BottleItem::registerIcons(IconRegister *iconRegister) +{ + // We reuse another texture. +} \ No newline at end of file diff --git a/Minecraft.World/BottleItem.h b/Minecraft.World/BottleItem.h new file mode 100644 index 00000000..a8f5bc09 --- /dev/null +++ b/Minecraft.World/BottleItem.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Item.h" + +class Icon; + +class BottleItem : public Item +{ +public: + BottleItem(int id); + + //@Override + Icon *getIcon(int auxValue); + + virtual shared_ptr use(shared_ptr itemInstance, Level *level, shared_ptr player); + virtual bool TestUse(Level *level, shared_ptr player); + + //@Override + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/BoundingBox.cpp b/Minecraft.World/BoundingBox.cpp new file mode 100644 index 00000000..eb828ed0 --- /dev/null +++ b/Minecraft.World/BoundingBox.cpp @@ -0,0 +1,166 @@ +#include "stdafx.h" +#include "BoundingBox.h" +#include "Direction.h" +#include "JavaMath.h" + +BoundingBox::BoundingBox() +{ + // 4J added initialisers + x0 = 0; + y0 = 0; + z0 = 0; + x1 = 0; + y1 = 0; + z1 = 0; +} + +BoundingBox *BoundingBox::getUnknownBox() +{ + return new BoundingBox(INT_MAX, INT_MAX, INT_MAX, INT_MIN, INT_MIN, INT_MIN ); +} + +BoundingBox *BoundingBox::orientBox(int footX, int footY, int footZ, int offX, int offY, int offZ, int width, int height, int depth, int orientation) +{ + switch (orientation) + { + default: + return new BoundingBox(footX + offX, footY + offY, footZ + offZ, footX + width - 1 + offX, footY + height - 1 + offY, footZ + depth - 1 + offZ); + case Direction::NORTH: + // foot is at x0, y0, z1 + return new BoundingBox(footX + offX, footY + offY, footZ - depth + 1 + offZ, footX + width - 1 + offX, footY + height - 1 + offY, footZ + offZ); + case Direction::SOUTH: + // foot is at x0, y0, z0 + return new BoundingBox(footX + offX, footY + offY, footZ + offZ, footX + width - 1 + offX, footY + height - 1 + offY, footZ + depth - 1 + offZ); + case Direction::WEST: + // foot is at x1, y0, z0, but width and depth are flipped + return new BoundingBox(footX - depth + 1 + offZ, footY + offY, footZ + offX, footX + offZ, footY + height - 1 + offY, footZ + width - 1 + offX); + case Direction::EAST: + // foot is at x0, y0, z0, but width and depth are flipped + return new BoundingBox(footX + offZ, footY + offY, footZ + offX, footX + depth - 1 + offZ, footY + height - 1 + offY, footZ + width - 1 + offX); + } +} + +BoundingBox::BoundingBox(BoundingBox *other) +{ + this->x0 = other->x0; + this->y0 = other->y0; + this->z0 = other->z0; + this->x1 = other->x1; + this->y1 = other->y1; + this->z1 = other->z1; +} + +BoundingBox::BoundingBox(int x0, int y0, int z0, int x1, int y1, int z1) +{ + this->x0 = x0; + this->y0 = y0; + this->z0 = z0; + this->x1 = x1; + this->y1 = y1; + this->z1 = z1; +} + +BoundingBox::BoundingBox(int x0, int z0, int x1, int z1) +{ + this->x0 = x0; + this->z0 = z0; + this->x1 = x1; + this->z1 = z1; + + // the bounding box for this constructor is limited to world size, + // excluding bedrock level + this->y0 = 1; + this->y1 = 512; +} + +bool BoundingBox::intersects(BoundingBox *other) +{ + return !(this->x1 < other->x0 || this->x0 > other->x1 || this->z1 < other->z0 || this->z0 > other->z1 || this->y1 < other->y0 || this->y0 > other->y1); +} + +bool BoundingBox::intersects(int x0, int y0, int z0, int x1, int y1, int z1) +{ + return !(this->x1 < x0 || this->x0 > x1 || this->z1 < z0 || this->z0 > z1 || this->y1 < y0 || this->y0 > y1); +} + +bool BoundingBox::intersects(int x0, int z0, int x1, int z1) +{ + return !(this->x1 < x0 || this->x0 > x1 || this->z1 < z0 || this->z0 > z1); +} + +void BoundingBox::expand(BoundingBox *other) +{ + this->x0 = Math::_min(this->x0, other->x0); + this->y0 = Math::_min(this->y0, other->y0); + this->z0 = Math::_min(this->z0, other->z0); + this->x1 = Math::_max(this->x1, other->x1); + this->y1 = Math::_max(this->y1, other->y1); + this->z1 = Math::_max(this->z1, other->z1); +} + +BoundingBox *BoundingBox::getIntersection(BoundingBox *other) +{ + if (!intersects(other)) + { + return NULL; + } + BoundingBox *result = new BoundingBox(); + result->x0 = Math::_max(this->x0, other->x0); + result->y0 = Math::_max(this->y0, other->y0); + result->z0 = Math::_max(this->z0, other->z0); + result->x1 = Math::_min(this->x1, other->x1); + result->y1 = Math::_min(this->y1, other->y1); + result->z1 = Math::_min(this->z1, other->z1); + + return result; +} + +void BoundingBox::move(int dx, int dy, int dz) +{ + x0 += dx; + y0 += dy; + z0 += dz; + x1 += dx; + y1 += dy; + z1 += dz; +} + +bool BoundingBox::isInside(int x, int y, int z) +{ + return (x >= x0 && x <= x1 && z >= z0 && z <= z1 && y >= y0 && y <= y1); +} + +int BoundingBox::getXSpan() +{ + return x1 - x0 + 1; +} + +int BoundingBox::getYSpan() +{ + return y1 - y0 + 1; +} + +int BoundingBox::getZSpan() +{ + return z1 - z0 + 1; +} + +int BoundingBox::getXCenter() +{ + return x0 + (x1 - x0 + 1) / 2; +} + +int BoundingBox::getYCenter() +{ + return y0 + (y1 - y0 + 1) / 2; +} + +int BoundingBox::getZCenter() +{ + return z0 + (z1 - z0 + 1) / 2; +} + +wstring BoundingBox::toString() +{ + return L"(" + _toString(x0) + L", " + _toString(y0) + L", " + _toString(z0) + L"; " + _toString(x1) + L", " + _toString(y1) + L", " + _toString(z1) + L")"; +} \ No newline at end of file diff --git a/Minecraft.World/BoundingBox.h b/Minecraft.World/BoundingBox.h new file mode 100644 index 00000000..23abd66d --- /dev/null +++ b/Minecraft.World/BoundingBox.h @@ -0,0 +1,31 @@ +#pragma once + +class BoundingBox +{ +public: + int x0, y0, z0, x1, y1, z1; + + BoundingBox(); + static BoundingBox *getUnknownBox(); + static BoundingBox *orientBox(int footX, int footY, int footZ, int offX, int offY, int offZ, int width, int height, int depth, int orientation); + BoundingBox(BoundingBox *other); + BoundingBox(int x0, int y0, int z0, int x1, int y1, int z1); + BoundingBox(int x0, int z0, int x1, int z1); + bool intersects(BoundingBox *other); + bool intersects(int x0, int y0, int z0, int x1, int y1, int z1); + + bool intersects(int x0, int z0, int x1, int z1); + void expand(BoundingBox *other); + BoundingBox *getIntersection(BoundingBox *other); + void move(int dx, int dy, int dz); + bool isInside(int x, int y, int z); + + int getXSpan(); + int getYSpan(); + int getZSpan(); + int getXCenter(); + int getYCenter(); + int getZCenter(); + + wstring toString(); +}; \ No newline at end of file diff --git a/Minecraft.World/BowItem.cpp b/Minecraft.World/BowItem.cpp new file mode 100644 index 00000000..ed2376b0 --- /dev/null +++ b/Minecraft.World/BowItem.cpp @@ -0,0 +1,109 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.h" +#include "BowItem.h" +#include "SoundTypes.h" + +const wstring BowItem::TEXTURE_PULL[] = {L"bow_pull_0", L"bow_pull_1", L"bow_pull_2"}; + +BowItem::BowItem(int id) : Item( id ) +{ + maxStackSize = 1; + setMaxDamage(384); + + icons = NULL; +} + +void BowItem::releaseUsing(shared_ptr itemInstance, Level *level, shared_ptr player, int durationLeft) +{ + bool infiniteArrows = player->abilities.instabuild || EnchantmentHelper::getEnchantmentLevel(Enchantment::arrowInfinite->id, itemInstance) > 0; + + if (infiniteArrows || player->inventory->hasResource(Item::arrow_Id)) + { + int timeHeld = getUseDuration(itemInstance) - durationLeft; + float pow = timeHeld / (float) MAX_DRAW_DURATION; + pow = ((pow * pow) + pow * 2) / 3; + if (pow < 0.1) return; + if (pow > 1) pow = 1; + + shared_ptr arrow = shared_ptr( new Arrow(level, player, pow * 2.0f) ); + if (pow == 1) arrow->setCritArrow(true); + int damageBonus = EnchantmentHelper::getEnchantmentLevel(Enchantment::arrowBonus->id, itemInstance); + if (damageBonus > 0) + { + arrow->setBaseDamage(arrow->getBaseDamage() + (double) damageBonus * .5 + .5); + } + int knockbackBonus = EnchantmentHelper::getEnchantmentLevel(Enchantment::arrowKnockback->id, itemInstance); + if (knockbackBonus > 0) + { + arrow->setKnockback(knockbackBonus); + } + if (EnchantmentHelper::getEnchantmentLevel(Enchantment::arrowFire->id, itemInstance) > 0) + { + arrow->setOnFire(100); + } + itemInstance->hurt(1, player); + + level->playSound(player, eSoundType_RANDOM_BOW, 1.0f, 1 / (random->nextFloat() * 0.4f + 1.2f) + pow * 0.5f); + + if (infiniteArrows) + { + arrow->pickup = Arrow::PICKUP_CREATIVE_ONLY; + } + else + { + player->inventory->removeResource(Item::arrow_Id); + } + if (!level->isClientSide) level->addEntity(arrow); + } +} + +shared_ptr BowItem::useTimeDepleted(shared_ptr instance, Level *level, shared_ptr player) +{ + return instance; +} + +int BowItem::getUseDuration(shared_ptr itemInstance) +{ + return 20 * 60 * 60; +} + +UseAnim BowItem::getUseAnimation(shared_ptr itemInstance) +{ + return UseAnim_bow; +} + +shared_ptr BowItem::use(shared_ptr instance, Level *level, shared_ptr player) +{ + if (player->abilities.instabuild || player->inventory->hasResource(Item::arrow_Id)) + { + player->startUsingItem(instance, getUseDuration(instance)); + } + return instance; +} + +int BowItem::getEnchantmentValue() +{ + return 1; +} + +void BowItem::registerIcons(IconRegister *iconRegister) +{ + Item::registerIcons(iconRegister); + + icons = new Icon *[BOW_ICONS_COUNT]; + + for (int i = 0; i < BOW_ICONS_COUNT; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURE_PULL[i]); + } +} + +Icon *BowItem::getDrawnIcon(int amount) +{ + return icons[amount]; +} \ No newline at end of file diff --git a/Minecraft.World/BowItem.h b/Minecraft.World/BowItem.h new file mode 100644 index 00000000..b4eba111 --- /dev/null +++ b/Minecraft.World/BowItem.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Item.h" + +class Player; +class Level; + +class BowItem : public Item +{ +public: + static const wstring TEXTURE_PULL[]; + static const int MAX_DRAW_DURATION = 20 * 1; + +private: + static const int BOW_ICONS_COUNT = 3; + Icon **icons; + +public: + BowItem(int id); + + virtual void releaseUsing(shared_ptr itemInstance, Level *level, shared_ptr player, int durationLeft); + virtual shared_ptr useTimeDepleted(shared_ptr instance, Level *level, shared_ptr player); + virtual int getUseDuration(shared_ptr itemInstance); + virtual UseAnim getUseAnimation(shared_ptr itemInstance); + virtual shared_ptr use(shared_ptr instance, Level *level, shared_ptr player); + virtual int getEnchantmentValue(); + + //@Override + void registerIcons(IconRegister *iconRegister); + Icon *getDrawnIcon(int amount); +}; \ No newline at end of file diff --git a/Minecraft.World/BowlFoodItem.cpp b/Minecraft.World/BowlFoodItem.cpp new file mode 100644 index 00000000..f8ab0605 --- /dev/null +++ b/Minecraft.World/BowlFoodItem.cpp @@ -0,0 +1,16 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "ItemInstance.h" +#include "BowlFoodItem.h" + +BowlFoodItem::BowlFoodItem(int id, int nutrition) : FoodItem( id, nutrition, false ) +{ + setMaxStackSize(1); +} + +shared_ptr BowlFoodItem::useTimeDepleted(shared_ptr instance, Level *level, shared_ptr player) +{ + FoodItem::useTimeDepleted(instance, level, player); + + return shared_ptr(new ItemInstance(Item::bowl)); +} \ No newline at end of file diff --git a/Minecraft.World/BowlFoodItem.h b/Minecraft.World/BowlFoodItem.h new file mode 100644 index 00000000..77980bc2 --- /dev/null +++ b/Minecraft.World/BowlFoodItem.h @@ -0,0 +1,14 @@ +#pragma once + +#include "FoodItem.h" + +class Player; +class Level; + +class BowlFoodItem : public FoodItem +{ +public: + BowlFoodItem(int id, int nutrition); + + shared_ptr useTimeDepleted(shared_ptr instance, Level *level, shared_ptr player); +}; \ No newline at end of file diff --git a/Minecraft.World/BreakDoorGoal.cpp b/Minecraft.World/BreakDoorGoal.cpp new file mode 100644 index 00000000..e4581c67 --- /dev/null +++ b/Minecraft.World/BreakDoorGoal.cpp @@ -0,0 +1,65 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.h" +#include "SharedConstants.h" +#include "BreakDoorGoal.h" + +BreakDoorGoal::BreakDoorGoal(Mob *mob) : DoorInteractGoal(mob) +{ + breakTime = 0; + lastBreakProgress = -1; +} + +bool BreakDoorGoal::canUse() +{ + if (!DoorInteractGoal::canUse()) return false; + return !doorTile->isOpen(mob->level, doorX, doorY, doorZ); +} + +void BreakDoorGoal::start() +{ + DoorInteractGoal::start(); + breakTime = 0; +} + +bool BreakDoorGoal::canContinueToUse() +{ + double d = mob->distanceToSqr(doorX, doorY, doorZ); + return breakTime <= DOOR_BREAK_TIME && !doorTile->isOpen(mob->level, doorX, doorY, doorZ) && d < 2 * 2; +} + +void BreakDoorGoal::stop() +{ + DoorInteractGoal::stop(); + mob->level->destroyTileProgress(mob->entityId, doorX, doorY, doorZ, -1); +} + +void BreakDoorGoal::tick() +{ + DoorInteractGoal::tick(); + if (mob->getRandom()->nextInt(20) == 0) + { + mob->level->levelEvent(LevelEvent::SOUND_ZOMBIE_WOODEN_DOOR, doorX, doorY, doorZ, 0); + } + + breakTime++; + + int progress = (int) (breakTime / (float) DOOR_BREAK_TIME * 10); + if (progress != lastBreakProgress) + { + mob->level->destroyTileProgress(mob->entityId, doorX, doorY, doorZ, progress); + lastBreakProgress = progress; + } + + if (breakTime == DOOR_BREAK_TIME) + { + if (mob->level->difficulty == Difficulty::HARD) + { + mob->level->setTile(doorX, doorY, doorZ, 0); + mob->level->levelEvent(LevelEvent::SOUND_ZOMBIE_DOOR_CRASH, doorX, doorY, doorZ, 0); + mob->level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, doorX, doorY, doorZ, doorTile->id); + } + } +} \ No newline at end of file diff --git a/Minecraft.World/BreakDoorGoal.h b/Minecraft.World/BreakDoorGoal.h new file mode 100644 index 00000000..5c546d38 --- /dev/null +++ b/Minecraft.World/BreakDoorGoal.h @@ -0,0 +1,22 @@ +#pragma once + +#include "DoorInteractGoal.h" +#include "SharedConstants.h" + +class BreakDoorGoal : public DoorInteractGoal +{ +private: + static const int DOOR_BREAK_TIME = SharedConstants::TICKS_PER_SECOND * 12; + + int breakTime; + int lastBreakProgress; + +public: + BreakDoorGoal(Mob *mob); + + virtual bool canUse(); + virtual void start(); + virtual void stop(); + virtual bool canContinueToUse(); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/BreedGoal.cpp b/Minecraft.World/BreedGoal.cpp new file mode 100644 index 00000000..2145d371 --- /dev/null +++ b/Minecraft.World/BreedGoal.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "BreedGoal.h" +#include "ExperienceOrb.h" + +#include "GenericStats.h" + +BreedGoal::BreedGoal(Animal *animal, float speed) +{ + partner = weak_ptr(); + loveTime = 0; + + this->animal = animal; + this->level = animal->level; + this->speed = speed; + setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag); +} + +bool BreedGoal::canUse() +{ + if (!animal->isInLove()) return false; + partner = weak_ptr(getFreePartner()); + return partner.lock() != NULL; +} + +bool BreedGoal::canContinueToUse() +{ + return partner.lock() != NULL && partner.lock()->isAlive() && partner.lock()->isInLove() && loveTime < 20 * 3; +} + +void BreedGoal::stop() +{ + partner = weak_ptr(); + loveTime = 0; +} + +void BreedGoal::tick() +{ + animal->getLookControl()->setLookAt(partner.lock(), 10, animal->getMaxHeadXRot()); + animal->getNavigation()->moveTo(partner.lock(), speed); + ++loveTime; + if (loveTime == 20 * 3) breed(); +} + +shared_ptr BreedGoal::getFreePartner() +{ + float r = 8; + vector > *others = level->getEntitiesOfClass(typeid(*animal), animal->bb->grow(r, r, r)); + for(AUTO_VAR(it, others->begin()); it != others->end(); ++it) + { + shared_ptr p = dynamic_pointer_cast(*it); + if (animal->canMate(p)) + { + delete others; + return p; + } + } + delete others; + return nullptr; +} + +void BreedGoal::breed() +{ + shared_ptr offspring = animal->getBreedOffspring(partner.lock()); + animal->setDespawnProtected(); + partner.lock()->setDespawnProtected(); + if (offspring == NULL) + { + // This will be NULL if we've hit our limits for spawning any particular type of animal... reset things as normally as we can, without actually producing any offspring + animal->resetLove(); + partner.lock()->resetLove(); + return; + } + + shared_ptr loveCause = animal->getLoveCause(); + if (loveCause == NULL && partner.lock()->getLoveCause() != NULL) + { + loveCause = partner.lock()->getLoveCause(); + } + + if (loveCause != NULL) + { + // Record mob bred stat. + loveCause->awardStat(GenericStats::breedEntity(offspring->GetType()),GenericStats::param_breedEntity(offspring->GetType())); + + if (animal->GetType() == eTYPE_COW) + { + //loveCause->awardStat(Achievements.breedCow); + } + } + + animal->setAge(5 * 60 * 20); + partner.lock()->setAge(5 * 60 * 20); + animal->resetLove(); + partner.lock()->resetLove(); + offspring->setAge(-20 * 60 * 20); + offspring->moveTo(animal->x, animal->y, animal->z, 0, 0); + offspring->setDespawnProtected(); + level->addEntity(offspring); + + Random *random = animal->getRandom(); + for (int i = 0; i < 7; i++) + { + double xa = random->nextGaussian() * 0.02; + double ya = random->nextGaussian() * 0.02; + double za = random->nextGaussian() * 0.02; + level->addParticle(eParticleType_heart, animal->x + random->nextFloat() * animal->bbWidth * 2 - animal->bbWidth, animal->y + .5f + random->nextFloat() * animal->bbHeight, animal->z + random->nextFloat() + * animal->bbWidth * 2 - animal->bbWidth, xa, ya, za); + } + // 4J-PB - Fix for 106869- Customer Encountered: TU12: Content: Gameplay: Breeding animals does not give any Experience Orbs. + level->addEntity( shared_ptr( new ExperienceOrb(level, animal->x, animal->y, animal->z, random->nextInt(7) + 1) ) ); +} diff --git a/Minecraft.World/BreedGoal.h b/Minecraft.World/BreedGoal.h new file mode 100644 index 00000000..d2b47e7d --- /dev/null +++ b/Minecraft.World/BreedGoal.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Goal.h" + +class Animal; +class Level; + +class BreedGoal : public Goal +{ +private: + Animal *animal; // Owner of this goal + Level *level; + weak_ptr partner; + int loveTime; + float speed; + +public: + BreedGoal(Animal *animal, float speed); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void stop(); + virtual void tick(); + +private: + shared_ptr getFreePartner(); + void breed(); + +public: + // 4J Added override to update ai elements when loading entity from schematics + virtual void setLevel(Level *level) { this->level = level; } +}; \ No newline at end of file diff --git a/Minecraft.World/BrewingStandMenu.cpp b/Minecraft.World/BrewingStandMenu.cpp new file mode 100644 index 00000000..af1fac5c --- /dev/null +++ b/Minecraft.World/BrewingStandMenu.cpp @@ -0,0 +1,239 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.alchemy.h" +#include "net.minecraft.stats.h" +#include "BrewingStandMenu.h" + +BrewingStandMenu::BrewingStandMenu(shared_ptr inventory, shared_ptr brewingStand) +{ + tc = 0; + + this->brewingStand = brewingStand; + + addSlot(new PotionSlot(dynamic_pointer_cast( inventory->player->shared_from_this() ), brewingStand, 0, 56, 46)); + addSlot(new PotionSlot(dynamic_pointer_cast( inventory->player->shared_from_this() ), brewingStand, 1, 79, 53)); + addSlot(new PotionSlot(dynamic_pointer_cast( inventory->player->shared_from_this() ), brewingStand, 2, 102, 46)); + ingredientSlot = addSlot(new IngredientsSlot(brewingStand, 3, 79, 17)); + + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x + y * 9 + 9, 8 + x * 18, 84 + y * 18)); + } + } + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x, 8 + x * 18, 142)); + } +} + +void BrewingStandMenu::addSlotListener(ContainerListener *listener) +{ + AbstractContainerMenu::addSlotListener(listener); + listener->setContainerData(this, 0, brewingStand->getBrewTime()); +} + +void BrewingStandMenu::broadcastChanges() +{ + AbstractContainerMenu::broadcastChanges(); + + //for (int i = 0; i < containerListeners->size(); i++) + for(AUTO_VAR(it, containerListeners->begin()); it != containerListeners->end(); ++it) + { + ContainerListener *listener = *it; //containerListeners.at(i); + if (tc != brewingStand->getBrewTime()) + { + listener->setContainerData(this, 0, brewingStand->getBrewTime()); + } + } + tc = brewingStand->getBrewTime(); +} + +void BrewingStandMenu::setData(int id, int value) +{ + if (id == 0) brewingStand->setBrewTime(value); +} + +bool BrewingStandMenu::stillValid(shared_ptr player) +{ + return brewingStand->stillValid(player); +} + +shared_ptr BrewingStandMenu::quickMoveStack(shared_ptr player, int slotIndex) +{ + shared_ptr clicked = nullptr; + Slot *slot = slots->at(slotIndex); + Slot *IngredientSlot = slots->at(INGREDIENT_SLOT); + Slot *PotionSlot1 = slots->at(BOTTLE_SLOT_START); + Slot *PotionSlot2 = slots->at(BOTTLE_SLOT_START+1); + Slot *PotionSlot3 = slots->at(BOTTLE_SLOT_START+2); + + if (slot != NULL && slot->hasItem()) + { + shared_ptr stack = slot->getItem(); + clicked = stack->copy(); + + if ((slotIndex >= BOTTLE_SLOT_START && slotIndex <= BOTTLE_SLOT_END) || (slotIndex == INGREDIENT_SLOT)) + { + if (!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, true)) + { + return nullptr; + } + slot->onQuickCraft(stack, clicked); + } + else if (!ingredientSlot->hasItem() && ingredientSlot->mayPlace(stack)) + { + if (!moveItemStackTo(stack, INGREDIENT_SLOT, INGREDIENT_SLOT + 1, false)) + { + return nullptr; + } + } + else if (PotionSlot::mayPlaceItem(clicked)) + { + if (!moveItemStackTo(stack, BOTTLE_SLOT_START, BOTTLE_SLOT_END + 1, false)) + { + return nullptr; + } + } + else if (slotIndex >= INV_SLOT_START && slotIndex < INV_SLOT_END) + { + // 4J-PB - if the item is an ingredient, quickmove it into the ingredient slot + if( (Item::items[stack->id]->hasPotionBrewingFormula() || (stack->id == Item::netherStalkSeeds_Id) ) && + (!IngredientSlot->hasItem() || (stack->id==IngredientSlot->getItem()->id) ) ) + { + if(!moveItemStackTo(stack, INGREDIENT_SLOT, INGREDIENT_SLOT+1, false)) + { + return nullptr; + } + } + // potion? + else if((stack->id==Item::potion_Id) &&(!PotionSlot1->hasItem() || !PotionSlot2->hasItem() || !PotionSlot3->hasItem())) + { + if(!moveItemStackTo(stack, BOTTLE_SLOT_START, BOTTLE_SLOT_END+1, false)) + { + return nullptr; + } + } + else if (!moveItemStackTo(stack, USE_ROW_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + } + else if (slotIndex >= USE_ROW_SLOT_START && slotIndex < USE_ROW_SLOT_END) + { + // 4J-PB - if the item is an ingredient, quickmove it into the ingredient slot + if((Item::items[stack->id]->hasPotionBrewingFormula() || (stack->id == Item::netherStalkSeeds_Id)) && + (!IngredientSlot->hasItem() || (stack->id==IngredientSlot->getItem()->id) )) + { + if(!moveItemStackTo(stack, INGREDIENT_SLOT, INGREDIENT_SLOT+1, false)) + { + return nullptr; + } + } + // potion? + else if((stack->id==Item::potion_Id) &&(!PotionSlot1->hasItem() || !PotionSlot2->hasItem() || !PotionSlot3->hasItem())) + { + if(!moveItemStackTo(stack, BOTTLE_SLOT_START, BOTTLE_SLOT_END+1, false)) + { + return nullptr; + } + } + else if (!moveItemStackTo(stack, INV_SLOT_START, INV_SLOT_END, false)) + { + return nullptr; + } + } + else + { + if (!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + } + if (stack->count == 0) + { + slot->set(nullptr); + } + else + { + slot->setChanged(); + } + if (stack->count == clicked->count) + { + return nullptr; + } + else + { + slot->onTake(player, stack); + } + } + return clicked; +} + +BrewingStandMenu::PotionSlot::PotionSlot(shared_ptr player, shared_ptr container, int slot, int x, int y) : Slot(container, slot, x, y) +{ + this->player = player; +} + +bool BrewingStandMenu::PotionSlot::mayPlace(shared_ptr item) +{ + return mayPlaceItem(item); +} + +int BrewingStandMenu::PotionSlot::getMaxStackSize() +{ + return 1; +} + +void BrewingStandMenu::PotionSlot::onTake(shared_ptr player, shared_ptr carried) +{ + carried->onCraftedBy(this->player->level, dynamic_pointer_cast( this->player->shared_from_this() ), 1); + if (carried->id == Item::potion_Id && carried->getAuxValue() > 0) + this->player->awardStat(GenericStats::potion(),GenericStats::param_potion()); + Slot::onTake(player, carried); +} + +bool BrewingStandMenu::PotionSlot::mayCombine(shared_ptr second) +{ + return false; +} + +bool BrewingStandMenu::PotionSlot::mayPlaceItem(shared_ptr item) +{ + return item != NULL && (item->id == Item::potion_Id || item->id == Item::glassBottle_Id); +} + + + +BrewingStandMenu::IngredientsSlot::IngredientsSlot(shared_ptr container, int slot, int x, int y) : Slot(container, slot, x ,y) +{ +} + +bool BrewingStandMenu::IngredientsSlot::mayPlace(shared_ptr item) +{ + if (item != NULL) + { + if (PotionBrewing::SIMPLIFIED_BREWING) + { + return Item::items[item->id]->hasPotionBrewingFormula(); + } + else + { + return Item::items[item->id]->hasPotionBrewingFormula() || item->id == Item::netherStalkSeeds_Id || item->id == Item::bucket_water_Id; + } + } + return false; +} + +bool BrewingStandMenu::IngredientsSlot::mayCombine(shared_ptr second) +{ + return false; +} + +int BrewingStandMenu::IngredientsSlot::getMaxStackSize() +{ + return 64; +} diff --git a/Minecraft.World/BrewingStandMenu.h b/Minecraft.World/BrewingStandMenu.h new file mode 100644 index 00000000..1f8d705d --- /dev/null +++ b/Minecraft.World/BrewingStandMenu.h @@ -0,0 +1,65 @@ +#pragma once + +#include "AbstractContainerMenu.h" +#include "Slot.h" + +class BrewingStandTileEntity; +class Inventory; +class Player; +class Container; + +class BrewingStandMenu : public AbstractContainerMenu +{ + // 4J Stu - Made public so that we can access these from the XUI menus +public: + static const int INGREDIENT_SLOT = 3; + static const int BOTTLE_SLOT_START = 0; + static const int BOTTLE_SLOT_END = 2; + static const int INV_SLOT_START = INGREDIENT_SLOT + 1; + static const int INV_SLOT_END = INV_SLOT_START + 9 * 3; + static const int USE_ROW_SLOT_START = INV_SLOT_END; + static const int USE_ROW_SLOT_END = USE_ROW_SLOT_START + 9; + +private: + shared_ptr brewingStand; + Slot *ingredientSlot; + +public: + BrewingStandMenu(shared_ptr inventory, shared_ptr brewingStand); + +private: + int tc; + +public: + virtual void addSlotListener(ContainerListener *listener); + virtual void broadcastChanges(); + virtual void setData(int id, int value); + virtual bool stillValid(shared_ptr player); + virtual shared_ptr quickMoveStack(shared_ptr player, int slotIndex); + +private: + class PotionSlot : public Slot + { + private: + shared_ptr player; + + public: + PotionSlot(shared_ptr player, shared_ptr container, int slot, int x, int y); + + virtual bool mayPlace(shared_ptr item); + virtual int getMaxStackSize(); + virtual void onTake(shared_ptr player, shared_ptr carried); + static bool mayPlaceItem(shared_ptr item); + virtual bool mayCombine(shared_ptr item); // 4J Added + }; + + class IngredientsSlot : public Slot + { + public: + IngredientsSlot(shared_ptr container, int slot, int x, int y); + + virtual bool mayPlace(shared_ptr item); + virtual int getMaxStackSize(); + virtual bool mayCombine(shared_ptr item); // 4J Added + }; +}; \ No newline at end of file diff --git a/Minecraft.World/BrewingStandTile.cpp b/Minecraft.World/BrewingStandTile.cpp new file mode 100644 index 00000000..5f09ba02 --- /dev/null +++ b/Minecraft.World/BrewingStandTile.cpp @@ -0,0 +1,136 @@ +#include "stdafx.h" +#include "BrewingStandTile.h" +#include "BrewingStandTileEntity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.h" + +const wstring BrewingStandTile::TEXTURE_BASE = L"brewingStand_base"; + +BrewingStandTile::BrewingStandTile(int id) : EntityTile(id, Material::metal, isSolidRender()) +{ + random = new Random(); + iconBase = NULL; +} + +BrewingStandTile::~BrewingStandTile() +{ + delete random; +} + +bool BrewingStandTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +int BrewingStandTile::getRenderShape() +{ + return SHAPE_BREWING_STAND; +} + +shared_ptr BrewingStandTile::newTileEntity(Level *level) +{ + return shared_ptr(new BrewingStandTileEntity()); +} + +bool BrewingStandTile::isCubeShaped() +{ + return false; +} + +void BrewingStandTile::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source) +{ + setShape(7.0f / 16.0f, 0, 7.0f / 16.0f, 9.0f / 16.0f, 14.0f / 16.0f, 9.0f / 16.0f); + EntityTile::addAABBs(level, x, y, z, box, boxes, source); + updateDefaultShape(); + EntityTile::addAABBs(level, x, y, z, box, boxes, source); +} + +void BrewingStandTile::updateDefaultShape() +{ + setShape(0, 0, 0, 1, 2.0f / 16.0f, 1); +} + +bool BrewingStandTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if(soundOnly) return false; + + if (level->isClientSide) + { + return true; + } + shared_ptr brewingStand = dynamic_pointer_cast(level->getTileEntity(x, y, z)); + if (brewingStand != NULL) player->openBrewingStand(brewingStand); + + return true; +} + +void BrewingStandTile::animateTick(Level *level, int xt, int yt, int zt, Random *random) +{ + double x = xt + 0.4f + random->nextFloat() * 0.2f; + double y = yt + 0.7f + random->nextFloat() * 0.3f; + double z = zt + 0.4f + random->nextFloat() * 0.2f; + + + level->addParticle(eParticleType_smoke, x, y, z, 0, 0, 0); +} + +void BrewingStandTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + shared_ptr tileEntity = level->getTileEntity(x, y, z); + if (tileEntity != NULL && ( dynamic_pointer_cast(tileEntity) != NULL) ) + { + shared_ptr container = dynamic_pointer_cast(tileEntity); + for (int i = 0; i < container->getContainerSize(); i++) + { + shared_ptr item = container->getItem(i); + if (item != NULL) + { + float xo = random->nextFloat() * 0.8f + 0.1f; + float yo = random->nextFloat() * 0.8f + 0.1f; + float zo = random->nextFloat() * 0.8f + 0.1f; + + while (item->count > 0) + { + int count = random->nextInt(21) + 10; + if (count > item->count) count = item->count; + item->count -= count; + + shared_ptr itemEntity = shared_ptr(new ItemEntity(level, x + xo, y + yo, z + zo, shared_ptr( new ItemInstance(item->id, count, item->getAuxValue())))); + float pow = 0.05f; + itemEntity->xd = (float) random->nextGaussian() * pow; + itemEntity->yd = (float) random->nextGaussian() * pow + 0.2f; + itemEntity->zd = (float) random->nextGaussian() * pow; + if (item->hasTag()) + { + itemEntity->getItem()->setTag((CompoundTag *) item->getTag()->copy()); + } + level->addEntity(itemEntity); + } + } + } + } + EntityTile::onRemove(level, x, y, z, id, data); +} + +int BrewingStandTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::brewingStand_Id; +} + +int BrewingStandTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Item::brewingStand_Id; +} + +void BrewingStandTile::registerIcons(IconRegister *iconRegister) +{ + EntityTile::registerIcons(iconRegister); + iconBase = iconRegister->registerIcon(TEXTURE_BASE); +} + +Icon *BrewingStandTile::getBaseTexture() +{ + return iconBase; +} \ No newline at end of file diff --git a/Minecraft.World/BrewingStandTile.h b/Minecraft.World/BrewingStandTile.h new file mode 100644 index 00000000..fc8ff462 --- /dev/null +++ b/Minecraft.World/BrewingStandTile.h @@ -0,0 +1,32 @@ +#pragma once +#include "EntityTile.h" + +class IconRegister; +class ChunkRebuildData; + +class BrewingStandTile : public EntityTile +{ + friend ChunkRebuildData; +public: + static const wstring TEXTURE_BASE; +private: + Random *random; + Icon *iconBase; + +public: + BrewingStandTile(int id); + ~BrewingStandTile(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual int getRenderShape(); + virtual shared_ptr newTileEntity(Level *level); + virtual bool isCubeShaped(); + virtual void addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source); + virtual void updateDefaultShape(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + virtual void animateTick(Level *level, int xt, int yt, int zt, Random *random); + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int cloneTileId(Level *level, int x, int y, int z); + void registerIcons(IconRegister *iconRegister); + Icon *getBaseTexture(); +}; \ No newline at end of file diff --git a/Minecraft.World/BrewingStandTileEntity.cpp b/Minecraft.World/BrewingStandTileEntity.cpp new file mode 100644 index 00000000..20cb3737 --- /dev/null +++ b/Minecraft.World/BrewingStandTileEntity.cpp @@ -0,0 +1,433 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "BrewingStandTileEntity.h" +#include "SharedConstants.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.alchemy.h" + + + +BrewingStandTileEntity::BrewingStandTileEntity() +{ + brewTime = 0; + items = ItemInstanceArray(4); +} + +BrewingStandTileEntity::~BrewingStandTileEntity() +{ + delete [] items.data; +} + +int BrewingStandTileEntity::getName() +{ + return IDS_TILE_BREWINGSTAND; +} + +unsigned int BrewingStandTileEntity::getContainerSize() +{ + return items.length; +} + +void BrewingStandTileEntity::tick() +{ + if (brewTime > 0) + { + brewTime--; + + if (brewTime == 0) + { + // apply ingredients to all potions + doBrew(); + setChanged(); + } + else if (!isBrewable()) + { + brewTime = 0; + setChanged(); + } + else if (ingredientId != items[INGREDIENT_SLOT]->id) + { + brewTime = 0; + setChanged(); + } + } + else if (isBrewable()) + { + brewTime = SharedConstants::TICKS_PER_SECOND * PotionBrewing::BREWING_TIME_SECONDS; + ingredientId = items[INGREDIENT_SLOT]->id; + } + + int newCount = getPotionBits(); + if (newCount != lastPotionCount) + { + lastPotionCount = newCount; + level->setData(x, y, z, newCount); + } + + TileEntity::tick(); +} + +int BrewingStandTileEntity::getBrewTime() +{ + return brewTime; +} + +bool BrewingStandTileEntity::isBrewable() +{ + if (items[INGREDIENT_SLOT] == NULL || items[INGREDIENT_SLOT]->count <= 0) + { + return false; + } + shared_ptr ingredient = items[INGREDIENT_SLOT]; + if (PotionBrewing::SIMPLIFIED_BREWING) + { + if (!Item::items[ingredient->id]->hasPotionBrewingFormula()) + { + return false; + } + + bool oneResult = false; + for (int dest = 0; dest < 3; dest++) + { + if (items[dest] != NULL && items[dest]->id == Item::potion_Id) + { + int currentBrew = items[dest]->getAuxValue(); + int newBrew = NORMALISE_POTION_AUXVAL( applyIngredient(currentBrew, ingredient) ); + + if (!PotionItem::isThrowable(currentBrew) && PotionItem::isThrowable(newBrew)) + { + oneResult = true; + break; + } + + vector *currentEffects = Item::potion->getMobEffects(currentBrew); + vector *newEffects = Item::potion->getMobEffects(newBrew); + + // 4J - this code replaces an expression "currentEffects.equals(newEffects)" in the java. + // TODO - find out whether actually checking pointers to MobEffectInstance classes for equality + // is of any use + bool equals = false; + if( ( currentEffects != NULL ) && ( newEffects != NULL ) ) + { + if( currentEffects->size() == newEffects->size() ) + { + if( std::equal(currentEffects->begin(), currentEffects->end(), newEffects->begin() ) ) + { + equals = true; + } + } + } + + if ((currentBrew > 0 && currentEffects == newEffects) || + (currentEffects != NULL && (equals || newEffects == NULL))) + { + } + else if (currentBrew != newBrew) + { + oneResult = true; + break; + } + } + } + return oneResult; + + } + else + { + if (!Item::items[ingredient->id]->hasPotionBrewingFormula() && ingredient->id != Item::bucket_water_Id && ingredient->id != Item::netherStalkSeeds_Id) + { + return false; + } + bool isWater = ingredient->id == Item::bucket_water_Id; + + // at least one destination potion must have a result + bool oneResult = false; + for (int dest = 0; dest < 3; dest++) + { + if (items[dest] != NULL && items[dest]->id == Item::potion_Id) + { + int currentBrew = items[dest]->getAuxValue(); + int newBrew = NORMALISE_POTION_AUXVAL( applyIngredient(currentBrew, ingredient) ); + if (currentBrew != newBrew) + { + oneResult = true; + break; + } + } + else if (isWater && items[dest] != NULL && items[dest]->id == Item::glassBottle_Id) + { + oneResult = true; + break; + } + } + return oneResult; + } +} + +void BrewingStandTileEntity::doBrew() +{ + if (!isBrewable()) + { + return; + } + + shared_ptr ingredient = items[INGREDIENT_SLOT]; + + if (PotionBrewing::SIMPLIFIED_BREWING) + { + for (int dest = 0; dest < 3; dest++) + { + if (items[dest] != NULL && items[dest]->id == Item::potion_Id) + { + int currentBrew = items[dest]->getAuxValue(); + int newBrew = NORMALISE_POTION_AUXVAL( applyIngredient(currentBrew, ingredient) ); + + vector *currentEffects = Item::potion->getMobEffects(currentBrew); + vector *newEffects = Item::potion->getMobEffects(newBrew); + + // 4J - this code replaces an expression "currentEffects.equals(newEffects)" in the java. + // TODO - find out whether actually checking pointers to MobEffectInstance classes for equality + // is of any use + bool equals = false; + if( ( currentEffects != NULL ) && ( newEffects != NULL ) ) + { + if( currentEffects->size() == newEffects->size() ) + { + if( std::equal(currentEffects->begin(), currentEffects->end(), newEffects->begin() ) ) + { + equals = true; + } + } + } + + if ((currentBrew > 0 && currentEffects == newEffects) || + (currentEffects != NULL && (equals || newEffects == NULL))) + { + if (!PotionItem::isThrowable(currentBrew) && PotionItem::isThrowable(newBrew)) + { + items[dest]->setAuxValue(newBrew); + } + + } + else if (currentBrew != newBrew) + { + items[dest]->setAuxValue(newBrew); + } + + } + } + + } + else + { + bool isWater = ingredient->id == Item::bucket_water_Id; + + for (int dest = 0; dest < 3; dest++) + { + if (items[dest] != NULL && items[dest]->id == Item::potion_Id) + { + int currentBrew = items[dest]->getAuxValue(); + int newBrew = NORMALISE_POTION_AUXVAL( applyIngredient(currentBrew, ingredient) ); + items[dest]->setAuxValue(newBrew); + } + else if (isWater && items[dest] != NULL && items[dest]->id == Item::glassBottle_Id) + { + items[dest] = shared_ptr(new ItemInstance(Item::potion)); + } + } + } + + if (Item::items[ingredient->id]->hasCraftingRemainingItem()) + { + items[INGREDIENT_SLOT] = shared_ptr(new ItemInstance(Item::items[ingredient->id]->getCraftingRemainingItem())); + } + else + { + items[INGREDIENT_SLOT]->count--; + if (items[INGREDIENT_SLOT]->count <= 0) + { + items[INGREDIENT_SLOT] = nullptr; + } + } +} + +int BrewingStandTileEntity::applyIngredient(int currentBrew, shared_ptr ingredient) +{ + if (ingredient == NULL) + { + return currentBrew; + } + if (!PotionBrewing::SIMPLIFIED_BREWING) + { +#if !(_SIMPLIFIED_BREWING) + // 4J Stu - SIMPLIFIED_BREWING is on, so we never use this + if (ingredient->id == Item::bucket_water_Id) + { + return PotionBrewing::applyBrew(currentBrew, PotionBrewing::MOD_WATER); + } + if (ingredient->id == Item::netherStalkSeeds_Id) + { + return PotionBrewing::stirr(currentBrew); + } +#endif + } + if (Item::items[ingredient->id]->hasPotionBrewingFormula()) + { + return PotionBrewing::applyBrew(currentBrew, Item::items[ingredient->id]->getPotionBrewingFormula()); + } + return currentBrew; +} + +void BrewingStandTileEntity::load(CompoundTag *base) +{ + TileEntity::load(base); + + ListTag *inventoryList = (ListTag *) base->getList(L"Items"); + delete [] items.data; + items = ItemInstanceArray(getContainerSize()); + for (int i = 0; i < inventoryList->size(); i++) + { + CompoundTag *tag = inventoryList->get(i); + int slot = tag->getByte(L"Slot"); + if (slot >= 0 && slot < items.length) items[slot] = ItemInstance::fromTag(tag); + } + + brewTime = base->getShort(L"BrewTime"); +} + +void BrewingStandTileEntity::save(CompoundTag *base) +{ + TileEntity::save(base); + + base->putShort(L"BrewTime", (short) (brewTime)); + ListTag *listTag = new ListTag(); + + for (int i = 0; i < items.length; i++) + { + if (items[i] != NULL) + { + CompoundTag *tag = new CompoundTag(); + tag->putByte(L"Slot", (byte) i); + items[i]->save(tag); + listTag->add(tag); + } + } + base->put(L"Items", listTag); +} + +shared_ptr BrewingStandTileEntity::getItem(unsigned int slot) +{ + if (slot >= 0 && slot < items.length) + { + return items[slot]; + } + return nullptr; +} + +shared_ptr BrewingStandTileEntity::removeItem(unsigned int slot, int count) +{ + // 4J Stu - Changed the implementation of this function to be the same as ChestTileEntity to enable the "Pickup Half" + // option on the ingredients slot + // Fix for #65373 - TU8: Content: UI: Command "Take Half" in the Brewing Stand interface doesn't work as intended. + + if (slot >= 0 && slot < items.length && items[slot] != NULL) + { + if (items[slot]->count <= count) + { + shared_ptr item = items[slot]; + items[slot] = nullptr; + this->setChanged(); + // 4J Stu - Fix for duplication glitch + if(item->count <= 0) return nullptr; + return item; + } + else + { + shared_ptr i = items[slot]->remove(count); + if (items[slot]->count == 0) items[slot] = nullptr; + this->setChanged(); + // 4J Stu - Fix for duplication glitch + if(i->count <= 0) return nullptr; + return i; + } + } + return nullptr; +} + +shared_ptr BrewingStandTileEntity::removeItemNoUpdate(int slot) +{ + if (slot >= 0 && slot < items.length) + { + shared_ptr item = items[slot]; + items[slot] = nullptr; + return item; + } + return nullptr; +} + +void BrewingStandTileEntity::setItem(unsigned int slot, shared_ptr item) +{ + if (slot >= 0 && slot < items.length) + { + items[slot] = item; + } +} + +int BrewingStandTileEntity::getMaxStackSize() +{ + return 1; +} + +bool BrewingStandTileEntity::stillValid(shared_ptr player) +{ + if (level->getTileEntity(x, y, z) != shared_from_this()) return false; + if (player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) > 8 * 8) return false; + return true; +} + +void BrewingStandTileEntity::startOpen() +{ +} + +void BrewingStandTileEntity::stopOpen() +{ +} + +void BrewingStandTileEntity::setBrewTime(int value) +{ + this->brewTime = value; +} + +int BrewingStandTileEntity::getPotionBits() +{ + int newCount = 0; + for (int potion = 0; potion < 3; potion++) + { + if (items[potion] != NULL) + { + newCount |= (1 << potion); + } + } + return newCount; +} + +// 4J Added +shared_ptr BrewingStandTileEntity::clone() +{ + shared_ptr result = shared_ptr( new BrewingStandTileEntity() ); + TileEntity::clone(result); + + result->brewTime = brewTime; + result->lastPotionCount = lastPotionCount; + result->ingredientId = ingredientId; + + for (unsigned int i = 0; i < items.length; i++) + { + if (items.data[i] != NULL) + { + result->items.data[i] = ItemInstance::clone(items.data[i]); + } + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/BrewingStandTileEntity.h b/Minecraft.World/BrewingStandTileEntity.h new file mode 100644 index 00000000..733e1073 --- /dev/null +++ b/Minecraft.World/BrewingStandTileEntity.h @@ -0,0 +1,51 @@ +#pragma once +#include "TileEntity.h" +#include "Container.h" + +class BrewingStandTileEntity : public TileEntity, public Container +{ +public: + eINSTANCEOF GetType() { return eTYPE_BREWINGSTANDTILEENTITY; } + static TileEntity *create() { return new BrewingStandTileEntity(); } + +private: + ItemInstanceArray items; + static const int INGREDIENT_SLOT = 3; + + int brewTime; + int lastPotionCount; + int ingredientId; + +public: + BrewingStandTileEntity(); + ~BrewingStandTileEntity(); + virtual int getName(); + virtual unsigned int getContainerSize(); + virtual void tick(); + + int getBrewTime(); + +private: + bool isBrewable(); + void doBrew(); + + int applyIngredient(int currentBrew, shared_ptr ingredient); + +public: + virtual void load(CompoundTag *base); + virtual void save(CompoundTag *base); + virtual shared_ptr getItem(unsigned int slot); + virtual shared_ptr removeItem(unsigned int slot, int i); + virtual shared_ptr removeItemNoUpdate(int slot); + virtual void setItem(unsigned int slot, shared_ptr item); + virtual int getMaxStackSize(); + virtual bool stillValid(shared_ptr player); + virtual void startOpen(); + virtual void stopOpen(); + virtual void setBrewTime(int value); + virtual void setChanged() {} // 4J added + int getPotionBits(); + + // 4J Added + virtual shared_ptr clone(); +}; \ No newline at end of file diff --git a/Minecraft.World/BucketItem.cpp b/Minecraft.World/BucketItem.cpp new file mode 100644 index 00000000..d3570cd7 --- /dev/null +++ b/Minecraft.World/BucketItem.cpp @@ -0,0 +1,261 @@ +#include "stdafx.h" +#include "JavaMath.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.stats.h" +#include "Material.h" +#include "ItemInstance.h" +#include "BucketItem.h" +#include "..\Minecraft.Client\LocalPlayer.h" +#include "..\Minecraft.Client\ServerPlayer.h" +#include "..\Minecraft.Client\PlayerConnection.h" +#include "..\Minecraft.World\ChatPacket.h" +#include "SoundTypes.h" + +BucketItem::BucketItem(int id, int content) : Item( id ) +{ + maxStackSize = 1; + this->content = content; +} + +bool BucketItem::TestUse(Level *level, shared_ptr player) +{ +// double x = player->xo + (player->x - player->xo); +// double y = player->yo + (player->y - player->yo) + 1.62 - player->heightOffset; +// double z = player->zo + (player->z - player->zo); + + bool pickLiquid = content == 0; + HitResult *hr = getPlayerPOVHitResult(level, player, pickLiquid); + if (hr == NULL) return false; + + if (hr->type == HitResult::TILE) + { + int xt = hr->x; + int yt = hr->y; + int zt = hr->z; + + if (!level->mayInteract(player, xt, yt, zt, content)) + { + delete hr; + return false; + } + + if (content == 0) + { + if (!player->mayBuild(xt, yt, zt)) return false; + if (level->getMaterial(xt, yt, zt) == Material::water && level->getData(xt, yt, zt) == 0) + { + delete hr; + return true; + } + if (level->getMaterial(xt, yt, zt) == Material::lava && level->getData(xt, yt, zt) == 0) + { + delete hr; + return true; + } + } + else if (content < 0) + { + delete hr; + return true; + } + else + { + if (hr->f == 0) yt--; + if (hr->f == 1) yt++; + if (hr->f == 2) zt--; + if (hr->f == 3) zt++; + if (hr->f == 4) xt--; + if (hr->f == 5) xt++; + + if (!player->mayBuild(xt, yt, zt)) return false; + + if (level->isEmptyTile(xt, yt, zt) || !level->getMaterial(xt, yt, zt)->isSolid()) + { + delete hr; + return true; + } + } + } + else + { + if (content == 0) + { + if (hr->entity->GetType() == eTYPE_COW) + { + delete hr; + return true; + } + } + } + delete hr; + + return false; +} + +shared_ptr BucketItem::use(shared_ptr itemInstance, Level *level, shared_ptr player) +{ + float a = 1; + + double x = player->xo + (player->x - player->xo) * a; + double y = player->yo + (player->y - player->yo) * a + 1.62 - player->heightOffset; + double z = player->zo + (player->z - player->zo) * a; + + bool pickLiquid = content == 0; + HitResult *hr = getPlayerPOVHitResult(level, player, pickLiquid); + if (hr == NULL) return itemInstance; + + if (hr->type == HitResult::TILE) + { + int xt = hr->x; + int yt = hr->y; + int zt = hr->z; + + if (!level->mayInteract(player, xt, yt, zt,content)) + { + app.DebugPrintf("!!!!!!!!!!! Can't place that here\n"); + shared_ptr servPlayer = dynamic_pointer_cast(player); + if( servPlayer != NULL ) + { + app.DebugPrintf("Sending ChatPacket::e_ChatCannotPlaceLava to player\n"); + servPlayer->connection->send( shared_ptr( new ChatPacket(L"", ChatPacket::e_ChatCannotPlaceLava ) ) ); + } + + delete hr; + return itemInstance; + } + + if (content == 0) + { + if (!player->mayBuild(xt, yt, zt)) return itemInstance; + if (level->getMaterial(xt, yt, zt) == Material::water && level->getData(xt, yt, zt) == 0) + { + level->setTile(xt, yt, zt, 0); + delete hr; + if (player->abilities.instabuild) + { + return itemInstance; + } + + if (--itemInstance->count <= 0) + { + return shared_ptr( new ItemInstance(Item::bucket_water) ); + } + else + { + if (!player->inventory->add(shared_ptr( new ItemInstance(Item::bucket_water)))) + { + player->drop(shared_ptr(new ItemInstance(Item::bucket_water_Id, 1, 0))); + } + return itemInstance; + } + } + if (level->getMaterial(xt, yt, zt) == Material::lava && level->getData(xt, yt, zt) == 0) + { + if( level->dimension->id == -1 ) + player->awardStat( + GenericStats::netherLavaCollected(), + GenericStats::param_noArgs() + ); + + level->setTile(xt, yt, zt, 0); + delete hr; + if (player->abilities.instabuild) + { + return itemInstance; + } + if (--itemInstance->count <= 0) + { + return shared_ptr( new ItemInstance(Item::bucket_lava) ); + } + else + { + if (!player->inventory->add(shared_ptr( new ItemInstance(Item::bucket_lava)))) + { + player->drop(shared_ptr(new ItemInstance(Item::bucket_lava_Id, 1, 0))); + } + return itemInstance; + } + } + } + else if (content < 0) + { + delete hr; + return shared_ptr( new ItemInstance(Item::bucket_empty) ); + } + else + { + if (hr->f == 0) yt--; + if (hr->f == 1) yt++; + if (hr->f == 2) zt--; + if (hr->f == 3) zt++; + if (hr->f == 4) xt--; + if (hr->f == 5) xt++; + + if (!player->mayBuild(xt, yt, zt)) return itemInstance; + + + if (emptyBucket(level, x, y, z, xt, yt, zt) && !player->abilities.instabuild) + { + return shared_ptr( new ItemInstance(Item::bucket_empty) ); + } + + } + } + else + { + if (content == 0) + { + if (hr->entity->GetType() == eTYPE_COW) + { + delete hr; + if (--itemInstance->count <= 0) + { + return shared_ptr( new ItemInstance(Item::milk) ); + } + else + { + if (!player->inventory->add(shared_ptr( new ItemInstance(Item::milk)))) + { + player->drop(shared_ptr(new ItemInstance(Item::milk_Id, 1, 0))); + } + return itemInstance; + } + } + } + } + delete hr; + return itemInstance; +} + +bool BucketItem::emptyBucket(Level *level, double x, double y, double z, int xt, int yt, int zt) +{ + if (content <= 0) return false; + + if (level->isEmptyTile(xt, yt, zt) || !level->getMaterial(xt, yt, zt)->isSolid()) + { + if (level->dimension->ultraWarm && content == Tile::water_Id) + { + level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_RANDOM_FIZZ, 0.5f, 2.6f + (level->random->nextFloat() - level->random->nextFloat()) * 0.8f); + + for (int i = 0; i < 8; i++) + { + level->addParticle(eParticleType_largesmoke, xt + Math::random(), yt + Math::random(), zt + Math::random(), 0, 0, 0); + } + } + else + { + level->setTileAndData(xt, yt, zt, content, 0); + } + + return true; + } + + return false; +} diff --git a/Minecraft.World/BucketItem.h b/Minecraft.World/BucketItem.h new file mode 100644 index 00000000..500d19d8 --- /dev/null +++ b/Minecraft.World/BucketItem.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Item.h" + +class Player; +class Level; + +class BucketItem : public Item +{ +private: + int content; + +public: + BucketItem(int id, int content); + + virtual bool TestUse(Level *level, shared_ptr player); + virtual shared_ptr use(shared_ptr itemInstance, Level *level, shared_ptr player); + + // TU9 + bool emptyBucket(Level *level, double x, double y, double z, int xt, int yt, int zt); + + /* + * public boolean useOn(ItemInstance instance, Player player, Level level, + * int x, int y, int z, int face) { if (content == 0) { } else { if (face == + * 0) y--; if (face == 1) y++; if (face == 2) z--; if (face == 3) z++; if + * (face == 4) x--; if (face == 5) x++; int targetType = level.getTile(x, y, + * z); if (targetType == 0) { level.setTile(x, y, z, content); } + * player->inventory.items[player->inventory.selected] = new + * ItemInstance(Item.bucket_empty); } return true; } + */ +}; \ No newline at end of file diff --git a/Minecraft.World/Buffer.cpp b/Minecraft.World/Buffer.cpp new file mode 100644 index 00000000..7fdf3b59 --- /dev/null +++ b/Minecraft.World/Buffer.cpp @@ -0,0 +1,73 @@ +#include "stdafx.h" +#include "Buffer.h" + +Buffer::Buffer( unsigned int capacity ) : m_capacity( capacity ), m_position( 0 ), m_limit( capacity ), hasBackingArray( false ) +{ +} + +//Clears this buffer. The position is set to zero, the limit is set to the capacity, and the mark is discarded. +//This method does not actually erase the data in the buffer, but it is named as if it did because it will most often +//be used in situations in which that might as well be the case. +// +//Returns: +//This buffer +Buffer *Buffer::clear() +{ + m_position = 0; + m_limit = m_capacity; + + return this; +} + +//Sets this buffer's limit. If the position is larger than the new limit then it is set to the new limit. +//If the mark is defined and larger than the new limit then it is discarded. +//Parameters: +//newLimit - The new limit value; must be non-negative and no larger than this buffer's capacity +//Returns: +//This buffer +Buffer *Buffer::limit( unsigned int newLimit ) +{ + assert( newLimit <= m_capacity ); + + m_limit = newLimit; + + if( m_position > newLimit ) + m_position = newLimit; + + return this; +} + +unsigned int Buffer::limit() +{ + return m_limit; +} + +//Sets this buffer's position. If the mark is defined and larger than the new position then it is discarded. +//Parameters: +//newPosition - The new position value; must be non-negative and no larger than the current limit +//Returns: +//This buffer +Buffer *Buffer::position( unsigned int newPosition ) +{ + assert( newPosition <= m_limit ); + + m_position = newPosition; + + return this; +} + +//Returns this buffer's position. +//Returns: +//The position of this buffer +unsigned int Buffer::position() +{ + return m_position; +} + +//Returns the number of elements between the current position and the limit. +//Returns: +//The number of elements remaining in this buffer +unsigned int Buffer::remaining() +{ + return m_limit - m_position; +} \ No newline at end of file diff --git a/Minecraft.World/Buffer.h b/Minecraft.World/Buffer.h new file mode 100644 index 00000000..1c66257b --- /dev/null +++ b/Minecraft.World/Buffer.h @@ -0,0 +1,34 @@ +#pragma once + +//A buffer is a linear, finite sequence of elements of a specific primitive type. Aside from its content, +//the essential properties of a buffer are its capacity, limit, and position: +// +//A buffer's capacity is the number of elements it contains. The capacity of a buffer is never negative and never changes. +// +//A buffer's limit is the index of the first element that should not be read or written. +//A buffer's limit is never negative and is never greater than its capacity. +// +//A buffer's position is the index of the next element to be read or written. +//A buffer's position is never negative and is never greater than its limit. +class Buffer +{ +protected: + const unsigned int m_capacity; + unsigned int m_position; + unsigned int m_limit; + unsigned int m_mark; + bool hasBackingArray; + +public: + Buffer( unsigned int capacity ); + virtual ~Buffer() {} + + Buffer *clear(); + Buffer *limit(unsigned int newLimit); + unsigned int limit(); + Buffer *position( unsigned int newPosition ); + unsigned int position(); + unsigned int remaining(); + + virtual Buffer *flip() = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/BufferedOutputStream.cpp b/Minecraft.World/BufferedOutputStream.cpp new file mode 100644 index 00000000..19fc95af --- /dev/null +++ b/Minecraft.World/BufferedOutputStream.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" + +#include "BufferedOutputStream.h" + +//Creates a new buffered output stream to write data to the specified underlying output stream with the specified buffer size. +//Parameters: +//out - the underlying output stream. +//size - the buffer size. +BufferedOutputStream::BufferedOutputStream(OutputStream *out, int size) +{ + stream = out; + buf = byteArray( size ); + count = 0; +} + +BufferedOutputStream::~BufferedOutputStream() +{ + delete buf.data; +} + +//Flushes this buffered output stream. This forces any buffered output bytes to be written out to the underlying output stream. +void BufferedOutputStream::flush() +{ + stream->write( buf, 0, count ); + + count = 0; +} + +//Closes this output stream and releases any system resources associated with the stream. +//The close method of FilterOutputStream calls its flush method, and then calls the close method of its underlying output stream. +void BufferedOutputStream::close() +{ + flush(); + stream->close(); +} + +//Writes len bytes from the specified byte array starting at offset off to this buffered output stream. +//Ordinarily this method stores bytes from the given array into this stream's buffer, flushing the buffer to the +//underlying output stream as needed. If the requested length is at least as large as this stream's buffer, however, +//then this method will flush the buffer and write the bytes directly to the underlying output stream. +//Thus redundant BufferedOutputStreams will not copy data unnecessarily. +// +//Overrides: +//write in class FilterOutputStream +//Parameters: +//b - the data. +//off - the start offset in the data. +//len - the number of bytes to write. +void BufferedOutputStream::write(byteArray b, unsigned int offset, unsigned int length) +{ + // Over the length of what we can store in our buffer - just flush the buffer and output directly + if( length >= buf.length ) + { + flush(); + stream->write(b, offset, length); + } + else + { + for(unsigned int i = 0; i < length; i++ ) + { + write( b[offset+i] ); + } + } +} + +//Writes b.length bytes to this output stream. +//The write method of FilterOutputStream calls its write method of three arguments with the arguments b, 0, and b.length. +// +//Note that this method does not call the one-argument write method of its underlying stream with the single argument b. +void BufferedOutputStream::write(byteArray b) +{ + write( b, 0, b.length ); +} + +//Writes the specified byte to this buffered output stream. +//Overrides: +//write in class FilterOutputStream +//Parameters: +//b - the byte to be written. +void BufferedOutputStream::write(unsigned int b) +{ + buf[count++] = (byte) b; + if( count == buf.length ) + { + flush(); + } +} \ No newline at end of file diff --git a/Minecraft.World/BufferedOutputStream.h b/Minecraft.World/BufferedOutputStream.h new file mode 100644 index 00000000..19c79d6c --- /dev/null +++ b/Minecraft.World/BufferedOutputStream.h @@ -0,0 +1,23 @@ +#pragma once + +#include "OutputStream.h" + +class BufferedOutputStream : public OutputStream +{ +private: + OutputStream *stream; + +protected : + byteArray buf; // The internal buffer where data is stored. + unsigned int count; // The number of valid bytes in the buffer. + +public: + BufferedOutputStream(OutputStream *out, int size); + ~BufferedOutputStream(); + + virtual void flush(); + virtual void close(); + virtual void write(byteArray b, unsigned int offset, unsigned int length); + virtual void write(byteArray b); + virtual void write(unsigned int b); +}; \ No newline at end of file diff --git a/Minecraft.World/BufferedReader.cpp b/Minecraft.World/BufferedReader.cpp new file mode 100644 index 00000000..80345ad0 --- /dev/null +++ b/Minecraft.World/BufferedReader.cpp @@ -0,0 +1,173 @@ +#include "stdafx.h" + +#include "BufferedReader.h" + +//Creates a buffering character-input stream that uses a default-sized input buffer. +//Parameters: +//in - A Reader +BufferedReader::BufferedReader( Reader *in ) : reader( in ), readMark( 0 ), bufferedMark( 0 ), eofReached( false ) +{ + bufferSize = 64; + buffer = new wchar_t[bufferSize]; + memset( buffer,0,sizeof(wchar_t)*bufferSize); + bufferMore(); +} + +BufferedReader::~BufferedReader() +{ + delete[] buffer; +} + +void BufferedReader::bufferMore() +{ + // Don't buffer more unless we are going to read at least twice as much as what is already left + if( bufferedMark - readMark > (BUFFER_MORE_AMOUNT / 2) ) + return; + + if( bufferSize < (bufferedMark + BUFFER_MORE_AMOUNT) ) + { + // Enlarge the buffer + wchar_t *temp = new wchar_t[bufferSize * 2]; + memset( temp,0,sizeof(wchar_t)*bufferSize*2); + std::copy( buffer, buffer+bufferSize, temp ); + + delete[] buffer; + buffer = temp; + bufferSize = bufferSize * 2; + } + + int value = 0; + unsigned int newCharsBuffered = 0; + while( newCharsBuffered < BUFFER_MORE_AMOUNT && (value = reader->read() ) != -1 ) + { + buffer[bufferedMark++] = value; + newCharsBuffered++; + } +} + +//Closes the stream and releases any system resources associated with it. Once the stream has been closed, +//further read(), ready(), mark(), reset(), or skip() invocations will throw an IOException. Closing a previously closed stream has no effect. +void BufferedReader::close() +{ + reader->close(); +} + +//Reads a single character. +//Returns: +//The character read, as an integer in the range 0 to 65535 (0x00-0xffff), or -1 if the end of the stream has been reached +int BufferedReader::read() +{ + // We should have buffered at least as much as we have read + assert( bufferedMark >= readMark ); + + if( bufferedMark == readMark ) + { + int value = reader->read(); + if( value == -1 ) + return -1; + + buffer[bufferedMark++] = value; + + bufferMore(); + } + + return buffer[readMark++]; +} + +//Reads characters into a portion of an array. +//This method implements the general contract of the corresponding read method of the Reader class. +//As an additional convenience, it attempts to read as many characters as possible by repeatedly invoking the read method +//of the underlying stream. This iterated read continues until one of the following conditions becomes true: +// +//The specified number of characters have been read, +//The read method of the underlying stream returns -1, indicating end-of-file, or +//The ready method of the underlying stream returns false, indicating that further input requests would block. +//If the first read on the underlying stream returns -1 to indicate end-of-file then this method returns -1. +//Otherwise this method returns the number of characters actually read. +//Subclasses of this class are encouraged, but not required, to attempt to read as many characters as possible in the same fashion. +// +//Ordinarily this method takes characters from this stream's character buffer, filling it from the underlying stream as necessary. +//If, however, the buffer is empty, the mark is not valid, and the requested length is at least as large as the buffer, +//then this method will read characters directly from the underlying stream into the given array. +//Thus redundant BufferedReaders will not copy data unnecessarily. +// +//Parameters: +//cbuf - Destination buffer +//off - Offset at which to start storing characters +//len - Maximum number of characters to read +//Returns: +//The number of characters read, or -1 if the end of the stream has been reached +int BufferedReader::read(wchar_t cbuf[], unsigned int off, unsigned int len) +{ + if( bufferSize < (bufferedMark + len) ) + { + // Enlarge the buffer + wchar_t *temp = new wchar_t[bufferSize * 2]; + memset( temp,0,sizeof(wchar_t)*bufferSize*2); + std::copy( buffer, buffer+bufferSize, temp ); + + delete[] buffer; + buffer = temp; + bufferSize = bufferSize * 2; + } + + unsigned int charsRead = 0; + while( charsRead < len && readMark <= bufferedMark ) + { + cbuf[off + charsRead] = buffer[ readMark++ ]; + charsRead++; + } + + int value = 0; + while( charsRead < len && (value = reader->read() ) != -1 ) + { + buffer[bufferedMark++] = value; + cbuf[off+charsRead] = value; + charsRead++; + readMark++; + } + + bufferMore(); + + return charsRead; +} + +//Reads a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), +//or a carriage return followed immediately by a linefeed. +//Returns: +//A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached +wstring BufferedReader::readLine() +{ + wstring output = L""; + bool newLineCharFound = false; + + while( readMark < bufferedMark ) + { + wchar_t value = buffer[readMark++]; + + if( !newLineCharFound ) + { + if( ( value == '\n') || ( value == '\r') ) + { + newLineCharFound = true; + } + else + { + output.push_back(value); + } + } + else + { + if( ( value != '\n') && ( value != '\r') ) + { + readMark--; // Move back the read mark on char so we get this char again next time + break; + } + } + + // This will only actually read more from the stream if we have less than half of the amount that + // will be added left to read + bufferMore(); + } + return output; +} \ No newline at end of file diff --git a/Minecraft.World/BufferedReader.h b/Minecraft.World/BufferedReader.h new file mode 100644 index 00000000..b2f7fc67 --- /dev/null +++ b/Minecraft.World/BufferedReader.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Reader.h" + +class BufferedReader : public Reader +{ +private: + Reader *reader; + wchar_t *buffer; + + unsigned int readMark; + unsigned int bufferedMark; + unsigned int bufferSize; + bool eofReached; + + static const unsigned int BUFFER_MORE_AMOUNT = 64; + void bufferMore(); + +public: + BufferedReader( Reader *in ); + virtual ~BufferedReader(); + + virtual void close(); + virtual int read(); + virtual int read(wchar_t cbuf[], unsigned int off, unsigned int len); + wstring readLine(); +}; \ No newline at end of file diff --git a/Minecraft.World/Bush.cpp b/Minecraft.World/Bush.cpp new file mode 100644 index 00000000..e6cd7c85 --- /dev/null +++ b/Minecraft.World/Bush.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "GrassTile.h" +#include "Bush.h" + +void Bush::_init() +{ + setTicking(true); + updateDefaultShape(); +} + +Bush::Bush(int id, Material *material) : Tile(id, material, isSolidRender()) +{ + _init(); +} + +Bush::Bush(int id) : Tile(id, Material::plant, isSolidRender()) +{ + _init(); +} + +// 4J Added override +void Bush::updateDefaultShape() +{ + float ss = 0.2f; + this->setShape(0.5f - ss, 0, 0.5f - ss, 0.5f + ss, ss * 3, 0.5f + ss); +} + +bool Bush::mayPlace(Level *level, int x, int y, int z) +{ + return Tile::mayPlace(level, x, y, z) && mayPlaceOn(level->getTile(x, y - 1, z)); +} + +bool Bush::mayPlaceOn(int tile) +{ + return tile == Tile::grass_Id || tile == Tile::dirt_Id || tile == Tile::farmland_Id; +} + +void Bush::neighborChanged(Level *level, int x, int y, int z, int type) +{ + Tile::neighborChanged(level, x, y, z, type); + checkAlive(level, x, y, z); +} + +void Bush::tick(Level *level, int x, int y, int z, Random *random) +{ + checkAlive(level, x, y, z); +} + +void Bush::checkAlive(Level *level, int x, int y, int z) +{ + if (!canSurvive(level, x, y, z)) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } +} + +bool Bush::canSurvive(Level *level, int x, int y, int z) +{ + return ( level->getDaytimeRawBrightness(x, y, z) >= 8 || (level->canSeeSky(x, y, z))) && mayPlaceOn(level->getTile(x, y - 1, z)); +} + +AABB *Bush::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +bool Bush::blocksLight() +{ + return false; +} + +bool Bush::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool Bush::isCubeShaped() +{ + return false; +} + +int Bush::getRenderShape() +{ + return Tile::SHAPE_CROSS_TEXTURE; +} diff --git a/Minecraft.World/Bush.h b/Minecraft.World/Bush.h new file mode 100644 index 00000000..464fba88 --- /dev/null +++ b/Minecraft.World/Bush.h @@ -0,0 +1,42 @@ +#pragma once +#include "Tile.h" +#include "Material.h" +#include "Definitions.h" + +class Random; +class Level; + +class Bush : public Tile +{ + friend class Tile; + +private: + void _init(); + +protected: + Bush(int id, Material *material); + Bush(int id); + +public: + virtual void updateDefaultShape(); + virtual bool mayPlace(Level *level, int x, int y, int z); + +protected: + virtual bool mayPlaceOn(int tile); + +public: + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual void tick(Level *level, int x, int y, int z, Random *random); + +protected: + void checkAlive(Level *level, int x, int y, int z); + +public: + virtual bool canSurvive(Level *level, int x, int y, int z); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual bool blocksLight(); + + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual int getRenderShape(); +}; diff --git a/Minecraft.World/ButtonTile.cpp b/Minecraft.World/ButtonTile.cpp new file mode 100644 index 00000000..16055a7e --- /dev/null +++ b/Minecraft.World/ButtonTile.cpp @@ -0,0 +1,366 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.h" +#include "ButtonTile.h" +#include "SoundTypes.h" + +ButtonTile::ButtonTile(int id, bool sensitive) : Tile(id, Material::decoration,isSolidRender()) +{ + this->setTicking(true); + this->sensitive = sensitive; +} + +Icon *ButtonTile::getTexture(int face, int data) +{ + if(id == Tile::button_wood_Id) return Tile::wood->getTexture(Facing::UP); + else return Tile::rock->getTexture(Facing::UP); +} + +AABB *ButtonTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +int ButtonTile::getTickDelay() +{ + return sensitive ? 30 : 20; +} + +bool ButtonTile::blocksLight() +{ + return false; +} + +bool ButtonTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool ButtonTile::isCubeShaped() +{ + return false; +} + +bool ButtonTile::mayPlace(Level *level, int x, int y, int z, int face) +{ + if (face == 2 && level->isSolidBlockingTile(x, y, z + 1)) return true; + if (face == 3 && level->isSolidBlockingTile(x, y, z - 1)) return true; + if (face == 4 && level->isSolidBlockingTile(x + 1, y, z)) return true; + if (face == 5 && level->isSolidBlockingTile(x - 1, y, z)) return true; + return false; +} + +bool ButtonTile::mayPlace(Level *level, int x, int y, int z) +{ + if (level->isSolidBlockingTile(x - 1, y, z)) + { + return true; + } + else if (level->isSolidBlockingTile(x + 1, y, z)) + { + return true; + } + else if (level->isSolidBlockingTile(x, y, z - 1)) + { + return true; + } + else if (level->isSolidBlockingTile(x, y, z + 1)) + { + return true; + } + return false; +} + +int ButtonTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) +{ + int dir = level->getData(x, y, z); + + int oldFlip = dir & 8; + dir &= 7; + + if (face == 2 && level->isSolidBlockingTile(x, y, z + 1)) dir = 4; + else if (face == 3 && level->isSolidBlockingTile(x, y, z - 1)) dir = 3; + else if (face == 4 && level->isSolidBlockingTile(x + 1, y, z)) dir = 2; + else if (face == 5 && level->isSolidBlockingTile(x - 1, y, z)) dir = 1; + else dir = findFace(level, x, y, z); + + return dir + oldFlip; +} + +int ButtonTile::findFace(Level *level, int x, int y, int z) +{ + if (level->isSolidBlockingTile(x - 1, y, z)) + { + return 1; + } + else if (level->isSolidBlockingTile(x + 1, y, z)) + { + return 2; + } + else if (level->isSolidBlockingTile(x, y, z - 1)) + { + return 3; + } + else if (level->isSolidBlockingTile(x, y, z + 1)) + { + return 4; + } + return 1; +} + +void ButtonTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (checkCanSurvive(level, x, y, z)) + { + int dir = level->getData(x, y, z) & 7; + bool replace = false; + + if (!level->isSolidBlockingTile(x - 1, y, z) && dir == 1) replace = true; + if (!level->isSolidBlockingTile(x + 1, y, z) && dir == 2) replace = true; + if (!level->isSolidBlockingTile(x, y, z - 1) && dir == 3) replace = true; + if (!level->isSolidBlockingTile(x, y, z + 1) && dir == 4) replace = true; + + if (replace) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } + } +} + +bool ButtonTile::checkCanSurvive(Level *level, int x, int y, int z) +{ + if (!mayPlace(level, x, y, z)) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + return false; + } + return true; +} + +void ButtonTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + int data = level->getData(x, y, z); + updateShape(data); +} + +void ButtonTile::updateShape(int data) +{ + int dir = data & 7; + bool pressed = (data & 8) > 0; + + float h0 = 6 / 16.0f; + float h1 = 10 / 16.0f; + float r = 3 / 16.0f; + float d = 2 / 16.0f; + if (pressed) d = 1 / 16.0f; + + if (dir == 1) + { + setShape(0, h0, 0.5f - r, d, h1, 0.5f + r); + } + else if (dir == 2) + { + setShape(1 - d, h0, 0.5f - r, 1, h1, 0.5f + r); + } + else if (dir == 3) + { + setShape(0.5f - r, h0, 0, 0.5f + r, h1, d); + } + else if (dir == 4) + { + setShape(0.5f - r, h0, 1 - d, 0.5f + r, h1, 1); + } +} + +void ButtonTile::attack(Level *level, int x, int y, int z, shared_ptr player) +{ + //use(level, x, y, z, player, 0, 0, 0, 0); +} + +// 4J-PB - Adding a TestUse for tooltip display +bool ButtonTile::TestUse() +{ + return true; +} + +bool ButtonTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if( soundOnly) + { + // 4J - added - just do enough to play the sound + level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_CLICK, 0.3f, 0.6f); + return false; + } + int data = level->getData(x, y, z); + int dir = data & 7; + int open = 8 - (data & 8); + if (open == 0) return true; + + level->setData(x, y, z, dir + open); + level->setTilesDirty(x, y, z, x, y, z); + + level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_CLICK, 0.3f, 0.6f); + + updateNeighbours(level, x, y, z, dir); + + level->addToTickNextTick(x, y, z, id, getTickDelay()); + + return true; +} + +void ButtonTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + if ((data & 8) > 0) + { + int dir = data & 7; + updateNeighbours(level, x, y, z, dir); + } + Tile::onRemove(level, x, y, z, id, data); +} + +bool ButtonTile::getSignal(LevelSource *level, int x, int y, int z, int dir) +{ + return (level->getData(x, y, z) & 8) > 0; +} + +bool ButtonTile::getDirectSignal(Level *level, int x, int y, int z, int dir) +{ + int data = level->getData(x, y, z); + if ((data & 8) == 0) return false; + int myDir = data & 7; + + if (myDir == 5 && dir == 1) return true; + if (myDir == 4 && dir == 2) return true; + if (myDir == 3 && dir == 3) return true; + if (myDir == 2 && dir == 4) return true; + if (myDir == 1 && dir == 5) return true; + + return false; +} + +bool ButtonTile::isSignalSource() +{ + return true; +} + +void ButtonTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (level->isClientSide) return; + int data = level->getData(x, y, z); + if ((data & 8) == 0) + { + return; + } + if(sensitive) + { + checkPressed(level, x, y, z); + } + else + { + level->setData(x, y, z, data & 7); + + int dir = data & 7; + updateNeighbours(level, x, y, z, dir); + + level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_CLICK, 0.3f, 0.5f); + level->setTilesDirty(x, y, z, x, y, z); + } +} + +void ButtonTile::updateDefaultShape() +{ + float x = 3 / 16.0f; + float y = 2 / 16.0f; + float z = 2 / 16.0f; + setShape(0.5f - x, 0.5f - y, 0.5f - z, 0.5f + x, 0.5f + y, 0.5f + z); +} + +void ButtonTile::entityInside(Level *level, int x, int y, int z, shared_ptr entity) +{ + if (level->isClientSide) return; + if (!sensitive) return; + + if ((level->getData(x, y, z) & 8) != 0) + { + return; + } + + checkPressed(level, x, y, z); +} + +void ButtonTile::checkPressed(Level *level, int x, int y, int z) +{ + int data = level->getData(x, y, z); + int dir = data & 7; + bool wasPressed = (data & 8) != 0; + bool shouldBePressed; + + updateShape(data); + Tile::ThreadStorage *tls = (Tile::ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + vector > *entities = level->getEntitiesOfClass(typeid(Arrow), AABB::newTemp(x + tls->xx0, y + tls->yy0, z + tls->zz0, x + tls->xx1, y + tls->yy1, z + tls->zz1)); + shouldBePressed = !entities->empty(); + delete entities; + + if (shouldBePressed && !wasPressed) + { + level->setData(x, y, z, dir | 8); + updateNeighbours(level, x, y, z, dir); + level->setTilesDirty(x, y, z, x, y, z); + + level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_CLICK, 0.3f, 0.6f); + } + if (!shouldBePressed && wasPressed) + { + level->setData(x, y, z, dir); + updateNeighbours(level, x, y, z, dir); + level->setTilesDirty(x, y, z, x, y, z); + + level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_CLICK, 0.3f, 0.5f); + } + + if (shouldBePressed) + { + level->addToTickNextTick(x, y, z, id, getTickDelay()); + } +} + +void ButtonTile::updateNeighbours(Level *level, int x, int y, int z, int dir) +{ + level->updateNeighborsAt(x, y, z, id); + + if (dir == 1) + { + level->updateNeighborsAt(x - 1, y, z, id); + } + else if (dir == 2) + { + level->updateNeighborsAt(x + 1, y, z, id); + } + else if (dir == 3) + { + level->updateNeighborsAt(x, y, z - 1, id); + } + else if (dir == 4) + { + level->updateNeighborsAt(x, y, z + 1, id); + } + else + { + level->updateNeighborsAt(x, y - 1, z, id); + } +} + +bool ButtonTile::shouldTileTick(Level *level, int x,int y,int z) +{ + int currentData = level->getData(x, y, z); + return (currentData & 8) != 0; +} + +void ButtonTile::registerIcons(IconRegister *iconRegister) +{ + // None +} diff --git a/Minecraft.World/ButtonTile.h b/Minecraft.World/ButtonTile.h new file mode 100644 index 00000000..e06158e0 --- /dev/null +++ b/Minecraft.World/ButtonTile.h @@ -0,0 +1,66 @@ +#pragma once +#include "Tile.h" +#include "Definitions.h" + +class Player; +class Random; +class Level; + +class ButtonTile : public Tile +{ + friend class Tile; + +private: + bool sensitive; + +protected: + ButtonTile(int id, bool sensitive); + +public: + Icon *getTexture(int face, int data); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual int getTickDelay(); + virtual bool blocksLight(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual bool mayPlace(Level *level, int x, int y, int z, int face); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + +private: + int findFace(Level *level, int x, int y, int z); + +public: + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + +private: + bool checkCanSurvive(Level *level, int x, int y, int z); + +public: + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + +private: + void updateShape(int data); + +public: + virtual void attack(Level *level, int x, int y, int z, shared_ptr player); + virtual bool TestUse(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); + virtual bool getSignal(LevelSource *level, int x, int y, int z, int dir); + virtual bool getDirectSignal(Level *level, int x, int y, int z, int dir); + virtual bool isSignalSource(); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual void updateDefaultShape(); + void entityInside(Level *level, int x, int y, int z, shared_ptr entity); + +private: + void checkPressed(Level *level, int x, int y, int z); + void updateNeighbours(Level *level, int x, int y, int z, int dir); + +public: + void registerIcons(IconRegister *iconRegister); + + // 4J Added so we can check before we try to add a tile to the tick list if it's actually going to do seomthing + virtual bool shouldTileTick(Level *level, int x,int y,int z); +}; \ No newline at end of file diff --git a/Minecraft.World/ByteArrayInputStream.cpp b/Minecraft.World/ByteArrayInputStream.cpp new file mode 100644 index 00000000..394bda14 --- /dev/null +++ b/Minecraft.World/ByteArrayInputStream.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" + +#include "InputOutputStream.h" + +//Creates ByteArrayInputStream that uses buf as its buffer array. The initial value of pos is offset and +//the initial value of count is the minimum of offset+length and buf.length. The buffer array is not copied. +//The buffer's mark is set to the specified offset. +//Parameters: +//buf - the input buffer. +//offset - the offset in the buffer of the first byte to read. +//length - the maximum number of bytes to read from the buffer. +ByteArrayInputStream::ByteArrayInputStream(byteArray buf, unsigned int offset, unsigned int length) + : pos( offset ), count( min( offset+length, buf.length ) ), mark( offset ) +{ + this->buf = buf; +} + +//Creates a ByteArrayInputStream so that it uses buf as its buffer array. The buffer array is not copied. +//The initial value of pos is 0 and the initial value of count is the length of buf. +//Parameters: +//buf - the input buffer. +ByteArrayInputStream::ByteArrayInputStream(byteArray buf) + : pos( 0 ), count( buf.length ), mark( 0 ) +{ + this->buf = buf; +} + +//Reads the next byte of data from this input stream. The value byte is returned as an int in the range 0 to 255. +//If no byte is available because the end of the stream has been reached, the value -1 is returned. +//This read method cannot block. +//Returns: +//the next byte of data, or -1 if the end of the stream has been reached. +int ByteArrayInputStream::read() +{ + if( pos >= count ) + return -1; + else + return buf[pos++]; +} + +//Reads some number of bytes from the input stream and stores them into the buffer array b. +//The number of bytes actually read is returned as an integer. This method blocks until input data is available, +//end of file is detected, or an exception is thrown. +//If the length of b is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. +//If no byte is available because the stream is at the end of the file, the value -1 is returned; otherwise, +//at least one byte is read and stored into b. +// +//The first byte read is stored into element b[0], the next one into b[1], and so on. The number of bytes read is, +//at most, equal to the length of b. Let k be the number of bytes actually read; these bytes will be stored in elements b[0] through b[k-1], +//leaving elements b[k] through b[b.length-1] unaffected. +// +//The read(b) method for class InputStream has the same effect as: +// +// read(b, 0, b.length) +//Parameters: +//b - the buffer into which the data is read. +//Returns: +//the total number of bytes read into the buffer, or -1 is there is no more data because the end of the stream has been reached. +int ByteArrayInputStream::read(byteArray b) +{ + return read( b, 0, b.length ); +} + +//Reads up to len bytes of data into an array of bytes from this input stream. If pos equals count, +//then -1 is returned to indicate end of file. Otherwise, the number k of bytes read is equal to the smaller of len and count-pos. +//If k is positive, then bytes buf[pos] through buf[pos+k-1] are copied into b[off] through b[off+k-1] in the manner +//performed by System.arraycopy. The value k is added into pos and k is returned. +//This read method cannot block. +//Parameters: +//b - the buffer into which the data is read. +//off - the start offset in the destination array b +//len - the maximum number of bytes read. +//Returns: +//the total number of bytes read into the buffer, or -1 if there is no more data because the end of the stream has been reached. +int ByteArrayInputStream::read(byteArray b, unsigned int offset, unsigned int length) +{ + if( pos == count ) + return -1; + + int k = min( length, count-pos ); + XMemCpy( &b[offset], &buf[pos], k ); + //std::copy( buf->data+pos, buf->data+pos+k, b->data + offset ); // Or this instead? + + pos += k; + + return k; +} + +//Closing a ByteArrayInputStream has no effect. +//The methods in this class can be called after the stream has been closed without generating an IOException. +void ByteArrayInputStream::close() +{ + return; +} + +//Skips n bytes of input from this input stream. Fewer bytes might be skipped if the end of the input stream is reached. The actual number k of bytes to be skipped is equal to the smaller of n and count-pos. The value k is added into pos and k is returned. +//Overrides: +//skip in class InputStream +//Parameters: +//n - the number of bytes to be skipped. +//Returns: +//the actual number of bytes skipped. +__int64 ByteArrayInputStream::skip(__int64 n) +{ + int newPos = pos + n; + + if(newPos > count) newPos = count; + + int k = newPos - pos; + pos = newPos; + + return k; +} + +ByteArrayInputStream::~ByteArrayInputStream() +{ + if(buf.data != NULL) delete [] buf.data; +} \ No newline at end of file diff --git a/Minecraft.World/ByteArrayInputStream.h b/Minecraft.World/ByteArrayInputStream.h new file mode 100644 index 00000000..e74b1cf7 --- /dev/null +++ b/Minecraft.World/ByteArrayInputStream.h @@ -0,0 +1,26 @@ +#pragma once +// 4J Stu - Represents Java standard library class + +#include "InputStream.h" + +class ByteArrayInputStream : public InputStream +{ +protected: + byteArray buf; //An array of bytes that was provided by the creator of the stream. + unsigned int count; //The index one greater than the last valid character in the input stream buffer. + unsigned int mark; //The currently marked position in the stream. + unsigned int pos; //The index of the next character to read from the input stream buffer. + +public: + ByteArrayInputStream(byteArray buf, unsigned int offset, unsigned int length); + ByteArrayInputStream(byteArray buf); + virtual ~ByteArrayInputStream(); + virtual int read(); + virtual int read(byteArray b); + virtual int read(byteArray b, unsigned int offset, unsigned int length); + virtual void close(); + virtual __int64 skip(__int64 n); + + // 4J Stu Added - Sometimes we don't want to delete the data on destroying this + void reset() { buf = byteArray(); count = 0; mark = 0; pos = 0; } +}; \ No newline at end of file diff --git a/Minecraft.World/ByteArrayOutputStream.cpp b/Minecraft.World/ByteArrayOutputStream.cpp new file mode 100644 index 00000000..9296fe5e --- /dev/null +++ b/Minecraft.World/ByteArrayOutputStream.cpp @@ -0,0 +1,80 @@ +#include "stdafx.h" + +#include "ByteArrayOutputStream.h" + +// Creates a new byte array output stream. The buffer capacity is initially 32 bytes, though its size increases if necessary. +ByteArrayOutputStream::ByteArrayOutputStream() +{ + count = 0; + buf = byteArray( 32 ); +} + +//Creates a new byte array output stream, with a buffer capacity of the specified size, in bytes. +//Parameters: +//size - the initial size. +ByteArrayOutputStream::ByteArrayOutputStream(unsigned int size) +{ + count = 0; + buf = byteArray( size ); +} + +ByteArrayOutputStream::~ByteArrayOutputStream() +{ + if (buf.data != NULL) + delete[] buf.data; +} + +//Writes the specified byte to this byte array output stream. +//Parameters: +//b - the byte to be written. +void ByteArrayOutputStream::write(unsigned int b) +{ + // If we will fill the buffer we need to make it bigger + if( count + 1 >= buf.length ) + buf.resize( buf.length * 2 ); + + buf[count] = (byte) b; + count++; +} + +// Writes b.length bytes from the specified byte array to this output stream. +//The general contract for write(b) is that it should have exactly the same effect as the call write(b, 0, b.length). +void ByteArrayOutputStream::write(byteArray b) +{ + write(b, 0, b.length); +} + +//Writes len bytes from the specified byte array starting at offset off to this byte array output stream. +//Parameters: +//b - the data. +//off - the start offset in the data. +//len - the number of bytes to write. +void ByteArrayOutputStream::write(byteArray b, unsigned int offset, unsigned int length) +{ + assert( b.length >= offset + length ); + + // If we will fill the buffer we need to make it bigger + if( count + length >= buf.length ) + buf.resize( max( count + length + 1, buf.length * 2 ) ); + + XMemCpy( &buf[count], &b[offset], length ); + //std::copy( b->data+offset, b->data+offset+length, buf->data + count ); // Or this instead? + + count += length; +} + +//Closing a ByteArrayOutputStream has no effect. +//The methods in this class can be called after the stream has been closed without generating an IOException. +void ByteArrayOutputStream::close() +{ +} + +//Creates a newly allocated byte array. Its size is the current size of this output stream and the valid contents of the buffer have been copied into it. +//Returns: +//the current contents of this output stream, as a byte array. +byteArray ByteArrayOutputStream::toByteArray() +{ + byteArray out(count); + memcpy(out.data,buf.data,count); + return out; +} \ No newline at end of file diff --git a/Minecraft.World/ByteArrayOutputStream.h b/Minecraft.World/ByteArrayOutputStream.h new file mode 100644 index 00000000..9e0b498d --- /dev/null +++ b/Minecraft.World/ByteArrayOutputStream.h @@ -0,0 +1,29 @@ +#pragma once + +#include "OutputStream.h" + +class ByteArrayOutputStream : public OutputStream +{ +// Note - when actually implementing, byteArray will need to grow as data is written +public: + byteArray buf; //The buffer where data is stored. + +protected: + unsigned int count; //The number of valid bytes in the buffer. + +public: + ByteArrayOutputStream(); + ByteArrayOutputStream(unsigned int size); + virtual ~ByteArrayOutputStream(); + + virtual void flush() {} + virtual void write(unsigned int b); + virtual void write(byteArray b); + virtual void write(byteArray b, unsigned int offset, unsigned int length); + virtual void close(); + virtual byteArray toByteArray(); + + void reset() { count = 0; } + unsigned int size() { return count; } + +}; \ No newline at end of file diff --git a/Minecraft.World/ByteArrayTag.h b/Minecraft.World/ByteArrayTag.h new file mode 100644 index 00000000..1b2437c0 --- /dev/null +++ b/Minecraft.World/ByteArrayTag.h @@ -0,0 +1,53 @@ +#pragma once +#include "Tag.h" +#include "System.h" + +class ByteArrayTag : public Tag +{ +public: + byteArray data; + + ByteArrayTag(const wstring &name) : Tag(name) { } + ByteArrayTag(const wstring &name, byteArray data) : Tag(name) {this->data = data; } // 4J - added ownData param + + void write(DataOutput *dos) + { + dos->writeInt(data.length); + dos->write(data); + } + + void load(DataInput *dis) + { + int length = dis->readInt(); + + if ( data.data ) delete[] data.data; + data = byteArray(length); + dis->readFully(data); + } + + byte getId() { return TAG_Byte_Array; } + + wstring toString() + { + static wchar_t buf[32]; + swprintf(buf, 32, L"[%d bytes]",data.length); + return wstring( buf ); + } + + bool equals(Tag *obj) + { + if (Tag::equals(obj)) + { + ByteArrayTag *o = (ByteArrayTag *) obj; + return ((data.data == NULL && o->data.data == NULL) || (data.data != NULL && data.length == o->data.length && memcmp(data.data, o->data.data, data.length) == 0) ); + } + return false; + } + + Tag *copy() + { + byteArray cp = byteArray(data.length); + System::arraycopy(data, 0, &cp, 0, data.length); + return new ByteArrayTag(getName(), cp); + } +}; \ No newline at end of file diff --git a/Minecraft.World/ByteBuffer.cpp b/Minecraft.World/ByteBuffer.cpp new file mode 100644 index 00000000..66514737 --- /dev/null +++ b/Minecraft.World/ByteBuffer.cpp @@ -0,0 +1,478 @@ +#include "stdafx.h" + +#include "IntBuffer.h" +#include "FloatBuffer.h" +#include "ByteBuffer.h" + +ByteBuffer::ByteBuffer( unsigned int capacity ) : Buffer( capacity ) +{ + hasBackingArray = false; + buffer = new byte[capacity]; + memset( buffer,0,sizeof(byte)*capacity); + byteOrder = BIGENDIAN; +} + +//Allocates a new direct byte buffer. +//The new buffer's position will be zero, its limit will be its capacity, and its mark will be undefined. Whether or not it has a backing array is unspecified. +// +//Parameters: +//capacity - The new buffer's capacity, in bytes +//Returns: +//The new byte buffer +ByteBuffer *ByteBuffer::allocateDirect(int capacity) +{ + return new ByteBuffer(capacity); +} + + +ByteBuffer::ByteBuffer( unsigned int capacity, byte *backingArray ) : Buffer( capacity ) +{ + hasBackingArray = true; + buffer = backingArray; +} + +ByteBuffer::~ByteBuffer() +{ + if( !hasBackingArray ) + delete[] buffer; +} + +//Wraps a byte array into a buffer. +//The new buffer will be backed by the given byte array; that is, modifications to the buffer will cause the array +//to be modified and vice versa. The new buffer's capacity and limit will be array.length, its position will be zero, +//and its mark will be undefined. Its backing array will be the given array, and its array offset will be zero. +// +//Parameters: +//array - The array that will back this buffer +//Returns: +//The new byte buffer +ByteBuffer *ByteBuffer::wrap(byteArray &b) +{ + return new ByteBuffer( b.length, b.data ); +} + +//Allocates a new byte buffer. +//The new buffer's position will be zero, its limit will be its capacity, and its mark will be undefined. +//It will have a backing array, and its array offset will be zero. +// +//Parameters: +//capacity - The new buffer's capacity, in bytes +//Returns: +//The new byte buffer +ByteBuffer *ByteBuffer::allocate(unsigned int capacity) +{ + return new ByteBuffer( capacity ); +} + +//Modifies this buffer's byte order. +//Parameters: +//bo - The new byte order, either BIGENDIAN or LITTLEENDIAN +void ByteBuffer::order(ByteOrder bo) +{ + byteOrder = bo; +} + +//Flips this buffer. The limit is set to the current position and then the position is set to zero. +//If the mark is defined then it is discarded. +// +//Returns: +//This buffer +ByteBuffer *ByteBuffer::flip() +{ + m_limit = m_position; + m_position = 0; + return this; +} + +// 4J Added so we can write this to a file +byte *ByteBuffer::getBuffer() +{ + return buffer; +} + +int ByteBuffer::getSize() +{ + // TODO 4J Stu - Should this be the capcity and not the limit? + return m_limit; +} +// End 4J + +//Absolute get method. Reads the byte at the given index. +//Parameters: +//index - The index from which the byte will be read +//Returns: +//The byte at the given index +//Throws: +//IndexOutOfBoundsException - If index is negative or not smaller than the buffer's limit +BYTE ByteBuffer::get(int index) +{ + assert( index < m_limit ); + assert( index >= 0 ); + + return buffer[index]; +} + +//Relative get method for reading an int value. +//Reads the next four bytes at this buffer's current position, composing them into an int value according to the +//current byte order, and then increments the position by four. +// +//Returns: +//The int value at the buffer's current position +int ByteBuffer::getInt() +{ + assert( m_position+3 < m_limit ); + + int value = 0; + + int b1 = buffer[ m_position ]; + int b2 = buffer[ m_position+1 ]; + int b3 = buffer[ m_position+2 ]; + int b4 = buffer[ m_position+3 ]; + + m_position += 4; + + if( byteOrder == BIGENDIAN ) + { + value = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; + } + else if( byteOrder == LITTLEENDIAN ) + { + value = b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); + } + return value; +} + +//Absolute get method for reading an int value. +//Reads four bytes at the given index, composing them into a int value according to the current byte order. +// +//Parameters: +//index - The index from which the bytes will be read +//Returns: +//The int value at the given index +int ByteBuffer::getInt(unsigned int index) +{ + assert( index+3 < m_limit ); + int value = 0; + + int b1 = buffer[ index ]; + int b2 = buffer[ index+1 ]; + int b3 = buffer[ index+2 ]; + int b4 = buffer[ index+3 ]; + + if( byteOrder == BIGENDIAN ) + { + value = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; + } + else if( byteOrder == LITTLEENDIAN ) + { + value = b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); + } + return value; +} + +//Relative get method for reading a long value. +//Reads the next eight bytes at this buffer's current position, composing them into a long value according to the current byte order, +//and then increments the position by eight. +// +//Returns: +//The long value at the buffer's current position +__int64 ByteBuffer::getLong() +{ + assert( m_position+8 < m_limit ); + + __int64 value = 0; + + __int64 b1 = buffer[ m_position ]; + __int64 b2 = buffer[ m_position+1 ]; + __int64 b3 = buffer[ m_position+2 ]; + __int64 b4 = buffer[ m_position+3 ]; + __int64 b5 = buffer[ m_position+4 ]; + __int64 b6 = buffer[ m_position+5 ]; + __int64 b7 = buffer[ m_position+6 ]; + __int64 b8 = buffer[ m_position+7 ]; + + m_position += 8; + + if( byteOrder == BIGENDIAN ) + { + value = (b1 << 56) | (b2 << 48) | (b3 << 40) | (b4 << 32) | (b5 << 24) | (b6 << 16) | (b7 << 8) | b8; + } + else if( byteOrder == LITTLEENDIAN ) + { + value = b1 | (b2 << 8) | (b3 << 16) | (b4 << 24) | (b5 << 32) | (b6 << 40) | (b7 << 48) | (b8 << 56); + } + return value; +} + +//Relative get method for reading a short value. +//Reads the next two bytes at this buffer's current position, composing them into a short value according to the current +//byte order, and then increments the position by two. +// +//Returns: +//The short value at the buffer's current position +short ByteBuffer::getShort() +{ + assert( m_position+1 < m_limit ); + + short value = 0; + + short b1 = buffer[ m_position ]; + short b2 = buffer[ m_position+1 ]; + + m_position += 2; + + if( byteOrder == BIGENDIAN ) + { + value = (b1 << 8) | b2; + } + else if( byteOrder == LITTLEENDIAN ) + { + value = b1 | (b2 << 8); + } + return value; +} + +void ByteBuffer::getShortArray(shortArray &s) +{ + // TODO 4J Stu - Should this function be writing from the start of the buffer, or from position? + // And should it update position? + assert( s.length >= m_limit/2 ); + + // 4J Stu - Assumes big endian + memcpy( s.data, buffer, (m_limit-m_position) ); +} + +//Absolute put method (optional operation). +//Writes the given byte into this buffer at the given index. +// +//Parameters: +//index - The index at which the byte will be written +//b - The byte value to be written +//Returns: +//This buffer +//Throws: +//IndexOutOfBoundsException - If index is negative or not smaller than the buffer's limit +//ReadOnlyBufferException - If this buffer is read-only +ByteBuffer *ByteBuffer::put(int index, byte b) +{ + assert( index < m_limit ); + assert( index >= 0 ); + + buffer[index] = b; + return this; +} + + +//Relative put method for writing an int value (optional operation). +//Writes four bytes containing the given int value, in the current byte order, into this buffer at the current position, +//and then increments the position by four. +// +//Parameters: +//value - The int value to be written +//Returns: +//This buffer +ByteBuffer *ByteBuffer::putInt(int value) +{ + assert( m_position+3 < m_limit ); + + if( byteOrder == BIGENDIAN ) + { + buffer[m_position] = (value >> 24) & 0xFF; + buffer[m_position+1] = (value >> 16) & 0xFF; + buffer[m_position+2] = (value >> 8) & 0xFF; + buffer[m_position+3] = value & 0xFF; + } + else if( byteOrder == LITTLEENDIAN ) + { + buffer[m_position] = value & 0xFF; + buffer[m_position+1] = (value >> 8) & 0xFF; + buffer[m_position+2] = (value >> 16) & 0xFF; + buffer[m_position+3] = (value >> 24) & 0xFF; + } + + m_position += 4; + + return this; +} + +//Absolute put method for writing an int value (optional operation). +//Writes four bytes containing the given int value, in the current byte order, into this buffer at the given index. +// +//Parameters: +//index - The index at which the bytes will be written +//value - The int value to be written +//Returns: +//This buffer +ByteBuffer *ByteBuffer::putInt(unsigned int index, int value) +{ + assert( index+3 < m_limit ); + + if( byteOrder == BIGENDIAN ) + { + buffer[index] = (value >> 24) & 0xFF; + buffer[index+1] = (value >> 16) & 0xFF; + buffer[index+2] = (value >> 8) & 0xFF; + buffer[index+3] = value & 0xFF; + } + else if( byteOrder == LITTLEENDIAN ) + { + buffer[index] = value & 0xFF; + buffer[index+1] = (value >> 8) & 0xFF; + buffer[index+2] = (value >> 16) & 0xFF; + buffer[index+3] = (value >> 24) & 0xFF; + } + + return this; +} + +//Relative put method for writing a short value (optional operation). +//Writes two bytes containing the given short value, in the current byte order, into this buffer at the current position, +//and then increments the position by two. +// +//Parameters: +//value - The short value to be written +//Returns: +//This buffer +ByteBuffer *ByteBuffer::putShort(short value) +{ + assert( m_position+1 < m_limit ); + + if( byteOrder == BIGENDIAN ) + { + buffer[m_position] = (value >> 8) & 0xFF; + buffer[m_position+1] = value & 0xFF; + } + else if( byteOrder == LITTLEENDIAN ) + { + buffer[m_position] = value & 0xFF; + buffer[m_position+1] = (value >> 8) & 0xFF; + } + + m_position += 2; + + return this; +} + +ByteBuffer *ByteBuffer::putShortArray(shortArray &s) +{ + // TODO 4J Stu - Should this function be writing from the start of the buffer, or from position? + // And should it update position? + assert( s.length*2 <= m_limit); + + // 4J Stu - Assumes big endian + memcpy( buffer, s.data, s.length*2 ); + + return this; +} + +//Relative put method for writing a long value (optional operation). +//Writes eight bytes containing the given long value, in the current byte order, into this buffer at the current position, +//and then increments the position by eight. +// +//Parameters: +//value - The long value to be written +//Returns: +//This buffer +ByteBuffer *ByteBuffer::putLong(__int64 value) +{ + assert( m_position+7 < m_limit ); + + if( byteOrder == BIGENDIAN ) + { + buffer[m_position] = (value >> 56) & 0xFF; + buffer[m_position+1] = (value >> 48) & 0xFF; + buffer[m_position+2] = (value >> 40) & 0xFF; + buffer[m_position+3] = (value >> 32) & 0xFF; + buffer[m_position+4] = (value >> 24) & 0xFF; + buffer[m_position+5] = (value >> 16) & 0xFF; + buffer[m_position+6] = (value >> 8) & 0xFF; + buffer[m_position+7] = value & 0xFF; + } + else if( byteOrder == LITTLEENDIAN ) + { + buffer[m_position] = value & 0xFF; + buffer[m_position+1] = (value >> 8) & 0xFF; + buffer[m_position+2] = (value >> 16) & 0xFF; + buffer[m_position+3] = (value >> 24) & 0xFF; + buffer[m_position+4] = (value >> 32) & 0xFF; + buffer[m_position+5] = (value >> 40) & 0xFF; + buffer[m_position+6] = (value >> 48) & 0xFF; + buffer[m_position+7] = (value >> 56) & 0xFF; + } + + return this; +} + +//Relative bulk put method (optional operation). +//This method transfers the entire content of the given source byte array into this buffer. +//An invocation of this method of the form dst.put(a) behaves in exactly the same way as the invocation +// +// dst.put(a, 0, a.length) +//Returns: +//This buffer +ByteBuffer *ByteBuffer::put(byteArray inputArray) +{ + if( inputArray.length > remaining() ) + assert( false ); //TODO 4J Stu - Some kind of exception? + + std::copy( inputArray.data, inputArray.data + inputArray.length, buffer+m_position ); + + m_position += inputArray.length; + + return this; +} + +byteArray ByteBuffer::array() +{ + return byteArray( buffer, m_capacity ); +} + +//Creates a view of this byte buffer as an int buffer. +//The content of the new buffer will start at this buffer's current position. Changes to this buffer's content +//will be visible in the new buffer, and vice versa; the two buffers' position, limit, and mark values will be independent. +// +//The new buffer's position will be zero, its capacity and its limit will be the number of bytes remaining in this buffer +//divided by four, and its mark will be undefined. The new buffer will be direct if, and only if, this buffer is direct, and +//it will be read-only if, and only if, this buffer is read-only. +// +//Returns: +//A new int buffer +IntBuffer *ByteBuffer::asIntBuffer() +{ + // TODO 4J Stu - Is it safe to just cast our byte array pointer to another type? + return new IntBuffer( (m_limit-m_position)/4, (int *) (buffer+m_position) ); +} + +//Creates a view of this byte buffer as a float buffer. +//The content of the new buffer will start at this buffer's current position. Changes to this buffer's content will be +//visible in the new buffer, and vice versa; the two buffers' position, limit, and mark values will be independent. +// +//The new buffer's position will be zero, its capacity and its limit will be the number of bytes remaining in this buffer +//divided by four, and its mark will be undefined. The new buffer will be direct if, and only if, this buffer is direct, +//and it will be read-only if, and only if, this buffer is read-only. +// +//Returns: +//A new float buffer +FloatBuffer *ByteBuffer::asFloatBuffer() +{ + // TODO 4J Stu - Is it safe to just cast our byte array pointer to another type? + return new FloatBuffer( (m_limit-m_position)/4, (float *) (buffer+m_position) ); +} + + + +#ifdef __PS3__ +// we're using the RSX now to upload textures to vram, so we need th main ram textures allocated from io space +ByteBuffer_IO::ByteBuffer_IO( unsigned int capacity ) + : ByteBuffer(capacity, (byte*)RenderManager.allocIOMem(capacity, 64)) +{ + memset( buffer,0,sizeof(byte)*capacity); + byteOrder = BIGENDIAN; +} + +ByteBuffer_IO::~ByteBuffer_IO() +{ +// delete buffer; + RenderManager.freeIOMem(buffer); +} +#endif // __PS3__ \ No newline at end of file diff --git a/Minecraft.World/ByteBuffer.h b/Minecraft.World/ByteBuffer.h new file mode 100644 index 00000000..2d936374 --- /dev/null +++ b/Minecraft.World/ByteBuffer.h @@ -0,0 +1,57 @@ +#pragma once + +#include "Buffer.h" +#include "Definitions.h" + +class IntBuffer; +class FloatBuffer; + +class ByteBuffer : public Buffer +{ +protected: + byte *buffer; + ByteOrder byteOrder; + +public: + ByteBuffer(unsigned int capacity); + static ByteBuffer *allocateDirect(int capacity); + ByteBuffer( unsigned int capacity, byte *backingArray ); + virtual ~ByteBuffer(); + + static ByteBuffer *wrap(byteArray &b); + static ByteBuffer *allocate(unsigned int capacity); + void order(ByteOrder a); + ByteBuffer *flip(); + byte *getBuffer(); + int getSize(); + int getInt(); + int getInt(unsigned int index); + void get(byteArray) {} // 4J - TODO + byte get(int index); + __int64 getLong(); + short getShort(); + void getShortArray(shortArray &s); + ByteBuffer *put(int index, byte b); + ByteBuffer *putInt(int value); + ByteBuffer *putInt(unsigned int index, int value); + ByteBuffer *putShort(short value); + ByteBuffer *putShortArray(shortArray &s); + ByteBuffer *putLong(__int64 value); + ByteBuffer *put(byteArray inputArray); + byteArray array(); + IntBuffer *asIntBuffer(); + FloatBuffer *asFloatBuffer(); + +}; + + +#ifdef __PS3__ +// we're using the RSX now to upload textures to vram, so we need th main ram textures allocated from io space +class ByteBuffer_IO : public ByteBuffer +{ +public: + ByteBuffer_IO(unsigned int capacity); + ~ByteBuffer_IO(); +}; + +#endif // __PS3__ \ No newline at end of file diff --git a/Minecraft.World/ByteTag.h b/Minecraft.World/ByteTag.h new file mode 100644 index 00000000..0d2b3965 --- /dev/null +++ b/Minecraft.World/ByteTag.h @@ -0,0 +1,36 @@ +#pragma once +#include "Tag.h" + +class ByteTag : public Tag +{ +public: + byte data; + ByteTag(const wstring &name) : Tag(name) {} + ByteTag(const wstring &name, byte data) : Tag(name) {this->data = data; } + + void write(DataOutput *dos) { dos->writeByte(data); } + void load(DataInput *dis) { data = dis->readByte(); } + + byte getId() { return TAG_Byte; } + wstring toString() + { + static wchar_t buf[32]; + swprintf(buf,32,L"%d",data); + return wstring( buf ); + } + + bool equals(Tag *obj) + { + if (Tag::equals(obj)) + { + ByteTag *o = (ByteTag *) obj; + return data == o->data; + } + return false; + } + + Tag *copy() + { + return new ByteTag(getName(), data); + } +}; \ No newline at end of file diff --git a/Minecraft.World/C4JThread.cpp b/Minecraft.World/C4JThread.cpp new file mode 100644 index 00000000..4b9760c1 --- /dev/null +++ b/Minecraft.World/C4JThread.cpp @@ -0,0 +1,1100 @@ +#include "stdafx.h" + + +#include "C4JThread.h" +#ifdef __PSVITA__ +#include "..\Minecraft.Client\PSVita\PSVitaExtras\ShutdownManager.h" +#include "..\Minecraft.Client\PSVita\PSVitaExtras\PSVitaTLSStorage.h" + +// AP - this comes from the low level user_malloc.c file used to overide the default memory functions. These must be called when a thread is started/stopped +extern "C" { +extern void user_registerthread(); +extern void user_removethread(); +} +#else +#include "..\Minecraft.Client\PS3\PS3Extras\ShutdownManager.h" + +#endif + +std::vector C4JThread::ms_threadList; +CRITICAL_SECTION C4JThread::ms_threadListCS; + +#ifdef _XBOX_ONE + // 4J Stu - On XboxOne the main thread is not the one that does all the static init, so we have to set this up later +C4JThread *C4JThread::m_mainThread = NULL; + +void C4JThread::StaticInit() +{ + m_mainThread = new C4JThread("Main thread"); +} +#else +C4JThread C4JThread::m_mainThread("Main thread"); +#endif + +#ifdef __ORBIS__ +__thread SceKernelCpumask C4JThread::m_oldAffinityMask; +#endif + + +#if __PSVITA__ +static SceInt32 g_DefaultCPU; +static SceInt32 g_DefaultPriority; +#endif + +C4JThread::C4JThread( C4JThreadStartFunc* startFunc, void* param, const char* threadName, int stackSize/* = 0*/ ) +{ + m_startFunc = startFunc; + m_threadParam = param; + m_stackSize = stackSize; + + // to match XBox, if the stack size is zero, use the default 64k + if(m_stackSize == 0) + m_stackSize = 65536 * 2; + // make sure it's at least 16K + if(m_stackSize < 16384) + m_stackSize = 16384; + +#ifdef __PS3__ + sprintf(m_threadName, "(4J) %s", threadName ); +#else + sprintf_s(m_threadName,64, "(4J) %s", threadName ); +#endif + + m_isRunning = false; + m_hasStarted = false; + + m_exitCode = STILL_ACTIVE; + +#ifdef __PS3__ + m_completionFlag = new Event(Event::e_modeManualClear); + m_threadID = 0; + m_lastSleepTime = 0; + m_priority = 1002; // main thread has priority 1001 +#elif defined __ORBIS__ + m_completionFlag = new Event(Event::e_modeManualClear); + m_threadID = 0; + m_lastSleepTime = 0; + scePthreadAttrInit(&m_threadAttr); + int err = scePthreadAttrSetaffinity(&m_threadAttr, 63); // set the thread affinity to all cores to start with + assert(err == SCE_OK); + m_oldAffinityMask = 0; + m_priority = SCE_KERNEL_PRIO_FIFO_DEFAULT; +#elif defined __PSVITA__ + m_completionFlag = new Event(Event::e_modeManualClear); + m_threadID = 0; + m_lastSleepTime = 0; + m_priority = g_DefaultPriority; + //m_CPUMask = SCE_KERNEL_CPU_MASK_USER_ALL; + + // AP - I had trouble getting the cpu to change once the thread was created so I've hard coded them here + // The main work division is... + // 0 - Main + // 1 - Chunk/Tile Update + // 2 - Server/Audio + // These three can sometimes consume ALL the CPU time so they are set to below average priority so as not to block other critical threads + int CPU = SCE_KERNEL_CPU_MASK_USER_ALL; + if( !strcmp(threadName, "Chunk update") ) + { + CPU = SCE_KERNEL_CPU_MASK_USER_2; + m_priority = g_DefaultPriority + 1; + } + if( !strcmp(threadName, "Server" ) ) + { + CPU = SCE_KERNEL_CPU_MASK_USER_1; + m_priority = g_DefaultPriority + 1; + } + // make sure Tile Update doesn't go on cpu 0 because it will hold up the main thread. And it can't go on cpu 1 because Chunk Update crashes. + if( !strcmp(threadName, "Tile update") ) + { + CPU = SCE_KERNEL_CPU_MASK_USER_1; + } + + m_threadID = sceKernelCreateThread(m_threadName, entryPoint, g_DefaultPriority, m_stackSize, 0, CPU, NULL); + app.DebugPrintf("***************************** start thread %s **************************\n", m_threadName); +#else + m_threadID = 0; + m_threadHandle = 0; + m_threadHandle = CreateThread(NULL, m_stackSize, entryPoint, this, CREATE_SUSPENDED, &m_threadID); +#endif + EnterCriticalSection(&ms_threadListCS); + ms_threadList.push_back(this); + LeaveCriticalSection(&ms_threadListCS); +} + +// only used for the main thread +C4JThread::C4JThread( const char* mainThreadName) +{ +#ifdef __PSVITA__ + user_registerthread(); +#endif + + m_startFunc = NULL; + m_threadParam = NULL; + m_stackSize = 0; + +#ifdef __PS3__ + sprintf(m_threadName, "(4J) %s", mainThreadName); +#else + sprintf_s(m_threadName, 64, "(4J) %s", mainThreadName); +#endif + m_isRunning = true; + m_hasStarted = true; + m_lastSleepTime = System::currentTimeMillis(); + + // should be the first thread to be created, so init the static critical section for the threadlist here + InitializeCriticalSection(&ms_threadListCS); + + +#ifdef __PS3__ + m_completionFlag = new Event(Event::e_modeManualClear); + sys_ppu_thread_get_id(&m_threadID); +#elif defined __ORBIS__ + m_completionFlag = new Event(Event::e_modeManualClear); + m_threadID = scePthreadSelf(); + m_priority = SCE_KERNEL_PRIO_FIFO_DEFAULT; +#elif defined __PSVITA__ + m_completionFlag = new Event(Event::e_modeManualClear); + g_DefaultPriority = sceKernelGetThreadCurrentPriority(); + m_threadID = sceKernelGetThreadId(); + int err = sceKernelChangeThreadCpuAffinityMask(m_threadID, SCE_KERNEL_CPU_MASK_USER_0); +// sceKernelChangeThreadPriority(m_threadID, g_DefaultPriority + 1); + g_DefaultCPU = SCE_KERNEL_CPU_MASK_USER_ALL;//sceKernelGetThreadCpuAffinityMask(m_threadID); +#else + m_threadID = GetCurrentThreadId(); + m_threadHandle = GetCurrentThread(); +#endif +#ifdef _XBOX_ONE + SetThreadName(-1, m_threadName); +#endif + EnterCriticalSection(&ms_threadListCS); + ms_threadList.push_back(this); + LeaveCriticalSection(&ms_threadListCS); +} + +C4JThread::~C4JThread() +{ +#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ + delete m_completionFlag; +#endif + +#if defined __ORBIS__ + scePthreadJoin(m_threadID, NULL); +#endif + + EnterCriticalSection(&ms_threadListCS); + + for( AUTO_VAR(it,ms_threadList.begin()); it != ms_threadList.end(); it++ ) + { + if( (*it) == this ) + { + ms_threadList.erase(it); + LeaveCriticalSection(&ms_threadListCS); + return; + } + } + + LeaveCriticalSection(&ms_threadListCS); +} + +#ifdef __PS3__ +void C4JThread::entryPoint(uint64_t param) +{ + C4JThread* pThread = (C4JThread*)param; + pThread->m_exitCode = (*pThread->m_startFunc)(pThread->m_threadParam); + pThread->m_completionFlag->Set(); + pThread->m_isRunning = false; + sys_ppu_thread_exit(0); +} +#elif defined __ORBIS__ +void * C4JThread::entryPoint(void *param) +{ + C4JThread* pThread = (C4JThread*)param; + pThread->m_exitCode = (*pThread->m_startFunc)(pThread->m_threadParam); + pThread->m_completionFlag->Set(); + pThread->m_isRunning = false; + scePthreadExit(NULL); +} +#elif defined __PSVITA__ +struct StrArg { + C4JThread* Thread; +}; + +SceInt32 C4JThread::entryPoint(SceSize argSize, void *pArgBlock) +{ + StrArg *strArg = (StrArg*)pArgBlock; + C4JThread* pThread = strArg->Thread; + user_registerthread(); + pThread->m_exitCode = (*pThread->m_startFunc)(pThread->m_threadParam); + app.DebugPrintf("***************************** thread exit %s **************************\n", pThread->m_threadName); + pThread->m_completionFlag->Set(); + pThread->m_isRunning = false; + + // AP - make sure we clean up this thread's storage and memory + PSVitaTLSStorage::RemoveThread(pThread->m_threadID); + user_removethread(); + + sceKernelExitDeleteThread(NULL); + + return pThread->m_exitCode; +} +#else +DWORD WINAPI C4JThread::entryPoint(LPVOID lpParam) +{ + C4JThread* pThread = (C4JThread*)lpParam; + SetThreadName(-1, pThread->m_threadName); + pThread->m_exitCode = (*pThread->m_startFunc)(pThread->m_threadParam); + pThread->m_isRunning = false; + return pThread->m_exitCode; +} +#endif + + + + +void C4JThread::Run() +{ +#ifdef __PS3__ + // prio specifies the priority value of the PPU thread within the range from 0 to 3071 where 0 is the highest. + // One of the following values is set to flags: + // 0 - non-joinable non-interrupt thread + // SYS_PPU_THREAD_CREATE_JOINABLE - Create a joinable thread + // SYS_PPU_THREAD_CREATE_INTERRUPT - Create an interrupt thread + uint64_t flags = 0; + int err = sys_ppu_thread_create(&m_threadID, entryPoint, (uint64_t)this, m_priority, m_stackSize, flags, m_threadName); +#elif defined __ORBIS__ + scePthreadAttrSetstacksize(&m_threadAttr, m_stackSize); + scePthreadAttrSetguardsize(&m_threadAttr, 1024); + int ret = scePthreadCreate(&m_threadID, &m_threadAttr, entryPoint, this, m_threadName); + assert( ret == SCE_OK ); + scePthreadSetprio(m_threadID,m_priority); + scePthreadAttrDestroy(&m_threadAttr); +#elif defined __PSVITA__ + StrArg strArg = {this}; +// m_threadID = sceKernelCreateThread(m_threadName, entryPoint, m_priority, m_stackSize, 0, m_CPUMask, NULL); + sceKernelStartThread( m_threadID, sizeof(strArg), &strArg); +#else + ResumeThread(m_threadHandle); +#endif + m_lastSleepTime = System::currentTimeMillis(); + m_isRunning = true; + m_hasStarted = true; +} + +void C4JThread::SetProcessor( int proc ) +{ +#ifdef __PS3__ + // does nothing since we only have the 1 processor +#elif defined __ORBIS__ + scePthreadAttrSetaffinity(&m_threadAttr, 1 << proc); +#elif defined __PSVITA__ + int Proc = proc >> 1; // convert from 360's 3 cores * 2 hardware threads to Vita's 3 cores + int Mask = SCE_KERNEL_CPU_MASK_USER_0 << Proc; + //m_CPUMask = Mask; +// int err = sceKernelChangeThreadCpuAffinityMask(m_threadID, Mask); + int Newmask = sceKernelGetThreadCpuAffinityMask(m_threadID); + app.DebugPrintf("***************************** set thread proc %s %d %d %d **************************\n", m_threadName, proc, Mask, Newmask); +#elif defined _DURANGO + SetThreadAffinityMask(m_threadHandle, 1 << proc ); +#else + XSetThreadProcessor( m_threadHandle, proc); +#endif +} + +void C4JThread::SetPriority( int priority ) +{ +#ifdef __PS3__ + switch(priority) + { + case THREAD_PRIORITY_LOWEST: m_priority = 1003; break; + case THREAD_PRIORITY_BELOW_NORMAL: m_priority = 1002; break; + case THREAD_PRIORITY_NORMAL: m_priority = 1001; break; // same as main thread + case THREAD_PRIORITY_ABOVE_NORMAL: m_priority = 1000; break; + case THREAD_PRIORITY_HIGHEST: m_priority = 999; break; + } + if(m_threadID != 0) + sys_ppu_thread_set_priority(m_threadID, m_priority); + //int erro = sys_ppu_thread_set_priority(m_threadID, priority); +#elif defined __ORBIS__ + + switch(priority) + { + case THREAD_PRIORITY_LOWEST: m_priority = SCE_KERNEL_PRIO_FIFO_LOWEST; break; + case THREAD_PRIORITY_BELOW_NORMAL: m_priority = SCE_KERNEL_PRIO_FIFO_LOWEST + ((SCE_KERNEL_PRIO_FIFO_DEFAULT-SCE_KERNEL_PRIO_FIFO_LOWEST)/2); break; + case THREAD_PRIORITY_NORMAL: m_priority = SCE_KERNEL_PRIO_FIFO_DEFAULT; break; // same as main thread + case THREAD_PRIORITY_ABOVE_NORMAL: m_priority = SCE_KERNEL_PRIO_FIFO_DEFAULT + ((SCE_KERNEL_PRIO_FIFO_HIGHEST-SCE_KERNEL_PRIO_FIFO_DEFAULT)/2); break; + case THREAD_PRIORITY_HIGHEST: m_priority = SCE_KERNEL_PRIO_FIFO_HIGHEST; break; + } + + if( m_threadID != 0 ) + { + scePthreadSetprio(m_threadID,m_priority); + } +#elif defined __PSVITA__ + int Mid = g_DefaultPriority;//(SCE_KERNEL_LOWEST_PRIORITY_USER + SCE_KERNEL_HIGHEST_PRIORITY_USER) / 2; + switch(priority) + { + case THREAD_PRIORITY_LOWEST: + m_priority = SCE_KERNEL_LOWEST_PRIORITY_USER; + break; + case THREAD_PRIORITY_BELOW_NORMAL: + m_priority = Mid + 1; + break; + case THREAD_PRIORITY_NORMAL: + m_priority = Mid; + break; // same as main thread + case THREAD_PRIORITY_ABOVE_NORMAL: + m_priority = Mid - 1; + break; + case THREAD_PRIORITY_HIGHEST: + m_priority = SCE_KERNEL_HIGHEST_PRIORITY_USER; + break; + } + +// sceKernelChangeThreadPriority(m_threadID, m_priority); + app.DebugPrintf("***************************** set thread prio %s %d %d **************************\n", m_threadName, priority, m_priority); +#else + SetThreadPriority(m_threadHandle, priority); +#endif // __PS3__ +} + +DWORD C4JThread::WaitForCompletion( int timeoutMs ) +{ +#ifdef __PS3__ + if(timeoutMs == INFINITE) + timeoutMs = SYS_NO_TIMEOUT ; + return m_completionFlag->WaitForSignal(timeoutMs); +#elif defined __ORBIS__ + return m_completionFlag->WaitForSignal( timeoutMs ); +#elif defined __PSVITA__ + return m_completionFlag->WaitForSignal( timeoutMs ); +/* SceUInt32 Timeout = timeoutMs * 1000; + SceInt32 err = sceKernelWaitThreadEnd(m_threadID, &m_exitCode, &Timeout); + if( err == 0 ) + { + return m_exitCode; + } + else + { + if( err == SCE_KERNEL_ERROR_WAIT_TIMEOUT ) + { + return WAIT_TIMEOUT; + } + else + { + // AP - not sure what to do here + return 0; + } + }*/ + +// return m_exitCode; +#else + return WaitForSingleObject(m_threadHandle, timeoutMs); +#endif // __PS3__ +} + +int C4JThread::GetExitCode() +{ +#if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ + return m_exitCode; +#else + DWORD exitcode = 0; + GetExitCodeThread(m_threadHandle, &exitcode); + + return *((int *)&exitcode); +#endif +} + +void C4JThread::Sleep( int millisecs ) +{ +#ifdef __PS3__ + if(millisecs == 0) + { + // https://ps3.scedev.net/forums/thread/116470/ + // "sys_timer_usleep(0) does not yield the CPU." + sys_ppu_thread_yield(); + } + else + sys_timer_usleep(millisecs * 1000); +#elif defined __ORBIS__ + sceKernelUsleep(((SceKernelUseconds)millisecs) * 1000); +#elif defined __PSVITA__ + // 4J Stu - 0 is an error, so add a tiny sleep when we just want to yield + sceKernelDelayThread(millisecs * 1000 + 1); +#else + ::Sleep(millisecs); +#endif // __PS3__ +} + +C4JThread* C4JThread::getCurrentThread() +{ +#ifdef __PS3__ + sys_ppu_thread_t currThreadID; + sys_ppu_thread_get_id(&currThreadID); +#elif defined __ORBIS__ + ScePthread currThreadID = scePthreadSelf(); +#elif defined __PSVITA__ + SceUID currThreadID = sceKernelGetThreadId(); +#else + DWORD currThreadID = GetCurrentThreadId(); +#endif //__PS3__ + EnterCriticalSection(&ms_threadListCS); + + for(int i=0;im_threadID) + { + LeaveCriticalSection(&ms_threadListCS); + return ms_threadList[i]; + } + } + + LeaveCriticalSection(&ms_threadListCS); + + return NULL; +} + +bool C4JThread::isMainThread() +{ +#ifdef _XBOX_ONE + return getCurrentThread() == m_mainThread; +#else + return getCurrentThread() == &m_mainThread; +#endif +} + +C4JThread::Event::Event(EMode mode/* = e_modeAutoClear*/) +{ + m_mode = mode; +#ifdef __PS3__ + sys_event_flag_attribute_t attr; + // default values taken from sys_event_flag_attribute_initialize + attr.attr_protocol = SYS_SYNC_PRIORITY; + attr.attr_pshared = SYS_SYNC_NOT_PROCESS_SHARED; + attr.key = 0; + attr.flags = 0; + attr.type = SYS_SYNC_WAITER_SINGLE; + attr.name[0] = '\0'; + sys_event_flag_attribute_initialize(attr); + + int err = sys_event_flag_create(&m_event, &attr, 0); + +#elif defined __ORBIS__ + char name[1] = {0}; + sceKernelCreateEventFlag( &m_event, name, SCE_KERNEL_EVF_ATTR_TH_FIFO | SCE_KERNEL_EVF_ATTR_MULTI, 0, NULL); +#elif defined __PSVITA__ + char name[1] = {0}; + m_event = sceKernelCreateEventFlag( name, SCE_KERNEL_EVF_ATTR_TH_FIFO | SCE_KERNEL_EVF_ATTR_MULTI, 0, NULL); +#else + m_event = CreateEvent( NULL, (m_mode == e_modeManualClear), FALSE, NULL ); +#endif //__PS3__ +} + + +C4JThread::Event::~Event() +{ +#ifdef __PS3__ + sys_event_flag_destroy(m_event); +#elif defined __ORBIS__ + sceKernelDeleteEventFlag(m_event); +#elif defined __PSVITA__ + sceKernelDeleteEventFlag(m_event); +#else + CloseHandle( m_event ); +#endif // __PS3__ +} + + +void C4JThread::Event::Set() +{ +#ifdef __PS3__ + int err =sys_event_flag_set(m_event, 1); +#elif defined __ORBIS__ + sceKernelSetEventFlag(m_event, 1); +#elif defined __PSVITA__ + sceKernelSetEventFlag(m_event, 1); +#else + SetEvent(m_event); +#endif //__PS3__ +} + +void C4JThread::Event::Clear() +{ +#ifdef __PS3__ + int err =sys_event_flag_clear(m_event, ~(1)); +#elif defined __ORBIS__ + sceKernelClearEventFlag(m_event, ~(1)); +#elif defined __PSVITA__ + sceKernelClearEventFlag(m_event, ~1); +#else + ResetEvent(m_event); +#endif //__PS3__ +} + +DWORD C4JThread::Event::WaitForSignal( int timeoutMs ) +{ +#ifdef __PS3__ + if(timeoutMs == INFINITE) + timeoutMs = SYS_NO_TIMEOUT ; + int timoutMicrosecs = timeoutMs * 1000; + uint32_t mode = SYS_EVENT_FLAG_WAIT_AND; + if(m_mode == e_modeAutoClear) + mode |= SYS_EVENT_FLAG_WAIT_CLEAR; + int err = sys_event_flag_wait(m_event, 1, mode, 0, timoutMicrosecs); + + switch(err) + { + case CELL_OK: return WAIT_OBJECT_0; + case ETIMEDOUT: return WAIT_TIMEOUT; + case ECANCELED: return WAIT_ABANDONED; + default: return WAIT_FAILED; + } + +#elif defined __ORBIS__ + SceKernelUseconds timeoutMicrosecs; + SceKernelUseconds *pTimeoutMicrosecs; + if( timeoutMs == INFINITE ) + { + pTimeoutMicrosecs = NULL; + } + else + { + timeoutMicrosecs = ((SceKernelUseconds)timeoutMs) * 1000; + pTimeoutMicrosecs = &timeoutMicrosecs; + } + uint32_t waitMode = SCE_KERNEL_EVF_WAITMODE_AND; + if(m_mode == e_modeAutoClear) + { + waitMode |= SCE_KERNEL_EVF_WAITMODE_CLEAR_PAT; + } + int err = sceKernelWaitEventFlag(m_event, 1, waitMode, NULL, pTimeoutMicrosecs); + switch(err) + { + case SCE_OK: return WAIT_OBJECT_0; + case SCE_KERNEL_ERROR_ETIMEDOUT: return WAIT_TIMEOUT; + case SCE_KERNEL_ERROR_ECANCELED: return WAIT_ABANDONED; + default: return WAIT_FAILED; + } +#elif defined __PSVITA__ + SceUInt32 timeoutMicrosecs; + SceUInt32 *pTimeoutMicrosecs; + if( timeoutMs == INFINITE ) + { + pTimeoutMicrosecs = NULL; + } + else + { + timeoutMicrosecs = ((SceInt32)timeoutMs) * 1000; + pTimeoutMicrosecs = &timeoutMicrosecs; + } + uint32_t waitMode = SCE_KERNEL_EVF_WAITMODE_AND; + if(m_mode == e_modeAutoClear) + { + waitMode |= SCE_KERNEL_EVF_WAITMODE_CLEAR_ALL; + } + int err = sceKernelWaitEventFlag(m_event, 1, waitMode, NULL, pTimeoutMicrosecs); + switch(err) + { + case SCE_OK: return WAIT_OBJECT_0; + case SCE_KERNEL_ERROR_WAIT_TIMEOUT: return WAIT_TIMEOUT; + case SCE_KERNEL_ERROR_WAIT_CANCEL: return WAIT_ABANDONED; + default: return WAIT_FAILED; + } +#else + return WaitForSingleObject(m_event, timeoutMs); +#endif // __PS3__ +} + +C4JThread::EventArray::EventArray( int size, EMode mode/* = e_modeAutoClear*/) +{ + assert(size<32); + m_size = size; + m_mode = mode; +#ifdef __PS3__ + sys_event_flag_attribute_t attr; + // default values taken from sys_event_flag_attribute_initialize + attr.attr_protocol = SYS_SYNC_PRIORITY; + attr.attr_pshared = SYS_SYNC_NOT_PROCESS_SHARED; + attr.key = 0; + attr.flags = 0; + attr.type = SYS_SYNC_WAITER_SINGLE; + attr.name[0] = '\0'; + sys_event_flag_attribute_initialize(attr); + int err = sys_event_flag_create(&m_events, &attr, 0); + assert(err == CELL_OK); +#elif defined __ORBIS__ + char name[1] = {0}; + sceKernelCreateEventFlag( &m_events, name, SCE_KERNEL_EVF_ATTR_TH_FIFO | SCE_KERNEL_EVF_ATTR_MULTI, 0, NULL); +#elif defined __PSVITA__ + char name[1] = {0}; + m_events = sceKernelCreateEventFlag( name, SCE_KERNEL_EVF_ATTR_TH_FIFO | SCE_KERNEL_EVF_ATTR_MULTI, 0, NULL); +#else + m_events = new HANDLE[size]; + for(int i=0;i= 0) + m_thread->SetProcessor(m_processor); + if(m_priority != THREAD_PRIORITY_HIGHEST+1) + m_thread->SetPriority(m_priority); + m_thread->Run(); +} + +void C4JThread::EventQueue::sendEvent( Level* pLevel ) +{ + if(m_thread == NULL) + init(); + EnterCriticalSection(&m_critSect); + m_queue.push(pLevel); + m_startEvent->Set(0); + m_finishedEvent->Clear(); + LeaveCriticalSection(&m_critSect); +} + +void C4JThread::EventQueue::waitForFinish() +{ + if(m_thread == NULL) + init(); + EnterCriticalSection(&m_critSect); + if(m_queue.empty()) + { + LeaveCriticalSection((&m_critSect)); + return; + } + LeaveCriticalSection((&m_critSect)); + m_finishedEvent->WaitForSignal(INFINITE); +} + +int C4JThread::EventQueue::threadFunc( void* lpParam ) +{ + EventQueue* p = (EventQueue*)lpParam; + p->threadPoll(); + return 0; +} + +void C4JThread::EventQueue::threadPoll() +{ + ShutdownManager::HasStarted(ShutdownManager::eEventQueueThreads, m_startEvent); + + if(m_threadInitFunc) + m_threadInitFunc(); + + while(ShutdownManager::ShouldRun(ShutdownManager::eEventQueueThreads)) + { + + DWORD err = m_startEvent->WaitForAny(INFINITE); + if(err == WAIT_OBJECT_0) + { + bool bListEmpty = true; + do + { + EnterCriticalSection(&m_critSect); + void* updateParam = m_queue.front(); + LeaveCriticalSection(&m_critSect); + + m_updateFunc(updateParam); + + EnterCriticalSection(&m_critSect); + m_queue.pop(); + bListEmpty = m_queue.empty(); + if(bListEmpty) + { + m_finishedEvent->Set(); + } + LeaveCriticalSection(&m_critSect); + + } while(!bListEmpty); + } + }; + + ShutdownManager::HasFinished(ShutdownManager::eEventQueueThreads); +} + + +#ifdef __ORBIS__ + +void C4JThread::PushAffinityAllCores() +{ + assert(m_oldAffinityMask == 0); + int err; + ScePthread currThreadID = scePthreadSelf(); + err = scePthreadGetaffinity(currThreadID, &m_oldAffinityMask); + assert(err == SCE_OK); + err = scePthreadSetaffinity(currThreadID, 63); + assert(err == SCE_OK); + + +} + +void C4JThread::PopAffinity() +{ + int err; + ScePthread currThreadID = scePthreadSelf(); + err = scePthreadSetaffinity(currThreadID, m_oldAffinityMask); + m_oldAffinityMask = 0; + assert(err == SCE_OK); +} + +#endif // __ORBIS__ \ No newline at end of file diff --git a/Minecraft.World/C4JThread.h b/Minecraft.World/C4JThread.h new file mode 100644 index 00000000..9a303c7b --- /dev/null +++ b/Minecraft.World/C4JThread.h @@ -0,0 +1,225 @@ +#pragma once +#include + +typedef int (C4JThreadStartFunc)(void* lpThreadParameter); + +class Level; + +#if defined(_XBOX_ONE) || defined(__ORBIS__) + +#define CPU_CORE_MAIN_THREAD 0 + +#define CPU_CORE_SERVER 1 + +#define CPU_CORE_CHUNK_UPDATE 2 +#define CPU_CORE_REMOVE_PLAYER 2 + +#define CPU_CORE_CHUNK_REBUILD_A 3 +#define CPU_CORE_SAVE_THREAD_A 3 +#define CPU_CORE_UI_SCENE 3 +#define CPU_CORE_POST_PROCESSING 3 +#define CPU_CORE_DQR_REALTIMESESSION 3 + +#define CPU_CORE_CHUNK_REBUILD_B 4 +#define CPU_CORE_SAVE_THREAD_B 4 +#define CPU_CORE_TILE_UPDATE 4 +#define CPU_CORE_CONNECTIONS 4 + +#define CPU_CORE_CHUNK_REBUILD_C 5 +#define CPU_CORE_SAVE_THREAD_C 5 +#define CPU_CORE_LEADERBOARDS 5 // Orbis only + +#else + +#define CPU_CORE_MAIN_THREAD 0 + +#define CPU_CORE_CHUNK_REBUILD_A 1 +#define CPU_CORE_SAVE_THREAD_A 1 +#define CPU_CORE_TILE_UPDATE 1 +#define CPU_CORE_CONNECTIONS 1 + +#define CPU_CORE_CHUNK_UPDATE 2 +#define CPU_CORE_REMOVE_PLAYER 2 + +#define CPU_CORE_CHUNK_REBUILD_B 3 +#define CPU_CORE_SAVE_THREAD_B 3 +#define CPU_CORE_UI_SCENE 3 +#define CPU_CORE_POST_PROCESSING 3 + +#define CPU_CORE_SERVER 4 + +#define CPU_CORE_CHUNK_REBUILD_C 5 +#define CPU_CORE_SAVE_THREAD_C 5 +#define CPU_CORE_LEADERBOARDS 5 // Sony only + +#endif + +class C4JThread +{ +public: + + class Event + { + public: + enum EMode + { + e_modeAutoClear, + e_modeManualClear + }; + Event(EMode mode = e_modeAutoClear); + ~Event(); + void Set(); + void Clear(); + DWORD WaitForSignal(int timeoutMs); + + private: + EMode m_mode; + #ifdef __PS3__ + sys_event_flag_t m_event; + #elif defined __ORBIS__ + SceKernelEventFlag m_event; + #elif defined __PSVITA__ + SceUID m_event; + #else + HANDLE m_event; + #endif // __PS3__ + }; + + class EventArray + { + public: + enum EMode + { + e_modeAutoClear, + e_modeManualClear + }; + + EventArray(int size, EMode mode = e_modeAutoClear); + + void Set(int index); + void Clear(int index); + void SetAll(); + void ClearAll(); + DWORD WaitForAll(int timeoutMs); + DWORD WaitForAny(int timeoutMs); + DWORD WaitForSingle(int index, int timeoutMs); +#ifdef __PS3__ + void Cancel(); +#endif + + private: + int m_size; + EMode m_mode; +#ifdef __PS3__ + sys_event_flag_t m_events; +#elif defined __ORBIS__ + SceKernelEventFlag m_events; +#elif defined __PSVITA__ + SceUID m_events; +#else + HANDLE* m_events; +#endif // __PS3__ + }; + + + + class EventQueue + { + typedef void (UpdateFunc)(void* lpParameter); + typedef void (ThreadInitFunc)(); + + C4JThread* m_thread; + std::queue m_queue; + C4JThread::EventArray* m_startEvent; + C4JThread::Event* m_finishedEvent; + CRITICAL_SECTION m_critSect; + UpdateFunc* m_updateFunc; + ThreadInitFunc* m_threadInitFunc; + char m_threadName[64]; + int m_processor; + int m_priority; + void init(); + static int threadFunc(void* lpParam); + void threadPoll(); + + public: + EventQueue(UpdateFunc* updateFunc, ThreadInitFunc threadInitFunc, const char* szThreadName); + void setProcessor(int proc) { m_processor = proc; if(m_thread) m_thread->SetProcessor(proc); } + void setPriority(int priority) { m_priority = priority; if(m_thread) m_thread->SetPriority(priority); } + void sendEvent(Level* pLevel); + void waitForFinish(); + }; + + + + C4JThread(C4JThreadStartFunc* startFunc, void* param, const char* threadName, int stackSize = 0); + C4JThread( const char* mainThreadName ); // only used for the main thread + ~C4JThread(); + + void Run(); + bool isRunning() { return m_isRunning; } + bool hasStarted() { return m_hasStarted; } + void SetProcessor(int proc); + void SetPriority(int priority); + DWORD WaitForCompletion(int timeoutMs); + int GetExitCode(); + char* getName() { return m_threadName; } + static void Sleep(int millisecs); + static C4JThread* getCurrentThread(); + static bool isMainThread(); + static char* getCurrentThreadName() { return getCurrentThread()->getName(); } + +#ifdef __ORBIS__ + static void PushAffinityAllCores(); // PS4 only + static void PopAffinity(); + static __thread SceKernelCpumask m_oldAffinityMask; +#endif // __ORBIS__ + +#ifdef _XBOX_ONE + static void StaticInit(); +#endif + +private: + void* m_threadParam; + C4JThreadStartFunc* m_startFunc; + int m_stackSize; + char m_threadName[64]; + bool m_isRunning; + bool m_hasStarted; + int m_exitCode; + __int64 m_lastSleepTime; + static std::vector ms_threadList; + static CRITICAL_SECTION ms_threadListCS; + +#ifdef _XBOX_ONE + // 4J Stu - On XboxOne the main thread is not the one that does all the static init, so we have to set this up later + static C4JThread *m_mainThread; +#else + static C4JThread m_mainThread; +#endif + +#ifdef __PS3__ + sys_ppu_thread_t m_threadID; + Event *m_completionFlag; + int m_priority; + static void entryPoint(uint64_t); +#elif defined __ORBIS__ + ScePthreadAttr m_threadAttr; + ScePthread m_threadID; + Event *m_completionFlag; + int m_priority; + static void *entryPoint(void *); +#elif defined __PSVITA__ + SceUID m_threadID; + Event *m_completionFlag; + int m_priority; + static SceInt32 entryPoint(SceSize argSize, void *pArgBlock); +#else + DWORD m_threadID; + HANDLE m_threadHandle; + Event *m_completionFlag; + static DWORD WINAPI entryPoint(LPVOID lpParam); +#endif +}; +void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName ); + diff --git a/Minecraft.World/CactusFeature.cpp b/Minecraft.World/CactusFeature.cpp new file mode 100644 index 00000000..175c4ddb --- /dev/null +++ b/Minecraft.World/CactusFeature.cpp @@ -0,0 +1,27 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "CactusFeature.h" +#include "net.minecraft.world.level.tile.h" + +bool CactusFeature::place(Level *level, Random *random, int x, int y, int z) +{ + for (int i = 0; i < 10; i++) { + int x2 = x + random->nextInt(8) - random->nextInt(8); + int y2 = y + random->nextInt(4) - random->nextInt(4); + int z2 = z + random->nextInt(8) - random->nextInt(8); + if (level->isEmptyTile(x2, y2, z2)) + { + int h = 1 + random->nextInt(random->nextInt(3) + 1); + for (int yy = 0; yy < h; yy++) + { + if (Tile::cactus->canSurvive(level, x2, y2+yy, z2)) + { + level->setTileNoUpdate(x2, y2+yy, z2, Tile::cactus_Id); + } + } + } + } + + return true; +} + diff --git a/Minecraft.World/CactusFeature.h b/Minecraft.World/CactusFeature.h new file mode 100644 index 00000000..6040289b --- /dev/null +++ b/Minecraft.World/CactusFeature.h @@ -0,0 +1,9 @@ +#pragma once +#include "Feature.h" + + +class CactusFeature : public Feature +{ +public: + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/CactusTile.cpp b/Minecraft.World/CactusTile.cpp new file mode 100644 index 00000000..b72570e1 --- /dev/null +++ b/Minecraft.World/CactusTile.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.h" +#include "net.minecraft.world.h" +#include "CactusTile.h" + +CactusTile::CactusTile(int id) : Tile(id, Material::cactus,isSolidRender()) +{ + setTicking(true); + iconTop = NULL; + iconBottom = NULL; +} + +void CactusTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (level->isEmptyTile(x, y + 1, z)) + { + int height = 1; + while (level->getTile(x, y - height, z) == id) + { + height++; + } + if (height < 3) + { + int age = level->getData(x, y, z); + if (age == 15) + { + level->setTile(x, y + 1, z, id); + level->setData(x, y, z, 0); + } else + { + level->setData(x, y, z, age + 1); + } + } + } +} + +AABB *CactusTile::getAABB(Level *level, int x, int y, int z) +{ + float r = 1 / 16.0f; + return AABB::newTemp(x + r, y, z + r, x + 1 - r, y + 1 - r, z + 1 - r); + +} + +AABB *CactusTile::getTileAABB(Level *level, int x, int y, int z) +{ + float r = 1 / 16.0f; + return AABB::newTemp(x + r, y, z + r, x + 1 - r, y + 1, z + 1 - r); +} + +Icon *CactusTile::getTexture(int face, int data) +{ + if (face == Facing::UP) return iconTop; + if (face == Facing::DOWN) return iconBottom; + else return icon; +} + +bool CactusTile::isCubeShaped() +{ + return false; +} + +bool CactusTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +int CactusTile::getRenderShape() +{ + return Tile::SHAPE_CACTUS; +} + +bool CactusTile::mayPlace(Level *level, int x, int y, int z) +{ + if (!Tile::mayPlace(level, x, y, z)) return false; + + return canSurvive(level, x, y, z); +} + +void CactusTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (!canSurvive(level, x, y, z)) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } +} + +bool CactusTile::canSurvive(Level *level, int x, int y, int z) +{ + if (level->getMaterial(x - 1, y, z)->isSolid()) return false; + if (level->getMaterial(x + 1, y, z)->isSolid()) return false; + if (level->getMaterial(x, y, z - 1)->isSolid()) return false; + if (level->getMaterial(x, y, z + 1)->isSolid()) return false; + int below = level->getTile(x, y - 1, z); + return below == Tile::cactus_Id || below == Tile::sand_Id; +} + +void CactusTile::entityInside(Level *level, int x, int y, int z, shared_ptr entity) +{ + entity->hurt(DamageSource::cactus, 1); +} + +void CactusTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"cactus_side"); + iconTop = iconRegister->registerIcon(L"cactus_top"); + iconBottom = iconRegister->registerIcon(L"cactus_bottom"); +} + +bool CactusTile::shouldTileTick(Level *level, int x,int y,int z) +{ + return level->isEmptyTile(x, y + 1, z); +} diff --git a/Minecraft.World/CactusTile.h b/Minecraft.World/CactusTile.h new file mode 100644 index 00000000..3c5e9b49 --- /dev/null +++ b/Minecraft.World/CactusTile.h @@ -0,0 +1,39 @@ +#pragma once +#include "Tile.h" +#include "Material.h" +#include "Definitions.h" + +class Random; +class Level; +class ChunkRebuildData; + +class CactusTile : public Tile +{ + friend class Tile; + friend class ChunkRebuildData; + +private: + Icon *iconTop; + Icon *iconBottom; + +protected: + CactusTile(int id); + +public: + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual AABB *getTileAABB(Level *level, int x, int y, int z); + virtual Icon *getTexture(int face, int data); + virtual bool isCubeShaped(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual int getRenderShape(); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual bool canSurvive(Level *level, int x, int y, int z); + virtual void entityInside(Level *level, int x, int y, int z, shared_ptr entity); + //@Override + void registerIcons(IconRegister *iconRegister); + + // 4J Added so we can check before we try to add a tile to the tick list if it's actually going to do seomthing + virtual bool shouldTileTick(Level *level, int x,int y,int z); +}; \ No newline at end of file diff --git a/Minecraft.World/CakeTile.cpp b/Minecraft.World/CakeTile.cpp new file mode 100644 index 00000000..1254e10e --- /dev/null +++ b/Minecraft.World/CakeTile.cpp @@ -0,0 +1,151 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.food.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.h" +#include "net.minecraft.h" +#include "CakeTile.h" + + +CakeTile::CakeTile(int id) : Tile(id, Material::cake,isSolidRender()) +{ + setTicking(true); + + iconTop = NULL; + iconBottom = NULL; + iconInner = NULL; +} + +void CakeTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + int d = level->getData(x, y, z); + float r = 1 / 16.0f; + float r2 = (1 + d * 2) / 16.0f; + float h = 8 / 16.0f; + this->setShape(r2, 0, r, 1 - r, h, 1 - r); +} + +void CakeTile::updateDefaultShape() +{ + float r = 1 / 16.0f; + float h = 8 / 16.0f; + this->setShape(r, 0, r, 1 - r, h, 1 - r); +} + +AABB *CakeTile::getAABB(Level *level, int x, int y, int z) +{ + int d = level->getData(x, y, z); + float r = 1 / 16.0f; + float r2 = (1 + d * 2) / 16.0f; + float h = 8 / 16.0f; + return AABB::newTemp(x + r2, y, z + r, x + 1 - r, y + h - r, z + 1 - r); +} + +AABB *CakeTile::getTileAABB(Level *level, int x, int y, int z) +{ + int d = level->getData(x, y, z); + float r = 1 / 16.0f; + float r2 = (1 + d * 2) / 16.0f; + float h = 8 / 16.0f; + return AABB::newTemp(x + r2, y, z + r, x + 1 - r, y + h, z + 1 - r); +} + +Icon *CakeTile::getTexture(int face, int data) +{ + if (face == Facing::UP) return iconTop; + if (face == Facing::DOWN) return iconBottom; + if (data > 0 && face == Facing::WEST) return iconInner; + return icon; +} + +void CakeTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"cake_side"); + iconInner = iconRegister->registerIcon(L"cake_inner"); + iconTop = iconRegister->registerIcon(L"cake_top"); + iconBottom = iconRegister->registerIcon(L"cake_bottom"); +} + +bool CakeTile::isCubeShaped() +{ + return false; +} + +bool CakeTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +// 4J-PB - Adding a TestUse for tooltip display +bool CakeTile::TestUse() +{ + return true; +} + +bool CakeTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if( soundOnly ) return false; + eat(level, x, y, z, player); + return true; +} + +void CakeTile::attack(Level *level, int x, int y, int z, shared_ptr player) +{ + eat(level, x, y, z, player); +} + +void CakeTile::eat(Level *level, int x, int y, int z, shared_ptr player) +{ + if (player->canEat(false)) + { + player->getFoodData()->eat(2, FoodConstants::FOOD_SATURATION_POOR); + + int d = level->getData(x, y, z) + 1; + if (d >= 6) + { + level->setTile(x, y, z, 0); + } else + { + level->setData(x, y, z, d); + level->setTileDirty(x, y, z); + } + } +} + +bool CakeTile::mayPlace(Level *level, int x, int y, int z) +{ + if (!Tile::mayPlace(level, x, y, z)) return false; + + return canSurvive(level, x, y, z); +} + +void CakeTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (!canSurvive(level, x, y, z)) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } +} + +bool CakeTile::canSurvive(Level *level, int x, int y, int z) +{ + return level->getMaterial(x, y - 1, z)->isSolid(); +} + +int CakeTile::getResourceCount(Random *random) +{ + return 0; +} + +int CakeTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return 0; +} + +int CakeTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Item::cake_Id; +} \ No newline at end of file diff --git a/Minecraft.World/CakeTile.h b/Minecraft.World/CakeTile.h new file mode 100644 index 00000000..7f03577d --- /dev/null +++ b/Minecraft.World/CakeTile.h @@ -0,0 +1,44 @@ +#pragma once +#include "Tile.h" +#include "Material.h" +#include "Definitions.h" + +class Player; +class Random; +class Level; +class ChunkRebuildData; + +class CakeTile : public Tile +{ + friend class Tile; + friend class ChunkRebuildData; + +private: + Icon *iconTop; + Icon *iconBottom; + Icon *iconInner; + +protected: + CakeTile(int id); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual void updateDefaultShape(); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual AABB *getTileAABB(Level *level, int x, int y, int z); + virtual Icon *getTexture(int face, int data); + //@Override + void registerIcons(IconRegister *iconRegister); + virtual bool isCubeShaped(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool TestUse(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + virtual void attack(Level *level, int x, int y, int z, shared_ptr player); +private: + void eat(Level *level, int x, int y, int z, shared_ptr player); +public: + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual bool canSurvive(Level *level, int x, int y, int z); + virtual int getResourceCount(Random *random); + virtual int getResource(int data, Random *random, int playerBonusLevel); + int cloneTileId(Level *level, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/CanyonFeature.cpp b/Minecraft.World/CanyonFeature.cpp new file mode 100644 index 00000000..b96c97e0 --- /dev/null +++ b/Minecraft.World/CanyonFeature.cpp @@ -0,0 +1,185 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "CanyonFeature.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.biome.h" + +void CanyonFeature::addTunnel(__int64 seed, int xOffs, int zOffs, byteArray blocks, double xCave, double yCave, double zCave, float thickness, float yRot, float xRot, int step, int dist, double yScale) +{ + MemSect(49); + Random *random = new Random(seed); + MemSect(0); + double xMid = xOffs * 16 + 8; + double zMid = zOffs * 16 + 8; + + float yRota = 0; + float xRota = 0; + // int dist = CAVE_RADIUS * 16 - 16; + // if (step>0) dist = step*2; + + if (dist <= 0) + { + int max = radius * 16 - 16; + dist = max - random->nextInt(max / 4); + } + bool singleStep = false; + + if (step == -1) + { + step = dist / 2; + singleStep = true; + } + + float f = 1; + for (int i = 0; i < Level::genDepth; i++) + { + if (i == 0 || random->nextInt(3) == 0) + { + f = 1 + (random->nextFloat() * random->nextFloat()) * 1.0f; + } + rs[i] = f * f; + } + + for (; step < dist; step++) + { + double rad = 1.5 + (Mth::sin(step * PI / dist) * thickness) * 1; + double yRad = rad * yScale; + + rad *= (random->nextFloat() * 0.25 + 0.75); + yRad *= (random->nextFloat() * 0.25 + 0.75); + + float xc = Mth::cos(xRot); + float xs = Mth::sin(xRot); + xCave += Mth::cos(yRot) * xc; + yCave += xs; + zCave += Mth::sin(yRot) * xc; + + xRot *= 0.7f; + + xRot += xRota * 0.05f; + yRot += yRota * 0.05f; + + xRota *= 0.80f; + yRota *= 0.50f; + xRota += (random->nextFloat() - random->nextFloat()) * random->nextFloat() * 2; + yRota += (random->nextFloat() - random->nextFloat()) * random->nextFloat() * 4; + + if (!singleStep && random->nextInt(4) == 0) continue; + + { + double xd = xCave - xMid; + double zd = zCave - zMid; + double remaining = dist - step; + double rr = (thickness + 2) + 16; + if (xd * xd + zd * zd - (remaining * remaining) > rr * rr) + { + delete random; + return; + } + } + + if (xCave < xMid - 16 - rad * 2 || zCave < zMid - 16 - rad * 2 || xCave > xMid + 16 + rad * 2 || zCave > zMid + 16 + rad * 2) continue; + + int x0 = Mth::floor(xCave - rad) - xOffs * 16 - 1; + int x1 = Mth::floor(xCave + rad) - xOffs * 16 + 1; + + int y0 = Mth::floor(yCave - yRad) - 1; + int y1 = Mth::floor(yCave + yRad) + 1; + + int z0 = Mth::floor(zCave - rad) - zOffs * 16 - 1; + int z1 = Mth::floor(zCave + rad) - zOffs * 16 + 1; + + if (x0 < 0) x0 = 0; + if (x1 > 16) x1 = 16; + + if (y0 < 1) y0 = 1; + if (y1 > Level::genDepth - 8) y1 = Level::genDepth - 8; + + if (z0 < 0) z0 = 0; + if (z1 > 16) z1 = 16; + + bool detectedWater = false; + for (int xx = x0; !detectedWater && xx < x1; xx++) + { + for (int zz = z0; !detectedWater && zz < z1; zz++) + { + for (int yy = y1 + 1; !detectedWater && yy >= y0 - 1; yy--) + { + int p = (xx * 16 + zz) * Level::genDepth + yy; + if (yy < 0 || yy >= Level::genDepth) continue; + if (blocks[p] == Tile::water_Id || blocks[p] == Tile::calmWater_Id) + { + detectedWater = true; + } + if (yy != y0 - 1 && xx != x0 && xx != x1 - 1 && zz != z0 && zz != z1 - 1) + { + yy = y0; + } + } + } + } + if (detectedWater) continue; + + for (int xx = x0; xx < x1; xx++) + { + double xd = ((xx + xOffs * 16 + 0.5) - xCave) / rad; + for (int zz = z0; zz < z1; zz++) + { + double zd = ((zz + zOffs * 16 + 0.5) - zCave) / rad; + int p = (xx * 16 + zz) * Level::genDepth + y1; + bool hasGrass = false; + if (xd * xd + zd * zd < 1) + { + for (int yy = y1 - 1; yy >= y0; yy--) + { + double yd = (yy + 0.5 - yCave) / yRad; + if ((xd * xd + zd * zd) * rs[yy] + (yd * yd / 6) < 1) + { + int block = blocks[p]; + if (block == Tile::grass_Id) hasGrass = true; + if (block == Tile::rock_Id || block == Tile::dirt_Id || block == Tile::grass_Id) + { + if (yy < 10) + { + blocks[p] = (byte) Tile::lava_Id; + } + else + { + blocks[p] = (byte) 0; + if (hasGrass && blocks[p - 1] == Tile::dirt_Id) blocks[p - 1] = (byte) level->getBiome(xx + xOffs * 16, zz + zOffs * 16)->topMaterial; + } + } + } + p--; + } + } + } + } + if (singleStep) break; + } + delete random; +} + +void CanyonFeature::addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks) +{ + if (random->nextInt(50) != 0) return; + + double xCave = x * 16 + random->nextInt(16); + double yCave = random->nextInt(random->nextInt(40) + 8) + 20; + double zCave = z * 16 + random->nextInt(16); + + int tunnels = 1; + + for (int i = 0; i < tunnels; i++) + { + float yRot = random->nextFloat() * PI * 2; + float xRot = ((random->nextFloat() - 0.5f) * 2) / 8; + float thickness = (random->nextFloat() * 2 + random->nextFloat()) * 2; + + addTunnel(random->nextLong(), xOffs, zOffs, blocks, xCave, yCave, zCave, thickness, yRot, xRot, 0, 0, 3.0); + + // 4J Add to feature list + app.AddTerrainFeaturePosition(eTerrainFeature_Ravine,(int)(xCave/16.0),(int)(yCave/16.0)); + } + +} \ No newline at end of file diff --git a/Minecraft.World/CanyonFeature.h b/Minecraft.World/CanyonFeature.h new file mode 100644 index 00000000..cf3f7a61 --- /dev/null +++ b/Minecraft.World/CanyonFeature.h @@ -0,0 +1,14 @@ +#pragma once +#include "LargeFeature.h" + +class Level; + +class CanyonFeature : public LargeFeature +{ +private: + float rs[1024]; + +protected: + void addTunnel(__int64 seed, int xOffs, int zOffs, byteArray blocks, double xCave, double yCave, double zCave, float thickness, float yRot, float xRot, int step, int dist, double yScale); + virtual void addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks); +}; diff --git a/Minecraft.World/CarrotOnAStickItem.cpp b/Minecraft.World/CarrotOnAStickItem.cpp new file mode 100644 index 00000000..9845cc25 --- /dev/null +++ b/Minecraft.World/CarrotOnAStickItem.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.item.h" +#include "CarrotOnAStickItem.h" + +CarrotOnAStickItem::CarrotOnAStickItem(int id) : Item(id) +{ + setMaxStackSize(1); + setMaxDamage(25); +} + +bool CarrotOnAStickItem::isHandEquipped() +{ + return true; +} + +bool CarrotOnAStickItem::isMirroredArt() +{ + return true; +} + +shared_ptr CarrotOnAStickItem::use(shared_ptr itemInstance, Level *level, shared_ptr player) +{ + if (player->isRiding()) + { + shared_ptr pig = dynamic_pointer_cast(player->riding); + if(pig) + { + if (pig->getControlGoal()->canBoost() && itemInstance->getMaxDamage() - itemInstance->getAuxValue() >= 7) + { + pig->getControlGoal()->boost(); + itemInstance->hurt(7, player); + + if (itemInstance->count == 0) + { + shared_ptr replacement = shared_ptr(new ItemInstance(Item::fishingRod)); + replacement->setTag(itemInstance->tag); + return replacement; + } + } + } + } + + return itemInstance; +} \ No newline at end of file diff --git a/Minecraft.World/CarrotOnAStickItem.h b/Minecraft.World/CarrotOnAStickItem.h new file mode 100644 index 00000000..5e98f592 --- /dev/null +++ b/Minecraft.World/CarrotOnAStickItem.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Item.h" + +class CarrotOnAStickItem : public Item +{ +public: + CarrotOnAStickItem(int id); + + bool isHandEquipped(); + bool isMirroredArt(); + shared_ptr use(shared_ptr itemInstance, Level *level, shared_ptr player); +}; \ No newline at end of file diff --git a/Minecraft.World/CarrotTile.cpp b/Minecraft.World/CarrotTile.cpp new file mode 100644 index 00000000..3eba830b --- /dev/null +++ b/Minecraft.World/CarrotTile.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.item.h" +#include "CarrotTile.h" + +CarrotTile::CarrotTile(int id) : CropTile(id) +{ +} + +Icon *CarrotTile::getTexture(int face, int data) +{ + if (data < 7) + { + if (data == 6) + { + data = 5; + } + return icons[data >> 1]; + } + else + { + return icons[3]; + } +} + +int CarrotTile::getBaseSeedId() +{ + return Item::carrots_Id; +} + +int CarrotTile::getBasePlantId() +{ + return Item::carrots_Id; +} + +void CarrotTile::registerIcons(IconRegister *iconRegister) +{ + for (int i = 0; i < 4; i++) + { + icons[i] = iconRegister->registerIcon(L"carrots_" + _toString(i)); + } +} \ No newline at end of file diff --git a/Minecraft.World/CarrotTile.h b/Minecraft.World/CarrotTile.h new file mode 100644 index 00000000..82c1c02b --- /dev/null +++ b/Minecraft.World/CarrotTile.h @@ -0,0 +1,21 @@ +#pragma once + +#include "CropTile.h" + +class CarrotTile : public CropTile +{ + friend class ChunkRebuildData; +private: + Icon *icons[4]; +public: + CarrotTile(int id); + + Icon *getTexture(int face, int data); + +protected: + int getBaseSeedId(); + int getBasePlantId(); + +public: + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/CauldronTile.cpp b/Minecraft.World/CauldronTile.cpp new file mode 100644 index 00000000..4d5640ca --- /dev/null +++ b/Minecraft.World/CauldronTile.cpp @@ -0,0 +1,177 @@ +#include "stdafx.h" +#include "CauldronTile.h" +#include "Facing.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.h" +#include "..\Minecraft.Client\ServerPlayer.h" + +const wstring CauldronTile::TEXTURE_INSIDE = L"cauldron_inner"; +const wstring CauldronTile::TEXTURE_BOTTOM = L"cauldron_bottom"; + +CauldronTile::CauldronTile(int id) : Tile(id, Material::metal, isSolidRender()) +{ + iconInner = NULL; + iconTop = NULL; + iconBottom = NULL; +} + +Icon *CauldronTile::getTexture(int face, int data) +{ + if (face == Facing::UP) + { + return iconTop; + } + if (face == Facing::DOWN) + { + return iconBottom; + } + return icon; +} + +void CauldronTile::registerIcons(IconRegister *iconRegister) +{ + iconInner = iconRegister->registerIcon(L"cauldron_inner"); + iconTop = iconRegister->registerIcon(L"cauldron_top"); + iconBottom = iconRegister->registerIcon(L"cauldron_bottom"); + icon = iconRegister->registerIcon(L"cauldron_side"); +} + +Icon *CauldronTile::getTexture(const wstring &name) +{ + if (name.compare(TEXTURE_INSIDE) == 0) return Tile::cauldron->iconInner; + if (name.compare(TEXTURE_BOTTOM) == 0) return Tile::cauldron->iconBottom; + return NULL; +} + +void CauldronTile::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source) +{ + setShape(0, 0, 0, 1, 5.0f / 16.0f, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + float thickness = 2.0f / 16.0f; + setShape(0, 0, 0, thickness, 1, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + setShape(0, 0, 0, 1, 1, thickness); + Tile::addAABBs(level, x, y, z, box, boxes, source); + setShape(1 - thickness, 0, 0, 1, 1, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + setShape(0, 0, 1 - thickness, 1, 1, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + + updateDefaultShape(); +} + +void CauldronTile::updateDefaultShape() +{ + setShape(0, 0, 0, 1, 1, 1); +} + +bool CauldronTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +int CauldronTile::getRenderShape() +{ + return SHAPE_CAULDRON; +} + +bool CauldronTile::isCubeShaped() +{ + return false; +} + +bool CauldronTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if(soundOnly) return false; + + if (level->isClientSide) + { + return true; + } + + shared_ptr item = player->inventory->getSelected(); + if (item == NULL) + { + return true; + } + + int currentData = level->getData(x, y, z); + + if (item->id == Item::bucket_water_Id) + { + if (currentData < 3) + { + if (!player->abilities.instabuild) + { + player->inventory->setItem(player->inventory->selected, shared_ptr(new ItemInstance(Item::bucket_empty))); + } + + level->setData(x, y, z, 3); + } + return true; + } + else if (item->id == Item::glassBottle_Id) + { + if (currentData > 0) + { + shared_ptr potion = shared_ptr(new ItemInstance(Item::potion, 1, 0)); + if (!player->inventory->add(potion)) + { + level->addEntity(shared_ptr(new ItemEntity(level, x + 0.5, y + 1.5, z + 0.5, potion))); + } + // 4J Stu - Brought forward change to update inventory when filling bottles with water + else if (dynamic_pointer_cast( player ) != NULL) + { + dynamic_pointer_cast( player )->refreshContainer(player->inventoryMenu); + } + // 4J-PB - don't lose the water in creative mode + if (player->abilities.instabuild==false) + { + item->count--; + if (item->count <= 0) + { + player->inventory->setItem(player->inventory->selected, nullptr); + } + } + level->setData(x, y, z, currentData - 1); + } + } + else if (currentData > 0) + { + ArmorItem *armor = dynamic_cast(item->getItem()); + if(armor && armor->getMaterial() == ArmorItem::ArmorMaterial::CLOTH) + { + armor->clearColor(item); + level->setData(x, y, z, currentData - 1); + return true; + } + } + + return true; + +} + +void CauldronTile::handleRain(Level *level, int x, int y, int z) +{ + if (level->random->nextInt(20) != 1) return; + + int data = level->getData(x, y, z); + + if (data < 3) + { + level->setData(x, y, z, data + 1); + } +} + +int CauldronTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::cauldron_Id; +} + +int CauldronTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Item::cauldron_Id; +} \ No newline at end of file diff --git a/Minecraft.World/CauldronTile.h b/Minecraft.World/CauldronTile.h new file mode 100644 index 00000000..4ed3fc90 --- /dev/null +++ b/Minecraft.World/CauldronTile.h @@ -0,0 +1,33 @@ +#pragma once +#include "Tile.h" + +class IconRegister; + +class CauldronTile : public Tile +{ +public: + static const wstring TEXTURE_INSIDE; + static const wstring TEXTURE_BOTTOM; + +private: + Icon *iconInner; + Icon *iconTop; + Icon *iconBottom; + +public: + CauldronTile(int id); + using Tile::getTexture; + virtual Icon *getTexture(int face, int data); + //@Override + void registerIcons(IconRegister *iconRegister); + static Icon *getTexture(const wstring &name); + virtual void addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source); + virtual void updateDefaultShape(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual int getRenderShape(); + virtual bool isCubeShaped(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + virtual void handleRain(Level *level, int x, int y, int z); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int cloneTileId(Level *level, int x, int y, int z); +}; diff --git a/Minecraft.World/CaveFeature.cpp b/Minecraft.World/CaveFeature.cpp new file mode 100644 index 00000000..aad92805 --- /dev/null +++ b/Minecraft.World/CaveFeature.cpp @@ -0,0 +1,92 @@ +#include "stdafx.h" +#include "CaveFeature.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +using namespace std; + + bool CaveFeature::place(Level *level, Random *random, int x, int y, int z) + { + float dir = random->nextFloat() * PI; + double rd = 8; + + double x0 = x + 8 + Mth::sin(dir) * rd; + double x1 = x + 8 - Mth::sin(dir) * rd; + double z0 = z + 8 + Mth::cos(dir) * rd; + double z1 = z + 8 - Mth::cos(dir) * rd; + + double y0 = y + random->nextInt(8) + 2; + double y1 = y + random->nextInt(8) + 2; + + double radius = random->nextDouble() * 4 + 2; + double fuss = random->nextDouble() * 0.6; + + __int64 seed = random->nextLong(); + random->setSeed(seed); + vector toRemove; + + for (int d = 0; d <= 16; d++) + { + double xx = x0 + (x1 - x0) * d / 16; + double yy = y0 + (y1 - y0) * d / 16; + double zz = z0 + (z1 - z0) * d / 16; + + double ss = random->nextDouble(); + double r = (Mth::sin(d / 16.0f * PI) * radius + 1) * ss + 1; + double hr = (Mth::sin(d / 16.0f * PI) * radius + 1) * ss + 1; + + // 4J Stu Added to stop cave features generating areas previously place by game rule generation + if(app.getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + bool intersects = levelGenOptions->checkIntersects((xx - r / 2), (yy - hr / 2), (zz - r / 2), (xx + r / 2), (yy + hr / 2), (zz + r / 2)); + if(intersects) + { + //app.DebugPrintf("Skipping cave feature generation as it overlaps a game rule structure\n"); + return false; + } + } + + for (int x2 = (int) (xx - r / 2); x2 <= (int) (xx + r / 2); x2++) + for (int y2 = (int) (yy - hr / 2); y2 <= (int) (yy + hr / 2); y2++) + for (int z2 = (int) (zz - r / 2); z2 <= (int) (zz + r / 2); z2++) + { + double xd = ((x2 + 0.5) - xx) / (r / 2); + double yd = ((y2 + 0.5) - yy) / (hr / 2); + double zd = ((z2 + 0.5) - zz) / (r / 2); + if (xd * xd + yd * yd + zd * zd < random->nextDouble() * fuss + (1 - fuss)) + { + if (!level->isEmptyTile(x2, y2, z2)) + { + for (int x3 = (x2 - 2); x3 <= (x2 + 1); x3++) + for (int y3 = (y2 - 1); y3 <= (y2 + 1); y3++) + for (int z3 = (z2 - 1); z3 <= (z2 + 1); z3++) + { + if (x3 <= x || z3 <= z || x3 >= x + 16 - 1 || z3 >= z + 16 - 1) return false; + if (level->getMaterial(x3, y3, z3)->isLiquid()) return false; + } + toRemove.push_back(new TilePos(x2, y2, z2)); + } + } + } + } + + AUTO_VAR(itEnd, toRemove.end()); + for (AUTO_VAR(it, toRemove.begin()); it != itEnd; it++) + { + TilePos *p = *it; //toRemove[i]; + level->setTileNoUpdate(p->x, p->y, p->z, 0); + } + + itEnd = toRemove.end(); + for (AUTO_VAR(it, toRemove.begin()); it != itEnd; it++) + { + TilePos *p = *it; //toRemove[i]; + if (level->getTile(p->x, p->y - 1, p->z) == Tile::dirt_Id && level->getDaytimeRawBrightness(p->x, p->y, p->z) > 8) + { + level->setTileNoUpdate(p->x, p->y - 1, p->z, Tile::grass_Id); + } + delete p; + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/CaveFeature.h b/Minecraft.World/CaveFeature.h new file mode 100644 index 00000000..961b2dff --- /dev/null +++ b/Minecraft.World/CaveFeature.h @@ -0,0 +1,10 @@ +#pragma once +#include "Feature.h" +#include "Material.h" + +class Level; + +class CaveFeature : public Feature +{ + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/CaveSpider.cpp b/Minecraft.World/CaveSpider.cpp new file mode 100644 index 00000000..5f7c6028 --- /dev/null +++ b/Minecraft.World/CaveSpider.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" +#include "SharedConstants.h" +#include "net.minecraft.world.effect.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" +#include "..\Minecraft.Client\Textures.h" +#include "CaveSpider.h" + + + +CaveSpider::CaveSpider(Level *level) : Spider(level) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_CAVE_SPIDER; // 4J was "/mob/cavespider.png"; + this->setSize(0.7f, 0.5f); +} + +int CaveSpider::getMaxHealth() +{ + return 12; +} + +float CaveSpider::getModelScale() +{ + return .7f; +} + + +bool CaveSpider::doHurtTarget(shared_ptr target) +{ + if (Spider::doHurtTarget(target)) + { + if ( dynamic_pointer_cast(target) != NULL) + { + int poisonTime = 0; + if (level->difficulty <= Difficulty::EASY) + { + // No poison! + } + else if (level->difficulty == Difficulty::NORMAL) + { + poisonTime = 7; + } + else if (level->difficulty == Difficulty::HARD) + { + poisonTime = 15; + } + + if (poisonTime > 0) { + dynamic_pointer_cast(target)->addEffect(new MobEffectInstance(MobEffect::poison->id, poisonTime * SharedConstants::TICKS_PER_SECOND, 0)); + } + } + + return true; + } + return false; +} + +void CaveSpider::finalizeMobSpawn() +{ + // do nothing +} \ No newline at end of file diff --git a/Minecraft.World/CaveSpider.h b/Minecraft.World/CaveSpider.h new file mode 100644 index 00000000..26f07e5f --- /dev/null +++ b/Minecraft.World/CaveSpider.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Spider.h" + +class CaveSpider : public Spider +{ + public: + eINSTANCEOF GetType() { return eTYPE_CAVESPIDER; } + static Entity *create(Level *level) { return new CaveSpider(level); } + + public: + CaveSpider(Level *level); + + virtual int getMaxHealth(); + virtual float getModelScale(); + virtual bool doHurtTarget(shared_ptr target); + void finalizeMobSpawn(); +}; \ No newline at end of file diff --git a/Minecraft.World/ChatAutoCompletePacket.h b/Minecraft.World/ChatAutoCompletePacket.h new file mode 100644 index 00000000..b1cd1170 --- /dev/null +++ b/Minecraft.World/ChatAutoCompletePacket.h @@ -0,0 +1,56 @@ +#pragma once + +#include "Packet.h" + +class ChatAutoCompletePacket : public Packet +{ +#if 0 + private String message; + + public ChatAutoCompletePacket() { + + } + public ChatAutoCompletePacket(String message) { + this.message = message; + } + + @Override + public void read(DataInputStream dis) throws IOException { + message = readUtf(dis, ChatPacket.MAX_LENGTH); + } + + @Override + public void write(DataOutputStream dos) throws IOException { + writeUtf(message, dos); + } + + @Override + public void handle(PacketListener listener) { + listener.handleChatAutoComplete(this); + } + + @Override + public int getEstimatedSize() { + return 2 + message.length() * 2; + } + + public String getMessage() { + return message; + } + + @Override + public String getDebugInfo() { + return String.format("message='%s'", message); + } + + @Override + public boolean canBeInvalidated() { + return true; + } + + @Override + public boolean isInvalidatedBy(Packet packet) { + return true; + } +#endif +}; \ No newline at end of file diff --git a/Minecraft.World/ChatPacket.cpp b/Minecraft.World/ChatPacket.cpp new file mode 100644 index 00000000..140de15d --- /dev/null +++ b/Minecraft.World/ChatPacket.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" +#include +#include "net.minecraft.h" +#include "net.minecraft.world.entity.player.h" +#include "PacketListener.h" +#include "ChatPacket.h" + +// longest allowed string is "<" + name + "> " + message +const unsigned int ChatPacket::MAX_LENGTH = SharedConstants::maxChatLength + Player::MAX_NAME_LENGTH + 3; + +ChatPacket::ChatPacket() +{ + m_messageType = e_ChatCustom; +} + +// Old chat packet constructor, adds message, custom data and additional message to arg vectors +ChatPacket::ChatPacket(const wstring& message, EChatPacketMessage type /*= e_ChatCustom*/, int customData /*= -1*/, const wstring& additionalMessage /*= L""*/) +{ + m_messageType = type; + if (customData != -1) m_intArgs.push_back(customData); + if (message != L"" || additionalMessage != L"") m_stringArgs.push_back(message); + if (additionalMessage != L"") m_stringArgs.push_back(additionalMessage); +} + +// Read chat packet (throws IOException) +void ChatPacket::read(DataInputStream *dis) +{ + m_messageType = (EChatPacketMessage) dis->readShort(); + + short packedCounts = dis->readShort(); + int stringCount = (packedCounts >> 4) & 0xF; + int intCount = (packedCounts >> 0) & 0xF; + + for(int i = 0; i < stringCount; i++) + { + m_stringArgs.push_back(readUtf(dis, MAX_LENGTH)); + } + + for(int i = 0; i < intCount; i++) + { + m_intArgs.push_back(dis->readInt()); + } +} + +// Write chat packet (throws IOException) +void ChatPacket::write(DataOutputStream *dos) +{ + dos->writeShort(m_messageType); + + short packedCounts = 0; + packedCounts |= (m_stringArgs.size() & 0xF) << 4; + packedCounts |= (m_intArgs.size() & 0xF) << 0; + + dos->writeShort(packedCounts); + + for(int i = 0; i < m_stringArgs.size(); i++) + { + writeUtf(m_stringArgs[i], dos); +} + + for(int i = 0; i < m_intArgs.size(); i++) + { + dos->writeInt(m_intArgs[i]); + } +} + +// Handle chat packet +void ChatPacket::handle(PacketListener *listener) +{ + listener->handleChat(shared_from_this()); +} + +// Get an estimated size of the packet +int ChatPacket::getEstimatedSize() +{ + int stringsSize = 0; + for(int i = 0; i < m_stringArgs.size(); i++) + { + stringsSize += m_stringArgs[i].length(); + } + + return + sizeof(EChatPacketMessage) + // message type + sizeof(short) + // packed arg counts + stringsSize + // string args + (m_intArgs.size() * sizeof(int)); // int args +} \ No newline at end of file diff --git a/Minecraft.World/ChatPacket.h b/Minecraft.World/ChatPacket.h new file mode 100644 index 00000000..c480dfd0 --- /dev/null +++ b/Minecraft.World/ChatPacket.h @@ -0,0 +1,98 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class ChatPacket : public Packet, public enable_shared_from_this +{ + // longest allowed string is "<" + name + "> " + message +private: + static const unsigned int MAX_LENGTH; + +public: + // 4J - We want to be able to localise the messages sent. The enum also allows for the posibility that there + // may be different versions playing the game, so the enum should map to a string id which may be different on + // different versions + enum EChatPacketMessage + { + e_ChatCustom = 0, // No localised string, only the text passed in + e_ChatBedOccupied, + e_ChatBedNoSleep, + e_ChatBedNotValid, + e_ChatBedNotSafe, + e_ChatBedPlayerSleep, + e_ChatBedMeSleep, + e_ChatPlayerLeftGame, + e_ChatPlayerJoinedGame, + e_ChatPlayerKickedFromGame, + e_ChatCannotPlaceLava, + + e_ChatDeathInFire, + e_ChatDeathOnFire, + e_ChatDeathLava, + e_ChatDeathInWall, + e_ChatDeathDrown, + e_ChatDeathStarve, + e_ChatDeathCactus, + e_ChatDeathFall, + e_ChatDeathOutOfWorld, + e_ChatDeathGeneric, + e_ChatDeathExplosion, + e_ChatDeathMagic, + e_ChatDeathMob, + e_ChatDeathPlayer, + e_ChatDeathArrow, + e_ChatDeathFireball, + e_ChatDeathThrown, + e_ChatDeathIndirectMagic, + e_ChatDeathDragonBreath, + e_ChatDeathWither, + e_ChatDeathAnvil, + e_ChatDeathFallingBlock, + e_ChatDeathThorns, + + e_ChatPlayerEnteredEnd, + e_ChatPlayerLeftEnd, + + e_ChatPlayerMaxPigsSheepCows, // tell the players they can't use the spawn egg + e_ChatPlayerMaxChickens, // tell the players they can't use the spawn egg + e_ChatPlayerMaxSquid, // tell the players they can't use the spawn egg + e_ChatPlayerMaxMooshrooms, // tell the players they can't use the spawn egg + e_ChatPlayerMaxWolves, // tell the players they can't use the spawn egg + e_ChatPlayerMaxAnimals, // tell the players they can't use the spawn egg + e_ChatPlayerMaxEnemies, // tell the players they can't use the spawn egg + e_ChatPlayerMaxVillagers, // tell the players they can't use the spawn egg + e_ChatPlayerMaxHangingEntities, // tell the players they hit the picture/itemframe limit + e_ChatPlayerCantSpawnInPeaceful, // Tell the player they can't spawn enemies in peaceful mode + e_ChatPlayerMaxBredAnimals, // Tell the player they can't put this animal in love mode because no breeding can be done + e_ChatPlayerMaxBredPigsSheepCows, // Tell the player they can't put this animal in love mode because no breeding can be done + e_ChatPlayerMaxBredChickens, // Tell the player they can't put this animal in love mode because no breeding can be done + e_ChatPlayerMaxBredMooshrooms, // Tell the player they can't put this animal in love mode because no breeding can be done + e_ChatPlayerMaxBredWolves, // Tell the player they can't put this wolf in love mode because no breeding can be done + e_ChatPlayerCantShearMooshroom, // Tell the player they can't shear because the limits have been reached + e_ChatPlayerMaxBoats, + + e_ChatCommandTeleportSuccess, + e_ChatCommandTeleportMe, + e_ChatCommandTeleportToMe, + + }; + +public: + vector m_stringArgs; + vector m_intArgs; + EChatPacketMessage m_messageType; + + ChatPacket(); + ChatPacket(const wstring& message, EChatPacketMessage type = e_ChatCustom, int customData = -1, const wstring& additionalMessage = L""); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ChatPacket()); } + virtual int getId() { return 3; } +}; + diff --git a/Minecraft.World/ChestTile.cpp b/Minecraft.World/ChestTile.cpp new file mode 100644 index 00000000..b7964e72 --- /dev/null +++ b/Minecraft.World/ChestTile.cpp @@ -0,0 +1,316 @@ +#include "stdafx.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.phys.h" +#include "ChestTile.h" +#include "Facing.h" +#include "Ozelot.h" + +ChestTile::ChestTile(int id) : EntityTile(id, Material::wood, isSolidRender() ) +{ + random = new Random(); + + setShape(1 / 16.0f, 0, 1 / 16.0f, 15 / 16.0f, 14 / 16.0f, 15 / 16.0f); +} + +ChestTile::~ChestTile() +{ + delete random; +} + +bool ChestTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool ChestTile::isCubeShaped() +{ + return false; +} + +int ChestTile::getRenderShape() +{ + return Tile::SHAPE_ENTITYTILE_ANIMATED; +} + +void ChestTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) +{ + if (level->getTile(x, y, z - 1) == id) + { + setShape(1 / 16.0f, 0, 0, 15 / 16.0f, 14 / 16.0f, 15 / 16.0f); + } + else if (level->getTile(x, y, z + 1) == id) + { + setShape(1 / 16.0f, 0, 1 / 16.0f, 15 / 16.0f, 14 / 16.0f, 1); + } + else if (level->getTile(x - 1, y, z) == id) + { + setShape(0, 0, 1 / 16.0f, 15 / 16.0f, 14 / 16.0f, 15 / 16.0f); + } + else if (level->getTile(x + 1, y, z) == id) + { + setShape(1 / 16.0f, 0, 1 / 16.0f, 1, 14 / 16.0f, 15 / 16.0f); + } + else + { + setShape(1 / 16.0f, 0, 1 / 16.0f, 15 / 16.0f, 14 / 16.0f, 15 / 16.0f); + } +} + +void ChestTile::onPlace(Level *level, int x, int y, int z) +{ + EntityTile::onPlace(level, x, y, z); + recalcLockDir(level, x, y, z); + + int n = level->getTile(x, y, z - 1); // face = 2 + int s = level->getTile(x, y, z + 1); // face = 3 + int w = level->getTile(x - 1, y, z); // face = 4 + int e = level->getTile(x + 1, y, z); // face = 5 + if (n == id) recalcLockDir(level, x, y, z - 1); + if (s == id) recalcLockDir(level, x, y, z + 1); + if (w == id) recalcLockDir(level, x - 1, y, z); + if (e == id) recalcLockDir(level, x + 1, y, z); +} + +void ChestTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int n = level->getTile(x, y, z - 1); // face = 2 + int s = level->getTile(x, y, z + 1); // face = 3 + int w = level->getTile(x - 1, y, z); // face = 4 + int e = level->getTile(x + 1, y, z); // face = 5 + + int facing = 0; + int dir = (Mth::floor(by->yRot * 4 / (360) + 0.5)) & 3; + + if (dir == 0) facing = Facing::NORTH; + if (dir == 1) facing = Facing::EAST; + if (dir == 2) facing = Facing::SOUTH; + if (dir == 3) facing = Facing::WEST; + + if (n != id && s != id && w != id && e != id) + { + level->setData(x, y, z, facing); + } + else + { + if ((n == id || s == id) && (facing == Facing::WEST || facing == Facing::EAST)) + { + if (n == id) level->setData(x, y, z - 1, facing); + else level->setData(x, y, z + 1, facing); + level->setData(x, y, z, facing); + } + if ((w == id || e == id) && (facing == Facing::NORTH || facing == Facing::SOUTH)) + { + if (w == id) level->setData(x - 1, y, z, facing); + else level->setData(x + 1, y, z, facing); + level->setData(x, y, z, facing); + } + } + +} + +void ChestTile::recalcLockDir(Level *level, int x, int y, int z) +{ + if (level->isClientSide) + { + return; + } + + int n = level->getTile(x, y, z - 1); // face = 2 + int s = level->getTile(x, y, z + 1); // face = 3 + int w = level->getTile(x - 1, y, z); // face = 4 + int e = level->getTile(x + 1, y, z); // face = 5 + + // Long! + int lockDir = 4; + if (n == id || s == id) + { + int w2 = level->getTile(x - 1, y, n == id ? z - 1 : z + 1); + int e2 = level->getTile(x + 1, y, n == id ? z - 1 : z + 1); + + lockDir = 5; + + int otherDir = -1; + if (n == id) otherDir = level->getData(x, y, z - 1); + else otherDir = level->getData(x, y, z + 1); + if (otherDir == 4) lockDir = 4; + + if ((Tile::solid[w] || Tile::solid[w2]) && !Tile::solid[e] && !Tile::solid[e2]) lockDir = 5; + if ((Tile::solid[e] || Tile::solid[e2]) && !Tile::solid[w] && !Tile::solid[w2]) lockDir = 4; + } + else if (w == id || e == id) + { + int n2 = level->getTile(w == id ? x - 1 : x + 1, y, z - 1); + int s2 = level->getTile(w == id ? x - 1 : x + 1, y, z + 1); + + lockDir = 3; + int otherDir = -1; + if (w == id) otherDir = level->getData(x - 1, y, z); + else otherDir = level->getData(x + 1, y, z); + if (otherDir == 2) lockDir = 2; + + if ((Tile::solid[n] || Tile::solid[n2]) && !Tile::solid[s] && !Tile::solid[s2]) lockDir = 3; + if ((Tile::solid[s] || Tile::solid[s2]) && !Tile::solid[n] && !Tile::solid[n2]) lockDir = 2; + } + else + { + lockDir = 3; + if (Tile::solid[n] && !Tile::solid[s]) lockDir = 3; + if (Tile::solid[s] && !Tile::solid[n]) lockDir = 2; + if (Tile::solid[w] && !Tile::solid[e]) lockDir = 5; + if (Tile::solid[e] && !Tile::solid[w]) lockDir = 4; + } + + level->setData(x, y, z, lockDir); +} + +bool ChestTile::mayPlace(Level *level, int x, int y, int z) +{ + int chestCount = 0; + + if (level->getTile(x - 1, y, z) == id) chestCount++; + if (level->getTile(x + 1, y, z) == id) chestCount++; + if (level->getTile(x, y, z - 1) == id) chestCount++; + if (level->getTile(x, y, z + 1) == id) chestCount++; + + if (chestCount > 1) return false; + + if (isFullChest(level, x - 1, y, z)) return false; + if (isFullChest(level, x + 1, y, z)) return false; + if (isFullChest(level, x, y, z - 1)) return false; + if (isFullChest(level, x, y, z + 1)) return false; + return true; + +} + +bool ChestTile::isFullChest(Level *level, int x, int y, int z) +{ + if (level->getTile(x, y, z) != id) return false; + if (level->getTile(x - 1, y, z) == id) return true; + if (level->getTile(x + 1, y, z) == id) return true; + if (level->getTile(x, y, z - 1) == id) return true; + if (level->getTile(x, y, z + 1) == id) return true; + return false; +} + +void ChestTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + EntityTile::neighborChanged(level, x, y, z, type); + shared_ptr(cte) = dynamic_pointer_cast(level->getTileEntity(x, y, z)); + if (cte != NULL) cte->clearCache(); +} + +void ChestTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + shared_ptr container = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + if (container != NULL ) + { + for (unsigned int i = 0; i < container->getContainerSize(); i++) + { + shared_ptr item = container->getItem(i); + if (item != NULL) + { + float xo = random->nextFloat() * 0.8f + 0.1f; + float yo = random->nextFloat() * 0.8f + 0.1f; + float zo = random->nextFloat() * 0.8f + 0.1f; + + while (item->count > 0) + { + int count = random->nextInt(21) + 10; + if (count > item->count) count = item->count; + item->count -= count; + + shared_ptr newItem = shared_ptr( new ItemInstance(item->id, count, item->getAuxValue()) ); + newItem->set4JData( item->get4JData() ); + shared_ptr itemEntity = shared_ptr(new ItemEntity(level, x + xo, y + yo, z + zo, newItem ) ); + float pow = 0.05f; + itemEntity->xd = (float) random->nextGaussian() * pow; + itemEntity->yd = (float) random->nextGaussian() * pow + 0.2f; + itemEntity->zd = (float) random->nextGaussian() * pow; + if (item->hasTag()) + { + itemEntity->getItem()->setTag((CompoundTag *) item->getTag()->copy()); + } + + level->addEntity(itemEntity); + } + + // 4J Stu - Fix for duplication glitch + container->setItem(i,nullptr); + } + } + } + EntityTile::onRemove(level, x, y, z, id, data); +} + +// 4J-PB - Adding a TestUse for tooltip display +bool ChestTile::TestUse() +{ + return true; +} + +// 4J-PB - changing to 1.5 equivalent +bool ChestTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if( soundOnly ) return true; + + if (level->isClientSide) + { + return true; + } + + shared_ptr container = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + if (container == NULL) return true; + + if (level->isSolidBlockingTile(x, y + 1, z)) return true; + if (isCatSittingOnChest(level,x, y, z)) return true; + + if (level->getTile(x - 1, y, z) == id && (level->isSolidBlockingTile(x - 1, y + 1, z) || isCatSittingOnChest(level, x - 1, y, z))) return true; + if (level->getTile(x + 1, y, z) == id && (level->isSolidBlockingTile(x + 1, y + 1, z) || isCatSittingOnChest(level, x + 1, y, z))) return true; + if (level->getTile(x, y, z - 1) == id && (level->isSolidBlockingTile(x, y + 1, z - 1) || isCatSittingOnChest(level, x, y, z - 1))) return true; + if (level->getTile(x, y, z + 1) == id && (level->isSolidBlockingTile(x, y + 1, z + 1) || isCatSittingOnChest(level, x, y, z + 1))) return true; + + if (level->getTile(x - 1, y, z) == id) container = shared_ptr( new CompoundContainer(IDS_CHEST_LARGE, dynamic_pointer_cast( level->getTileEntity(x - 1, y, z) ), container) ); + if (level->getTile(x + 1, y, z) == id) container = shared_ptr( new CompoundContainer(IDS_CHEST_LARGE, container, dynamic_pointer_cast( level->getTileEntity(x + 1, y, z) )) ); + if (level->getTile(x, y, z - 1) == id) container = shared_ptr( new CompoundContainer(IDS_CHEST_LARGE, dynamic_pointer_cast( level->getTileEntity(x, y, z - 1) ), container) ); + if (level->getTile(x, y, z + 1) == id) container = shared_ptr( new CompoundContainer(IDS_CHEST_LARGE, container, dynamic_pointer_cast( level->getTileEntity(x, y, z + 1) )) ); + + player->openContainer(container); + + return true; +} + +// 4J-PB - added from 1.5 +bool ChestTile::isCatSittingOnChest(Level *level, int x, int y, int z) +{ + vector > *entities = level->getEntitiesOfClass(typeid(Ozelot), AABB::newTemp(x, y + 1, z, x + 1, y + 2, z + 1)); + for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) + { + shared_ptr ocelot = dynamic_pointer_cast(*it); + if(ocelot->isSitting()) + { + return true; + } + } + + return false; +} + +shared_ptr ChestTile::newTileEntity(Level *level) +{ + MemSect(50); + shared_ptr retval = shared_ptr( new ChestTileEntity() ); + MemSect(0); + return retval; +} + +void ChestTile::registerIcons(IconRegister *iconRegister) +{ + // Register wood as the chest's icon, because it's used by the particles + // when destroying the chest + icon = iconRegister->registerIcon(L"wood"); +} diff --git a/Minecraft.World/ChestTile.h b/Minecraft.World/ChestTile.h new file mode 100644 index 00000000..ee2185cb --- /dev/null +++ b/Minecraft.World/ChestTile.h @@ -0,0 +1,38 @@ +#pragma once +#include "EntityTile.h" +#include "Material.h" + +class Player; +class Random; + +class ChestTile : public EntityTile +{ + friend class Tile; +public: + static const int EVENT_SET_OPEN_COUNT = 1; +private: + Random *random; +protected: + ChestTile(int id); + ~ChestTile(); +public: + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual int getRenderShape(); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity = shared_ptr()); + virtual void onPlace(Level *level, int x, int y, int z); + virtual void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + void recalcLockDir(Level *level, int x, int y, int z); + virtual bool mayPlace(Level *level, int x, int y, int z); +private: + bool isFullChest(Level *level, int x, int y, int z); + bool isCatSittingOnChest(Level *level, int x, int y, int z); +public: + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); + virtual bool TestUse(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + shared_ptr newTileEntity(Level *level); + //@Override + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/ChestTileEntity.cpp b/Minecraft.World/ChestTileEntity.cpp new file mode 100644 index 00000000..406b81b5 --- /dev/null +++ b/Minecraft.World/ChestTileEntity.cpp @@ -0,0 +1,289 @@ +using namespace std; + +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.level.h" +#include "TileEntity.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.h" +#include "ChestTileEntity.h" +#include "SoundTypes.h" + + + +ChestTileEntity::ChestTileEntity(bool isBonusChest/* = false*/) : TileEntity() +{ + items = new ItemInstanceArray(9 * 4); + + hasCheckedNeighbors = false; + this->isBonusChest = isBonusChest; + + openness = 0.0f; + oOpenness = 0.0f; + openCount = 0; + tickInterval = 0; +} + +ChestTileEntity::~ChestTileEntity() +{ + delete[] items->data; + delete items; +} + +unsigned int ChestTileEntity::getContainerSize() +{ + return 9 * 3; +} + +shared_ptr ChestTileEntity::getItem(unsigned int slot) +{ + return items->data[slot]; +} + +shared_ptr ChestTileEntity::removeItem(unsigned int slot, int count) +{ + if (items->data[slot] != NULL) + { + if (items->data[slot]->count <= count) + { + shared_ptr item = items->data[slot]; + items->data[slot] = nullptr; + this->setChanged(); + // 4J Stu - Fix for duplication glitch + if(item->count <= 0) return nullptr; + return item; + } + else + { + shared_ptr i = items->data[slot]->remove(count); + if (items->data[slot]->count == 0) items->data[slot] = nullptr; + this->setChanged(); + // 4J Stu - Fix for duplication glitch + if(i->count <= 0) return nullptr; + return i; + } + } + return nullptr; +} + +shared_ptr ChestTileEntity::removeItemNoUpdate(int slot) +{ + if (items->data[slot] != NULL) + { + shared_ptr item = items->data[slot]; + items->data[slot] = nullptr; + return item; + } + return nullptr; +} + +void ChestTileEntity::setItem(unsigned int slot, shared_ptr item) +{ + items->data[slot] = item; + if (item != NULL && item->count > getMaxStackSize()) item->count = getMaxStackSize(); + this->setChanged(); +} + +int ChestTileEntity::getName() +{ + return IDS_TILE_CHEST; +} + + +void ChestTileEntity::load(CompoundTag *base) +{ + TileEntity::load(base); + ListTag *inventoryList = (ListTag *) base->getList(L"Items"); + if( items ) + { + delete [] items->data; + delete items; + } + items = new ItemInstanceArray(getContainerSize()); + for (int i = 0; i < inventoryList->size(); i++) + { + CompoundTag *tag = inventoryList->get(i); + unsigned int slot = tag->getByte(L"Slot") & 0xff; + if (slot >= 0 && slot < items->length) (*items)[slot] = ItemInstance::fromTag(tag); + } + isBonusChest = base->getBoolean(L"bonus"); +} + +void ChestTileEntity::save(CompoundTag *base) +{ + TileEntity::save(base); + ListTag *listTag = new ListTag; + + for (unsigned int i = 0; i < items->length; i++) + { + if (items->data[i] != NULL) + { + CompoundTag *tag = new CompoundTag(); + tag->putByte(L"Slot", (byte) i); + items->data[i]->save(tag); + listTag->add(tag); + } + } + base->put(L"Items", listTag); + base->putBoolean(L"bonus", isBonusChest); +} + +int ChestTileEntity::getMaxStackSize() +{ + return Container::LARGE_MAX_STACK_SIZE; +} + +bool ChestTileEntity::stillValid(shared_ptr player) +{ + if (level->getTileEntity(x, y, z) != shared_from_this() ) return false; + if (player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) > 8 * 8) return false; + return true; +} + +void ChestTileEntity::setChanged() +{ + TileEntity::setChanged(); +} + +void ChestTileEntity::clearCache() +{ + TileEntity::clearCache(); + hasCheckedNeighbors = false; +} + +void ChestTileEntity::checkNeighbors() +{ + if (hasCheckedNeighbors) return; + + hasCheckedNeighbors = true; + n = weak_ptr(); + e = weak_ptr(); + w = weak_ptr(); + s = weak_ptr(); + + if (level->getTile(x - 1, y, z) == Tile::chest_Id) + { + w = dynamic_pointer_cast(level->getTileEntity(x - 1, y, z)); + } + if (level->getTile(x + 1, y, z) == Tile::chest_Id) + { + e = dynamic_pointer_cast(level->getTileEntity(x + 1, y, z)); + } + if (level->getTile(x, y, z - 1) == Tile::chest_Id) + { + n = dynamic_pointer_cast(level->getTileEntity(x, y, z - 1)); + } + if (level->getTile(x, y, z + 1) == Tile::chest_Id) + { + s = dynamic_pointer_cast(level->getTileEntity(x, y, z + 1)); + } + + if (n.lock() != NULL) n.lock()->clearCache(); + if (s.lock() != NULL) s.lock()->clearCache(); + if (e.lock() != NULL) e.lock()->clearCache(); + if (w.lock() != NULL) w.lock()->clearCache(); +} + +void ChestTileEntity::tick() +{ + TileEntity::tick(); + checkNeighbors(); + + if (++tickInterval % 20 * 4 == 0) + { + //level->tileEvent(x, y, z, ChestTile::EVENT_SET_OPEN_COUNT, openCount); + } + + oOpenness = openness; + + float speed = 0.10f; + if (openCount > 0 && openness == 0) + { + if (n.lock() == NULL && w.lock() == NULL) + { + double xc = x + 0.5; + double zc = z + 0.5; + if (s.lock() != NULL) zc += 0.5; + if (e.lock() != NULL) xc += 0.5; + + // 4J-PB - Seems the chest open volume is much louder than other sounds from user reports. We'll tone it down a bit + level->playSound(xc, y + 0.5, zc, eSoundType_RANDOM_CHEST_OPEN, 0.2f, level->random->nextFloat() * 0.1f + 0.9f); + } + } + if ((openCount == 0 && openness > 0) || (openCount > 0 && openness < 1)) + { + float oldOpen = openness; + if (openCount > 0) openness += speed; + else openness -= speed; + if (openness > 1) + { + openness = 1; + } + float lim = 0.5f; + if (openness < lim && oldOpen >= lim) + { + // Fix for #64546 - Customer Encountered: TU7: Chests placed by the Player are closing too fast. + //openness = 0; + if (n.lock() == NULL && w.lock() == NULL) + { + double xc = x + 0.5; + double zc = z + 0.5; + if (s.lock() != NULL) zc += 0.5; + if (e.lock() != NULL) xc += 0.5; + + // 4J-PB - Seems the chest open volume is much louder than other sounds from user reports. We'll tone it down a bit + level->playSound(xc, y + 0.5, zc, eSoundType_RANDOM_CHEST_CLOSE, 0.2f, level->random->nextFloat() * 0.1f + 0.9f); + } + } + if (openness < 0) + { + openness = 0; + } + } + +} + +void ChestTileEntity::triggerEvent(int b0, int b1) +{ + if (b0 == ChestTile::EVENT_SET_OPEN_COUNT) + { + openCount = b1; + } +} + +void ChestTileEntity::startOpen() +{ + openCount++; + level->tileEvent(x, y, z, Tile::chest_Id, ChestTile::EVENT_SET_OPEN_COUNT, openCount); +} + +void ChestTileEntity::stopOpen() +{ + openCount--; + level->tileEvent(x, y, z, Tile::chest_Id, ChestTile::EVENT_SET_OPEN_COUNT, openCount); +} + +void ChestTileEntity::setRemoved() +{ + clearCache(); + checkNeighbors(); + TileEntity::setRemoved(); +} + +// 4J Added +shared_ptr ChestTileEntity::clone() +{ + shared_ptr result = shared_ptr( new ChestTileEntity() ); + TileEntity::clone(result); + + for (unsigned int i = 0; i < items->length; i++) + { + if (items->data[i] != NULL) + { + result->items->data[i] = ItemInstance::clone(items->data[i]); + } + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/ChestTileEntity.h b/Minecraft.World/ChestTileEntity.h new file mode 100644 index 00000000..5eeb3c44 --- /dev/null +++ b/Minecraft.World/ChestTileEntity.h @@ -0,0 +1,63 @@ +#pragma once + +using namespace std; + +#include "TileEntity.h" +#include "Container.h" + +#include "ListTag.h" + +class Player; +class CompoundTag; + +class ChestTileEntity : public TileEntity, public Container +{ +public: + eINSTANCEOF GetType() { return eTYPE_CHESTTILEENTITY; } + static TileEntity *create() { return new ChestTileEntity(); } + +using TileEntity::setChanged; + +public: + ChestTileEntity(bool isBonusChest = false); // 4J added param + virtual ~ChestTileEntity(); + +private: + ItemInstanceArray *items; + +public: + bool isBonusChest; // 4J added + bool hasCheckedNeighbors; + weak_ptr n; + weak_ptr e; + weak_ptr w; + weak_ptr s; + + float openness, oOpenness; + int openCount; +private: + int tickInterval; + +public: + virtual unsigned int getContainerSize(); + virtual shared_ptr getItem(unsigned int slot); + virtual shared_ptr removeItem(unsigned int slot, int count); + virtual shared_ptr removeItemNoUpdate(int slot); + virtual void setItem(unsigned int slot, shared_ptr item); + virtual int getName(); + virtual void load(CompoundTag *base); + virtual void save(CompoundTag *base); + virtual int getMaxStackSize(); + virtual bool stillValid(shared_ptr player); + virtual void setChanged(); + virtual void clearCache(); + virtual void checkNeighbors(); + virtual void tick(); + virtual void triggerEvent(int b0, int b1); + virtual void startOpen(); + virtual void stopOpen(); + virtual void setRemoved(); + + // 4J Added + virtual shared_ptr clone(); +}; \ No newline at end of file diff --git a/Minecraft.World/Chicken.cpp b/Minecraft.World/Chicken.cpp new file mode 100644 index 00000000..0104b2bf --- /dev/null +++ b/Minecraft.World/Chicken.cpp @@ -0,0 +1,150 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "Chicken.h" +#include "..\Minecraft.Client\Textures.h" +#include "SoundTypes.h" +#include "MobCategory.h" + +void Chicken::_init() +{ + sheared = false; + flap = 0; + flapSpeed = 0; + flapping = 1; + oFlapSpeed = oFlap = 0.0f; + eggTime = 0; +} + +Chicken::Chicken(Level *level) : Animal( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + _init(); + this->textureIdx = TN_MOB_CHICKEN; // 4J - was L"/mob/chicken.png"; + this->setSize(0.3f, 0.7f); // 4J Changed from 0.4 to 0.7 in 1.8.2 + eggTime = random->nextInt(20 * 60 * 5) + 20 * 60 * 5; + + float walkSpeed = 0.25f; + goalSelector.addGoal(0, new FloatGoal(this)); + goalSelector.addGoal(1, new PanicGoal(this, 0.38f)); + goalSelector.addGoal(2, new BreedGoal(this, walkSpeed)); + goalSelector.addGoal(3, new TemptGoal(this, 0.25f, Item::seeds_wheat_Id, false)); + goalSelector.addGoal(4, new FollowParentGoal(this, 0.28f)); + goalSelector.addGoal(5, new RandomStrollGoal(this, walkSpeed)); + goalSelector.addGoal(6, new LookAtPlayerGoal(this, typeid(Player), 6)); + goalSelector.addGoal(7, new RandomLookAroundGoal(this)); +} + +bool Chicken::useNewAi() +{ + return true; +} + +int Chicken::getMaxHealth() +{ + return 4; +} + +void Chicken::aiStep() +{ + Animal::aiStep(); + + oFlap = flap; + oFlapSpeed = flapSpeed; + + flapSpeed += (onGround ? -1 : 4) * 0.3f; + if (flapSpeed < 0) flapSpeed = 0; + if (flapSpeed > 1) flapSpeed = 1; + + if (!onGround && flapping < 1) flapping = 1; + flapping *= 0.9; + + if (!onGround && yd < 0) + { + yd *= 0.6; + } + + flap += flapping * 2; + + if (!isBaby()) + { + if (!level->isClientSide && --eggTime <= 0) + { + level->playSound(shared_from_this(), eSoundType_MOB_CHICKENPLOP, 1.0f, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); + spawnAtLocation(Item::egg->id, 1); + eggTime = random->nextInt(20 * 60 * 5) + 20 * 60 * 5; + } + } + +} + +void Chicken::causeFallDamage(float distance) +{ +} + + +int Chicken::getAmbientSound() +{ + return eSoundType_MOB_CHICKEN_AMBIENT; +} + +int Chicken::getHurtSound() +{ + return eSoundType_MOB_CHICKEN_HURT; +} + +int Chicken::getDeathSound() +{ + return eSoundType_MOB_CHICKEN_HURT; +} + +int Chicken::getDeathLoot() +{ + return Item::feather->id; +} + +void Chicken::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + // drop some feathers + int count = random->nextInt(3) + random->nextInt(1 + playerBonusLevel); + for (int i = 0; i < count; i++) + { + spawnAtLocation(Item::feather_Id, 1); + } + // and some meat + if (this->isOnFire()) + { + spawnAtLocation(Item::chicken_cooked_Id, 1); + } + else + { + spawnAtLocation(Item::chicken_raw_Id, 1); + } +} + +shared_ptr Chicken::getBreedOffspring(shared_ptr target) +{ + // 4J - added limit to chickens that can be bred + if( level->canCreateMore( GetType(), Level::eSpawnType_Breed) ) + { + return shared_ptr(new Chicken(level)); + } + else + { + return nullptr; + } +} + +bool Chicken::isFood(shared_ptr itemInstance) +{ + return (itemInstance->id == Item::seeds_wheat_Id) || (itemInstance->id == Item::netherStalkSeeds_Id) || (itemInstance->id == Item::seeds_melon_Id) || (itemInstance->id == Item::seeds_pumpkin_Id); +} diff --git a/Minecraft.World/Chicken.h b/Minecraft.World/Chicken.h new file mode 100644 index 00000000..a162a5eb --- /dev/null +++ b/Minecraft.World/Chicken.h @@ -0,0 +1,42 @@ +#pragma once +using namespace std; + +#include "Animal.h" + +class Level; +class CompoundTag; + +class Chicken : public Animal +{ +public: + eINSTANCEOF GetType() { return eTYPE_CHICKEN; } + static Entity *create(Level *level) { return new Chicken(level); } + bool sheared; + float flap; + float flapSpeed; + float oFlapSpeed, oFlap; + float flapping; + int eggTime; + +private: + void _init(); + +public: + Chicken(Level *level); + virtual bool useNewAi(); + virtual int getMaxHealth(); + virtual void aiStep(); + +protected: + virtual void causeFallDamage(float distance); + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual int getDeathLoot(); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + +public: + virtual shared_ptr getBreedOffspring(shared_ptr target); + virtual bool isFood(shared_ptr itemInstance); + +}; diff --git a/Minecraft.World/ChunkPos.cpp b/Minecraft.World/ChunkPos.cpp new file mode 100644 index 00000000..3f9674a7 --- /dev/null +++ b/Minecraft.World/ChunkPos.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "ChunkPos.h" + +ChunkPos::ChunkPos(int x, int z) : x( x ), z( z ) +{ +} + +__int64 ChunkPos::hashCode(int x, int z) +{ + __int64 xx = x; + __int64 zz = z; + return (xx & 0xffffffffl) | ((zz & 0xffffffffl) << 32l); +} + +int ChunkPos::hashCode() +{ + __int64 hash = hashCode(x, z); + int h1 = (int) (hash); + int h2 = (int) (hash >> 32l); + return h1 ^ h2; +} + +double ChunkPos::distanceToSqr(shared_ptr e) +{ + double xPos = x * 16 + 8; + double zPos = z * 16 + 8; + + double xd = xPos - e->x; + double zd = zPos - e->z; + + return xd * xd + zd * zd; +} + +double ChunkPos::distanceToSqr(double px, double pz) +{ + double xPos = x * 16 + 8; + double zPos = z * 16 + 8; + + double xd = xPos - px; + double zd = zPos - pz; + + return xd * xd + zd * zd; +} + +int ChunkPos::getMiddleBlockX() +{ + return ( x << 4 ) + 8; +} + +int ChunkPos::getMiddleBlockZ() +{ + return ( z << 4 ) + 8; +} + +TilePos ChunkPos::getMiddleBlockPosition(int y) +{ + return TilePos(getMiddleBlockX(), y, getMiddleBlockZ()); +} + +wstring ChunkPos::toString() +{ + return L"[" + _toString(x) + L", " + _toString(z) + L"]"; +} + +__int64 ChunkPos::hash_fnct(const ChunkPos &k) +{ + return k.hashCode(k.x,k.z); +} + +bool ChunkPos::eq_test(const ChunkPos &x, const ChunkPos &y) +{ + return x.x == y.x && x.z == y.z; +} + diff --git a/Minecraft.World/ChunkPos.h b/Minecraft.World/ChunkPos.h new file mode 100644 index 00000000..f1d483c7 --- /dev/null +++ b/Minecraft.World/ChunkPos.h @@ -0,0 +1,40 @@ +#pragma once + +class Entity; +class TilePos; + +class ChunkPos +{ +public: + int x, z; // 4J - these were const but needed to make an assignment operator so we could make a vector of ChunkPos + + ChunkPos(int x, int z); + + static __int64 hashCode(int x, int z); + int hashCode(); + + double distanceToSqr(shared_ptr e); + double distanceToSqr(double px, double pz); // 4J added + + int getMiddleBlockX(); + int getMiddleBlockZ(); + + TilePos getMiddleBlockPosition(int y); + wstring toString(); + + static __int64 hash_fnct(const ChunkPos &k); + static bool eq_test(const ChunkPos &x, const ChunkPos &y); + bool operator == (const ChunkPos &k) const { return (this->x == k.x) && ( this->z == k.z); } + ChunkPos & operator= (const ChunkPos & other) { x = other.x; z = other.z; return *this; } +}; + +typedef struct +{ + __int64 operator() (const ChunkPos &k) const { return ChunkPos::hash_fnct(k); } + +} ChunkPosKeyHash; + +typedef struct +{ + bool operator() (const ChunkPos &x, const ChunkPos &y) const { return ChunkPos::eq_test(x, y); } +} ChunkPosKeyEq; \ No newline at end of file diff --git a/Minecraft.World/ChunkSource.h b/Minecraft.World/ChunkSource.h new file mode 100644 index 00000000..242f30a0 --- /dev/null +++ b/Minecraft.World/ChunkSource.h @@ -0,0 +1,67 @@ +#pragma once + +#include "Biome.h" +class ProgressListener; +class TilePos; + +// The maximum number of chunks that we can store +#ifdef _LARGE_WORLDS +// 4J Stu - Our default map (at zoom level 3) is 1024x1024 blocks (or 64 chunks) +#define LEVEL_MAX_WIDTH (5*64) //(6*54) +#else +#define LEVEL_MAX_WIDTH 54 +#endif +#define LEVEL_MIN_WIDTH 54 +#define LEVEL_LEGACY_WIDTH 54 + +// Scale was 8 in the Java game, but that would make our nether tiny +// Every 1 block you move in the nether maps to HELL_LEVEL_SCALE blocks in the overworld +#ifdef _LARGE_WORLDS +#define HELL_LEVEL_MAX_SCALE 8 +#else +#define HELL_LEVEL_MAX_SCALE 3 +#endif +#define HELL_LEVEL_MIN_SCALE 3 +#define HELL_LEVEL_LEGACY_SCALE 3 + +#define HELL_LEVEL_MAX_WIDTH (LEVEL_MAX_WIDTH / HELL_LEVEL_MAX_SCALE) +#define HELL_LEVEL_MIN_WIDTH 18 + +#define END_LEVEL_SCALE 3 +// 4J Stu - Fix the size of the end for all platforms +// 54 / 3 = 18 +#define END_LEVEL_MAX_WIDTH 18 +#define END_LEVEL_MIN_WIDTH 18 +//#define END_LEVEL_MAX_WIDTH (LEVEL_MAX_WIDTH / END_LEVEL_SCALE) + +class ChunkSource +{ +public: + // 4J Added so that we can store the maximum dimensions of this world + int m_XZSize; + +public: + virtual ~ChunkSource() {} + + virtual bool hasChunk(int x, int y) = 0; + virtual bool reallyHasChunk(int x, int y) { return hasChunk(x,y); } // 4J added + virtual LevelChunk *getChunk(int x, int z) = 0; + virtual void lightChunk(LevelChunk *lc) {} // 4J added + virtual LevelChunk *create(int x, int z) = 0; + virtual void postProcess(ChunkSource *parent, int x, int z) = 0; + virtual bool saveAllEntities() { return false; } // 4J Added + virtual bool save(bool force, ProgressListener *progressListener) = 0; + virtual bool tick() = 0; + virtual bool shouldSave() = 0; + + virtual LevelChunk **getCache() { return NULL; } // 4J added + virtual void dataReceived(int x, int z) {} // 4J added + + /** + * Returns some stats that are rendered when the user holds F3. + */ + virtual wstring gatherStats() = 0; + + virtual vector *getMobsAt(MobCategory *mobCategory, int x, int y, int z) = 0; + virtual TilePos *findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z) = 0; +}; diff --git a/Minecraft.World/ChunkStorage.h b/Minecraft.World/ChunkStorage.h new file mode 100644 index 00000000..8e4c9c0d --- /dev/null +++ b/Minecraft.World/ChunkStorage.h @@ -0,0 +1,15 @@ +#pragma once + +class Level; + +class ChunkStorage +{ +public: + virtual LevelChunk *load(Level *level, int x, int z) = 0; + virtual void save(Level *level, LevelChunk *levelChunk) = 0; + virtual void saveEntities(Level *level, LevelChunk *levelChunk) = 0; + virtual void tick() = 0; + virtual void flush() = 0; + virtual void WaitForAll() {}; // 4J Added + virtual void WaitIfTooManyQueuedChunks() {}; // 4J Added +}; diff --git a/Minecraft.World/ChunkStorageProfileDecorator.cpp b/Minecraft.World/ChunkStorageProfileDecorator.cpp new file mode 100644 index 00000000..76ffddbc --- /dev/null +++ b/Minecraft.World/ChunkStorageProfileDecorator.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "System.h" +#include "ChunkStorageProfileDecorator.h" + +ChunkStorageProfilerDecorator::ChunkStorageProfilerDecorator(ChunkStorage *capsulated) : + timeSpentLoading(0), loadCount(0), timeSpentSaving(0), saveCount(0), counter(0) +{ + this->capsulated = capsulated; +} + +LevelChunk *ChunkStorageProfilerDecorator::load(Level *level, int x, int z) +{ + __int64 nanoTime = System::nanoTime(); + LevelChunk *chunk = capsulated->load(level, x, z); + timeSpentLoading += System::nanoTime() - nanoTime; + loadCount++; + + return chunk; +} + +void ChunkStorageProfilerDecorator::save(Level *level, LevelChunk *levelChunk) +{ + __int64 nanoTime = System::nanoTime(); + capsulated->save(level, levelChunk); + timeSpentSaving += System::nanoTime() - nanoTime; + saveCount++; +} + +void ChunkStorageProfilerDecorator::saveEntities(Level *level, LevelChunk *levelChunk) +{ + capsulated->saveEntities(level, levelChunk); +} + +void ChunkStorageProfilerDecorator::tick() +{ + char buf[256]; + capsulated->tick(); + + counter++; + if (counter > 500) + { + if (loadCount > 0) + { +#ifndef _CONTENT_PACKAGE +#ifdef __PSVITA__ + sprintf(buf,"Average load time: %f (%lld)",0.000001 * (double) timeSpentLoading / (double) loadCount, loadCount); +#else + sprintf(buf,"Average load time: %f (%I64d)",0.000001 * (double) timeSpentLoading / (double) loadCount, loadCount); +#endif + app.DebugPrintf(buf); +#endif + } + if (saveCount > 0) + { +#ifndef _CONTENT_PACKAGE +#ifdef __PSVITA__ + sprintf(buf,"Average save time: %f (%lld)",0.000001 * (double) timeSpentSaving / (double) loadCount, loadCount); +#else + sprintf(buf,"Average save time: %f (%I64d)",0.000001 * (double) timeSpentSaving / (double) loadCount, loadCount); +#endif + app.DebugPrintf(buf); +#endif + } + counter = 0; + } +} + +void ChunkStorageProfilerDecorator::flush() +{ + capsulated->flush(); +} \ No newline at end of file diff --git a/Minecraft.World/ChunkStorageProfileDecorator.h b/Minecraft.World/ChunkStorageProfileDecorator.h new file mode 100644 index 00000000..67c582da --- /dev/null +++ b/Minecraft.World/ChunkStorageProfileDecorator.h @@ -0,0 +1,25 @@ +#pragma once +#include "ChunkStorage.h" + +class Level; + +class ChunkStorageProfilerDecorator : public ChunkStorage +{ +private: + ChunkStorage *capsulated; + + __int64 timeSpentLoading; + __int64 loadCount; + __int64 timeSpentSaving; + __int64 saveCount; + + int counter; + +public: + ChunkStorageProfilerDecorator(ChunkStorage *capsulated); + LevelChunk *load(Level *level, int x, int z); + void save(Level *level, LevelChunk *levelChunk); + void saveEntities(Level *level, LevelChunk *levelChunk); + void tick(); + void flush(); +}; diff --git a/Minecraft.World/ChunkTilesUpdatePacket.cpp b/Minecraft.World/ChunkTilesUpdatePacket.cpp new file mode 100644 index 00000000..d90e6b2f --- /dev/null +++ b/Minecraft.World/ChunkTilesUpdatePacket.cpp @@ -0,0 +1,173 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.chunk.h" +#include "PacketListener.h" +#include "ChunkTilesUpdatePacket.h" +#include "Dimension.h" + + + +ChunkTilesUpdatePacket::~ChunkTilesUpdatePacket() +{ + delete [] blocks.data; + delete [] data.data; + delete [] positions.data; +} + +ChunkTilesUpdatePacket::ChunkTilesUpdatePacket() +{ + shouldDelay = true; + xc = 0; + zc = 0; + count = 0; +} + +ChunkTilesUpdatePacket::ChunkTilesUpdatePacket(int xc, int zc, shortArray positions, byte count, Level *level) +{ + shouldDelay = true; + this->xc = xc; + this->zc = zc; + this->count = count; + this->positions = shortArray(count); + + this->blocks = byteArray(count); + this->data = byteArray(count); + LevelChunk *levelChunk = level->getChunk(xc, zc); + for (int i = 0; i < count; i++) + { + int x = (positions[i] >> 12) & 15; + int z = (positions[i] >> 8) & 15; + int y = (positions[i]) & 255; + + this->positions[i] = positions[i]; + blocks[i] = (byte) levelChunk->getTile(x, y, z); + data[i] = (byte) levelChunk->getData(x, y, z); + } + levelIdx = ( ( level->dimension->id == 0 ) ? 0 : ( (level->dimension->id == -1) ? 1 : 2 ) ); +} + +void ChunkTilesUpdatePacket::read(DataInputStream *dis) //throws IOException +{ + // 4J - changed format. See comments in write method. +#ifdef _LARGE_WORLDS + xc = dis->readShort(); + zc = dis->readShort(); + xc = ( xc << 16 ) >> 16; + zc = ( zc << 16 ) >> 16; +#else + xc = dis->read(); + zc = dis->read(); + xc = ( xc << 24 ) >> 24; + zc = ( zc << 24 ) >> 24; +#endif + + int countAndFlags = dis->readByte(); + bool dataAllZero = (( countAndFlags & 0x80 ) == 0x80 ); + levelIdx = ( countAndFlags >> 5 ) & 3; + count = countAndFlags & 0x1f; + + positions = shortArray(count); + blocks = byteArray(count); + data = byteArray(count); + + int currentBlockType = -1; + for( int i = 0; i < count; i++ ) + { + int xzAndFlag = dis->readShort(); + int y = dis->readByte(); + positions[i] = (xzAndFlag & 0xff00) | (y & 0xff); + if( ( xzAndFlag & 0x0080 ) == 0x0080 ) + { + currentBlockType = dis->read(); + } + blocks[i] = currentBlockType; + if( !dataAllZero) + { + data[i] = dis->read(); + } + else + { + data[i] = 0; + } + } +} + +void ChunkTilesUpdatePacket::write(DataOutputStream *dos) //throws IOException +{ + // 4J - changed format to reduce size of these packets. +#ifdef _LARGE_WORLDS + dos->writeShort(xc); + dos->writeShort(zc); +#else + dos->write(xc); + dos->write(zc); +#endif + // Determine if we've got any data elements that are non-zero - a large % of these packets set all data to zero, so we don't + // bother sending all those zeros in that case. + bool dataAllZero = true; + for( int i = 0; i < count; i++ ) + { + if( data[i] ) dataAllZero = false; + } + int countAndFlags = count; + if( dataAllZero ) countAndFlags |= 0x80; + countAndFlags |= ( levelIdx << 5 ); + dos->write(countAndFlags); + int lastBlockType = -1; + // Each block is represented by 15 bits of position, a flag to say whether the current block type is to change, and a possible data value. + // A large % of these packets set the same block type to a several positions, so no point resending the block type when not necessary. + for( int i = 0; i < count; i++ ) + { + int xzAndFlag = positions[i] &0xff00; + int y = positions[i] & 0xff; + int thisBlockType = blocks[i]; + if( thisBlockType != lastBlockType ) + { + xzAndFlag |= 0x0080; // Use top bit of y as a flag, we only need 7 bits for that + dos->writeShort(xzAndFlag); + dos->write(y); + dos->write(thisBlockType); + lastBlockType = thisBlockType; + } + else + { + dos->writeShort(xzAndFlag); + dos->write(y); + } + if( !dataAllZero ) + { + dos->write(data[i]); + } + } +} + +void ChunkTilesUpdatePacket::handle(PacketListener *listener) +{ + listener->handleChunkTilesUpdate(shared_from_this()); +} + +int ChunkTilesUpdatePacket::getEstimatedSize() +{ + bool dataAllZero = true; + int lastBlockType = -1; + int blockTypeChanges = 0; + for( int i = 0; i < count; i++ ) + { + if( data[i] ) dataAllZero = false; + int thisBlockType = blocks[i]; + if( thisBlockType != lastBlockType ) + { + blockTypeChanges++; + lastBlockType = thisBlockType; + } + } + int byteCount = 3 + 2 * count + blockTypeChanges; + if( !dataAllZero ) + { + byteCount += count; + } + + return byteCount; +} diff --git a/Minecraft.World/ChunkTilesUpdatePacket.h b/Minecraft.World/ChunkTilesUpdatePacket.h new file mode 100644 index 00000000..a97f49a3 --- /dev/null +++ b/Minecraft.World/ChunkTilesUpdatePacket.h @@ -0,0 +1,31 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class Level; + +class ChunkTilesUpdatePacket : public Packet, public enable_shared_from_this +{ +public: + int xc, zc; + shortArray positions; + byteArray blocks; + byteArray data; + byte count; // 4J Was int but never has a value higher than 10 + int levelIdx; + + ChunkTilesUpdatePacket(); + ~ChunkTilesUpdatePacket(); + ChunkTilesUpdatePacket(int xc, int zc, shortArray positions, byte count, Level *level); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ChunkTilesUpdatePacket()); } + virtual int getId() { return 52; } +}; + diff --git a/Minecraft.World/ChunkVisibilityAreaPacket.cpp b/Minecraft.World/ChunkVisibilityAreaPacket.cpp new file mode 100644 index 00000000..70f882f5 --- /dev/null +++ b/Minecraft.World/ChunkVisibilityAreaPacket.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.level.h" +#include "PacketListener.h" +#include "ChunkVisibilityAreaPacket.h" + + + +ChunkVisibilityAreaPacket::ChunkVisibilityAreaPacket() +{ + m_minX = 0; + m_maxX = 0; + m_minZ = 0; + m_maxZ = 0; +} + +ChunkVisibilityAreaPacket::ChunkVisibilityAreaPacket(int minX, int maxX, int minZ, int maxZ) +{ + m_minX = minX; + m_maxX = maxX; + m_minZ = minZ; + m_maxZ = maxZ; +} + +void ChunkVisibilityAreaPacket::read(DataInputStream *dis) //throws IOException +{ + m_minX = dis->readInt(); + m_maxX = dis->readInt(); + m_minZ = dis->readInt(); + m_maxZ = dis->readInt(); +} + +void ChunkVisibilityAreaPacket::write(DataOutputStream *dos) // throws IOException +{ + dos->writeInt(m_minX); + dos->writeInt(m_maxX); + dos->writeInt(m_minZ); + dos->writeInt(m_maxZ); +} + +void ChunkVisibilityAreaPacket::handle(PacketListener *listener) +{ + listener->handleChunkVisibilityArea(shared_from_this()); +} + +int ChunkVisibilityAreaPacket::getEstimatedSize() +{ + return 16; +} + diff --git a/Minecraft.World/ChunkVisibilityAreaPacket.h b/Minecraft.World/ChunkVisibilityAreaPacket.h new file mode 100644 index 00000000..33087497 --- /dev/null +++ b/Minecraft.World/ChunkVisibilityAreaPacket.h @@ -0,0 +1,30 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class Level; + +// 4J Added this packet so that when a player initially joins the game we just need to send them the area of chunks +// around them rather than one packet for each chunk +class ChunkVisibilityAreaPacket : public Packet, public enable_shared_from_this +{ +public: + int m_minX, m_maxX, m_minZ, m_maxZ; + +private: + //int size; + +public: + ChunkVisibilityAreaPacket(); + ChunkVisibilityAreaPacket(int minX, int maxX, int minZ, int maxZ); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ChunkVisibilityAreaPacket()); } + virtual int getId() { return 155; } +}; diff --git a/Minecraft.World/ChunkVisibilityPacket.cpp b/Minecraft.World/ChunkVisibilityPacket.cpp new file mode 100644 index 00000000..75a1916c --- /dev/null +++ b/Minecraft.World/ChunkVisibilityPacket.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "ChunkVisibilityPacket.h" + + + +ChunkVisibilityPacket::ChunkVisibilityPacket() +{ + this->shouldDelay = false; + x = 0; + z = 0; + visible = false; +} + +ChunkVisibilityPacket::ChunkVisibilityPacket(int x, int z, bool visible) +{ + this->shouldDelay = false; + this->x = x; + this->z = z; + this->visible = visible; +} + +void ChunkVisibilityPacket::read(DataInputStream *dis) //throws IOException +{ + x = dis->readInt(); + z = dis->readInt(); + visible = dis->read() != 0; +} + +void ChunkVisibilityPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(x); + dos->writeInt(z); + dos->write(visible ? 1 : 0); +} + +void ChunkVisibilityPacket::handle(PacketListener *listener) +{ + listener->handleChunkVisibility(shared_from_this()); +} + +int ChunkVisibilityPacket::getEstimatedSize() +{ + return 9; +} diff --git a/Minecraft.World/ChunkVisibilityPacket.h b/Minecraft.World/ChunkVisibilityPacket.h new file mode 100644 index 00000000..8c5856c7 --- /dev/null +++ b/Minecraft.World/ChunkVisibilityPacket.h @@ -0,0 +1,29 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +#include "stdafx.h" +#include +#include "PacketListener.h" + +class ChunkVisibilityPacket : public Packet, public enable_shared_from_this +{ +public: + int x, z; + bool visible; + + ChunkVisibilityPacket(); + ChunkVisibilityPacket(int x, int z, bool visible); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ChunkVisibilityPacket()); } + virtual int getId() { return 50; } +}; + + diff --git a/Minecraft.World/Class.cpp b/Minecraft.World/Class.cpp new file mode 100644 index 00000000..4f25f28c --- /dev/null +++ b/Minecraft.World/Class.cpp @@ -0,0 +1,5 @@ +#include "stdafx.h" +#include "Class.h" + +// 4J Stu - To ensure that other classes can get the _class object of it's superclass, we also need +// the BaseObject to have that member diff --git a/Minecraft.World/Class.h b/Minecraft.World/Class.h new file mode 100644 index 00000000..5dbbf965 --- /dev/null +++ b/Minecraft.World/Class.h @@ -0,0 +1,162 @@ +#pragma once +using namespace std; +class InputStream; + +// This file aims to provide functionality to mimic the subset of java's Class class that we require. Classes that require this +// functionality derive from BaseObject, and each contain a unique nested class definition Class. There are #defines here to +// simplify declaring classes with this added functionality. + + +// 4J Stu - This Enum can be used as a more lightweight version of the above, without having do dynamic casts +// 4J-PB - for replacement of instanceof +enum eINSTANCEOF +{ + eTYPE_NOTSET=0, + + // 4J-RR arranging these pathfinder types in a bitfield fashion so that a single and can determine whether they are derived from + // the 3 subclasses of pathfinders (water animals, animals, and monsters) that the mob spawner uses + eTYPE_WATERANIMAL = 0x100, + eTYPE_SQUID = 0x101, + + eTYPE_ANIMAL = 0x200, + + // 4J Stu - These have the ANIMAL, AGABLE_MOB and ANIMALS_SPAWN_LIMIT_CHECK bits set + eTYPE_COW = 0x82201, + eTYPE_SHEEP = 0x82202, + eTYPE_PIG = 0x82203, + eTYPE_SNOWMAN = 0x82204, + eTYPE_OZELOT = 0x82205, + + // 4J Stu - When adding new categories, please also update ConsoleSchematicFile::generateSchematicFile so these can be saved out to schematics + // 4J Stu- These have the ANIMAL and AGABLE_MOB bits set, but NOT ANIMALS_SPAWN_LIMIT_CHECK + eTYPE_CHICKEN = 0x2206, + eTYPE_WOLF = 0x2207, + eTYPE_MUSHROOMCOW = 0x2208, + + // 4J Stu - If you add new hostile mobs here you should also update the string lookup function at CConsoleMinecraftApp::getEntityName + eTYPE_MONSTER = 0x400, + eTYPE_ENEMY = 0x800, + eTYPE_CREEPER = 0xC01, + eTYPE_GIANT = 0xC02, + eTYPE_SKELETON = 0xC03, + eTYPE_SPIDER = 0xC04, + eTYPE_ZOMBIE = 0xC05, + eTYPE_PIGZOMBIE = 0xC06, + eTYPE_ENDERMAN = 0xC07, + eTYPE_SILVERFISH = 0xC08, + eTYPE_CAVESPIDER = 0xC09, + eTYPE_BLAZE = 0xC0A, + + eTYPE_GHAST = 0xC0B, // Now considering as a monster even though class inheritance doesn't work like this - but otherwise breaks mob spawning + eTYPE_SLIME = 0xC0C, // Now considering as a monster even though class inheritance doesn't work like this - but otherwise breaks mob spawning + eTYPE_LAVASLIME = 0xC0D, + + eTYPE_VILLAGERGOLEM = 0x1000, + + eTYPE_AGABLE_MOB = 0x2000, + + eTYPE_PLAYER = 0x8000, + eTYPE_SERVERPLAYER= 0x8001, + + // Include AGABLE_MOB + eTYPE_VILLAGER = 0x12000, + + eTYPE_PROJECTILE = 0x40000, + eTYPE_ARROW = 0x40001, + eTYPE_FIREBALL = 0x40002, + eTYPE_FISHINGHOOK = 0x40003, + eTYPE_SNOWBALL = 0x40004, + eTYPE_THROWNEGG = 0x40005, + eTYPE_EYEOFENDERSIGNAL = 0x40006, + eTYPE_SMALL_FIREBALL = 0x40007, + eTYPE_THROWNENDERPEARL = 0x40008, + eTYPE_THROWNPOTION = 0x40009, + eTYPE_THROWNEXPBOTTLE = 0x4000A, + + eTYPE_ANIMALS_SPAWN_LIMIT_CHECK = 0x80000, + + // Never used, exists to ensure all later entities don't match the bitmasks above + eTYPE_OTHERS = 0x100000, + + eTYPE_NETHER_SPHERE, + eTYPE_ENDER_CRYSTAL, + eTYPE_ENDERDRAGON, + eTYPE_BOSS_MOB_PART, + + eTYPE_ENTITY, + + eTYPE_MOB, + + eTYPE_LIGHTNINGBOLT, + + eTYPE_PAINTING, + eTYPE_ITEMENTITY, + eTYPE_FALLINGTILE, + eTYPE_BOAT, + eTYPE_MINECART, + eTYPE_PRIMEDTNT, + + eTYPE_TILEENTITY, + eTYPE_CHESTTILEENTITY, + eTYPE_DISPENSERTILEENTITY, + eTYPE_MOBSPAWNERTILEENTITY, + eTYPE_FURNACETILEENTITY, + eTYPE_SIGNTILEENTITY, + eTYPE_MUSICTILEENTITY, + eTYPE_RECORDPLAYERTILE, + eTYPE_PISTONPIECEENTITY, + eTYPE_BREWINGSTANDTILEENTITY, + eTYPE_ENCHANTMENTTABLEENTITY, + eTYPE_THEENDPORTALTILEENTITY, + eTYPE_SKULLTILEENTITY, + eTYPE_ENDERCHESTTILEENTITY, + + eType_NODE, + + eType_ITEM, + eType_ITEMINSTANCE, + eType_MAPITEM, + eType_TILE, + eType_FIRETILE, + + eType_BREAKINGITEMPARTICLE, + eType_BUBBLEPARTICLE, + eType_EXPLODEPARTICLE, + eType_FLAMEPARTICLE, + eType_FOOTSTEPPARTICLE, + eType_HEARTPARTICLE, + eType_LAVAPARTICLE, + eType_NOTEPARTICLE, + eType_NETHERPORTALPARTICLE, + eType_REDDUSTPARTICLE, + eType_SMOKEPARTICLE, + eType_SNOWSHOVELPARTICLE, + eType_SPLASHPARTICLE, + eType_TAKEANIMATIONPARTICLE, + eType_TERRAINPARTICLE, + eType_WATERDROPPARTICLE, + + // 1.8.2 + eTYPE_DELAYEDRELEASE, + eTYPE_EXPERIENCEORB, + eType_CRITPARTICLE, + eType_CRITPARTICLE2, + eType_HUGEEXPLOSIONPARTICLE, + eType_HUGEEXPLOSIONSEEDPARTICLE, + eType_PLAYERCLOUDPARTICLEPARTICLE, + eType_SUSPENDEDPARTICLE, + eType_SUSPENDEDTOWNPARTICLE, + + //1.0.1 + eTYPE_DRIPPARTICLE, + eTYPE_ENCHANTMENTTABLEPARTICLE, + eTYPE_SPELLPARTICLE, + + //TU9 + eTYPE_HANGING_ENTITY, + eTYPE_ITEM_FRAME, + eTYPE_DRAGONBREATHPARTICLE, + eTYPE_DRAGON_FIREBALL, + + eType_ENDERPARTICLE, +}; diff --git a/Minecraft.World/ClayFeature.cpp b/Minecraft.World/ClayFeature.cpp new file mode 100644 index 00000000..88fd4512 --- /dev/null +++ b/Minecraft.World/ClayFeature.cpp @@ -0,0 +1,38 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.material.h" +#include "ClayFeature.h" +#include "net.minecraft.world.level.tile.h" + +ClayFeature::ClayFeature(int radius) +{ + this->tile = Tile::clay_Id; + this->radius = radius; +} + +bool ClayFeature::place(Level *level, Random *random, int x, int y, int z) +{ + if (level->getMaterial(x, y, z) != Material::water) return false; + + int r = random->nextInt(radius - 2) + 2; + int yr = 1; + for (int xx = x - r; xx <= x + r; xx++) + { + for (int zz = z - r; zz <= z + r; zz++) + { + int xd = xx - x; + int zd = zz - z; + if (xd * xd + zd * zd > r * r) continue; + for (int yy = y - yr; yy <= y + yr; yy++) + { + int t = level->getTile(xx, yy, zz); + if (t == Tile::dirt_Id || t == Tile::clay_Id) + { + level->setTileNoUpdate(xx, yy, zz, tile); + } + } + } + } + + return true; +} diff --git a/Minecraft.World/ClayFeature.h b/Minecraft.World/ClayFeature.h new file mode 100644 index 00000000..067d759c --- /dev/null +++ b/Minecraft.World/ClayFeature.h @@ -0,0 +1,17 @@ +#pragma once +#include "Feature.h" +#include "Material.h" + +class Level; + +class ClayFeature : public Feature +{ +private: + int tile; + int radius; + +public: + ClayFeature (int radius); + + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/ClayTile.cpp b/Minecraft.World/ClayTile.cpp new file mode 100644 index 00000000..973b373b --- /dev/null +++ b/Minecraft.World/ClayTile.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "ClayTile.h" +#include "net.minecraft.world.item.h" + +ClayTile::ClayTile(int id) : Tile(id, Material::clay) +{ +} + +int ClayTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::clay->id; +} + +int ClayTile::getResourceCount(Random *random) +{ + return 4; +} diff --git a/Minecraft.World/ClayTile.h b/Minecraft.World/ClayTile.h new file mode 100644 index 00000000..a2d9422a --- /dev/null +++ b/Minecraft.World/ClayTile.h @@ -0,0 +1,13 @@ +#pragma once +#include "Tile.h" +#include "Material.h" + +class Random; + +class ClayTile : public Tile +{ +public: + ClayTile(int id); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int getResourceCount(Random *random); +}; \ No newline at end of file diff --git a/Minecraft.World/ClientCommandPacket.cpp b/Minecraft.World/ClientCommandPacket.cpp new file mode 100644 index 00000000..4245fc3f --- /dev/null +++ b/Minecraft.World/ClientCommandPacket.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" +#include "PacketListener.h" +#include "ClientCommandPacket.h" + +ClientCommandPacket::ClientCommandPacket() +{ + action = 0; +} + +ClientCommandPacket::ClientCommandPacket(int action) +{ + this->action = action; +} + +void ClientCommandPacket::read(DataInputStream *dis) +{ + action = dis->readByte(); +} + +void ClientCommandPacket::write(DataOutputStream *dos) +{ + dos->writeByte(action & 0xff); +} + +void ClientCommandPacket::handle(PacketListener *listener) +{ + listener->handleClientCommand(dynamic_pointer_cast(shared_from_this())); +} + +int ClientCommandPacket::getEstimatedSize() +{ + return 1; +} \ No newline at end of file diff --git a/Minecraft.World/ClientCommandPacket.h b/Minecraft.World/ClientCommandPacket.h new file mode 100644 index 00000000..2f614ddf --- /dev/null +++ b/Minecraft.World/ClientCommandPacket.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Packet.h" + +class ClientCommandPacket : public Packet, public enable_shared_from_this +{ +public: + static const int LOGIN_COMPLETE = 0; + static const int PERFORM_RESPAWN = 1; + + int action; + + ClientCommandPacket(); + ClientCommandPacket(int action); + + void read(DataInputStream *dis); + void write(DataOutputStream *dos); + void handle(PacketListener *listener); + int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ClientCommandPacket()); } + virtual int getId() { return 205; } +}; \ No newline at end of file diff --git a/Minecraft.World/ClientInformationPacket.h b/Minecraft.World/ClientInformationPacket.h new file mode 100644 index 00000000..e5e2cc42 --- /dev/null +++ b/Minecraft.World/ClientInformationPacket.h @@ -0,0 +1,94 @@ +#pragma once + +#include "stdafx.h" + +class ClientInformationPacket : public Packet +{ +#if 0 + private String language; + private int viewDistance; + private int chatVisibility; + private boolean chatColors; + private int difficulty; + + public ClientInformationPacket() { + } + + public ClientInformationPacket(String language, int viewDistance, int chatVisibility, boolean chatColors, int difficulty) { + this.language = language; + this.viewDistance = viewDistance; + this.chatVisibility = chatVisibility; + this.chatColors = chatColors; + this.difficulty = difficulty; + } + + @Override + public void read(DataInputStream dis) throws IOException { + language = readUtf(dis, 7); + viewDistance = dis.readByte(); + + int chat = dis.readByte(); + chatVisibility = chat & 0x7; + chatColors = (chat & 0x8) == 0x8; + + difficulty = dis.readByte(); + } + + @Override + public void write(DataOutputStream dos) throws IOException { + writeUtf(language, dos); + dos.writeByte(viewDistance); + dos.writeByte(chatVisibility | (chatColors ? 1 : 0) << 3); + dos.writeByte(difficulty); + } + + @Override + public void handle(PacketListener listener) { + listener.handleClientInformation(this); + } + + @Override + public int getEstimatedSize() { + return 0; + } + + public String getLanguage() { + return language; + } + + public int getViewDistance() { + return viewDistance; + } + + public int getChatVisibility() { + return chatVisibility; + } + + public boolean getChatColors() { + return chatColors; + } + + public int getDifficulty() { + return difficulty; + } + + public void setDifficulty(int difficulty) { + this.difficulty = difficulty; + } + + @Override + public String getDebugInfo() { + return String.format("lang='%s', view=%d, chat=%d, col=%b, difficulty=%d", language, viewDistance, chatVisibility, chatColors, difficulty); + } + + @Override + public boolean canBeInvalidated() { + return true; + } + + @Override + public boolean isInvalidatedBy(Packet packet) { + return true; + } +#endif +}; \ No newline at end of file diff --git a/Minecraft.World/ClientProtocolPacket.h b/Minecraft.World/ClientProtocolPacket.h new file mode 100644 index 00000000..9b708d16 --- /dev/null +++ b/Minecraft.World/ClientProtocolPacket.h @@ -0,0 +1,74 @@ +#pragma once + +#include "Packet.h" + +class ClientProtocolPacket : public Packet +{ +#if 0 + private int protocolVersion; + private String userName; + + // [EB]: Two fields below exist because we used to have a feature where we sent this + // information so people with dynamic proxies know where to connect us to. + private String hostName; + private int port; + + public ClientProtocolPacket() { + // Needed + } + + public ClientProtocolPacket(final int protocolVersion, final String userName, final String hostName, final int port) { + this.protocolVersion = protocolVersion; + this.userName = userName; + this.hostName = hostName; + this.port = port; + } + + @Override + public void read(DataInputStream dis) throws IOException { + protocolVersion = dis.readByte(); + userName = readUtf(dis, Player.MAX_NAME_LENGTH); + hostName = readUtf(dis, 255); + port = dis.readInt(); + } + + @Override + public void write(DataOutputStream dos) throws IOException { + dos.writeByte(protocolVersion); + writeUtf(userName, dos); + writeUtf(hostName, dos); + dos.writeInt(port); + } + + @Override + public void handle(PacketListener listener) { + listener.handleClientProtocolPacket(this); + } + + @Override + public int getEstimatedSize() { + return 1 + 2 + 2 * userName.length(); + } + + public int getProtocolVersion() { + return protocolVersion; + } + + public String getUserName() { + return userName; + } + + public String getHostName() { + return hostName; + } + + public int getPort() { + return port; + } + + @Override + public String getDebugInfo() { + return String.format("ver=%d, name='%s'", protocolVersion, userName); + } +#endif +}; \ No newline at end of file diff --git a/Minecraft.World/ClientSideMerchant.cpp b/Minecraft.World/ClientSideMerchant.cpp new file mode 100644 index 00000000..5d021c22 --- /dev/null +++ b/Minecraft.World/ClientSideMerchant.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.trading.h" +#include "net.minecraft.world.inventory.h" +#include "ClientSideMerchant.h" + +ClientSideMerchant::ClientSideMerchant(shared_ptr source, int name) +{ + this->source = source; + // 4J Stu - Need to do this after creating as a shared_ptr + container = NULL; //new MerchantContainer(source, this); + currentOffers = NULL; + m_name = name; +} + +ClientSideMerchant::~ClientSideMerchant() +{ + delete container; + delete currentOffers; +} + +void ClientSideMerchant::createContainer() +{ + container = new MerchantContainer(source, shared_from_this()); +} + +Container *ClientSideMerchant::getContainer() +{ + return container; +} + +shared_ptr ClientSideMerchant::getTradingPlayer() +{ + return source; +} + +void ClientSideMerchant::setTradingPlayer(shared_ptr player) +{ + +} + +MerchantRecipeList *ClientSideMerchant::getOffers(shared_ptr forPlayer) +{ + return currentOffers; +} + +void ClientSideMerchant::overrideOffers(MerchantRecipeList *recipeList) +{ + delete currentOffers; + currentOffers = recipeList; +} + +void ClientSideMerchant::notifyTrade(MerchantRecipe *activeRecipe) +{ + activeRecipe->increaseUses(); +} + +void ClientSideMerchant::notifyTradeUpdated(shared_ptr item) +{ +} + +int ClientSideMerchant::getDisplayName() +{ + return m_name; +} \ No newline at end of file diff --git a/Minecraft.World/ClientSideMerchant.h b/Minecraft.World/ClientSideMerchant.h new file mode 100644 index 00000000..33f0c0b1 --- /dev/null +++ b/Minecraft.World/ClientSideMerchant.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Merchant.h" + +class MerchantContainer; +class MerchantRecipeList; +class MerchantRecipe; + +class ClientSideMerchant : public Merchant, public enable_shared_from_this +{ +private: + MerchantContainer *container; + shared_ptr source; + MerchantRecipeList *currentOffers; + int m_name; + +public: + ClientSideMerchant(shared_ptr source, int name); + ~ClientSideMerchant(); + + void createContainer(); // 4J Added + Container *getContainer(); + shared_ptr getTradingPlayer(); + void setTradingPlayer(shared_ptr player); + MerchantRecipeList *getOffers(shared_ptr forPlayer); + void overrideOffers(MerchantRecipeList *recipeList); + void notifyTrade(MerchantRecipe *activeRecipe); + void notifyTradeUpdated(shared_ptr item); + int getDisplayName(); +}; \ No newline at end of file diff --git a/Minecraft.World/ClockItem.cpp b/Minecraft.World/ClockItem.cpp new file mode 100644 index 00000000..afe0544d --- /dev/null +++ b/Minecraft.World/ClockItem.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include "ClockItem.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\MultiPlayerLocalPlayer.h" +#include "net.minecraft.world.h" + +#ifdef __PSVITA__ +const wstring ClockItem::TEXTURE_PLAYER_ICON[XUSER_MAX_COUNT] = {L"clockP0"}; +#else +const wstring ClockItem::TEXTURE_PLAYER_ICON[XUSER_MAX_COUNT] = {L"clockP0",L"clockP1",L"clockP2",L"clockP3"}; +#endif + +ClockItem::ClockItem(int id) : Item(id) +{ + icons = NULL; +} + +// 4J Added so that we can override the icon id used to calculate the texture UV's for each player +Icon *ClockItem::getIcon(int auxValue) +{ + Icon *icon = Item::getIcon(auxValue); + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( pMinecraft->player != NULL && auxValue == 0 ) + { + icon = icons[pMinecraft->player->GetXboxPad()]; + } + return icon; +} + +void ClockItem::registerIcons(IconRegister *iconRegister) +{ + Item::registerIcons(iconRegister); + + icons = new Icon *[XUSER_MAX_COUNT]; + + for (int i = 0; i < XUSER_MAX_COUNT; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURE_PLAYER_ICON[i]); + } +} diff --git a/Minecraft.World/ClockItem.h b/Minecraft.World/ClockItem.h new file mode 100644 index 00000000..3ee4b5a4 --- /dev/null +++ b/Minecraft.World/ClockItem.h @@ -0,0 +1,19 @@ +#pragma once +// 4J Added so that we can override the icon id used to calculate the texture UV's for each player + +#include "Item.h" + +class ClockItem : public Item +{ +private: + Icon **icons; + static const wstring TEXTURE_PLAYER_ICON[XUSER_MAX_COUNT]; + +public: + ClockItem(int id); + + virtual Icon *getIcon(int auxValue); + + //@Override + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/ClothDyeRecipes.cpp b/Minecraft.World/ClothDyeRecipes.cpp new file mode 100644 index 00000000..5ad38d82 --- /dev/null +++ b/Minecraft.World/ClothDyeRecipes.cpp @@ -0,0 +1,107 @@ +#include "stdafx.h" +#include "net.minecraft.world.Item.h" +#include "DyePowderItem.h" +#include "Tile.h" +#include "ClothTile.h" +#include "Recipy.h" +#include "Recipes.h" +#include "ClothDyeRecipes.h" + +void ClothDyeRecipes::addRecipes(Recipes *r) +{ + // recipes for converting cloth to colored cloth using dye + for (int i = 0; i < 16; i++) + { + r->addShapelessRecipy(new ItemInstance(Tile::cloth, 1, ClothTile::getItemAuxValueForTileData(i)), // + L"zzg", + new ItemInstance(Item::dye_powder, 1, i), new ItemInstance(Item::items[Tile::cloth_Id], 1, 0),L'D'); + } + + // some dye recipes + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::YELLOW), + L"tg", + Tile::flower,L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::RED), + L"tg", + Tile::rose,L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 3, DyePowderItem::WHITE), + L"ig", + Item::bone,L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::PINK), // + L"zzg", + new ItemInstance(Item::dye_powder, 1, DyePowderItem::RED), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::WHITE),L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::ORANGE), // + L"zzg", + new ItemInstance(Item::dye_powder, 1, DyePowderItem::RED), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::YELLOW),L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::LIME), // + L"zzg", + new ItemInstance(Item::dye_powder, 1, DyePowderItem::GREEN), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::WHITE),L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::GRAY), // + L"zzg", + new ItemInstance(Item::dye_powder, 1, DyePowderItem::BLACK), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::WHITE),L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::SILVER), // + L"zzg", + new ItemInstance(Item::dye_powder, 1, DyePowderItem::GRAY), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::WHITE),L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 3, DyePowderItem::SILVER), // + L"zzzg", + new ItemInstance(Item::dye_powder, 1, DyePowderItem::BLACK), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::WHITE), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::WHITE),L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::LIGHT_BLUE), // + L"zzg", + new ItemInstance(Item::dye_powder, 1, DyePowderItem::BLUE), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::WHITE),L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::CYAN), // + L"zzg", + new ItemInstance(Item::dye_powder, 1, DyePowderItem::BLUE), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::GREEN),L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::PURPLE), // + L"zzg", + new ItemInstance(Item::dye_powder, 1, DyePowderItem::BLUE), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::RED),L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 2, DyePowderItem::MAGENTA), // + L"zzg", + new ItemInstance(Item::dye_powder, 1, DyePowderItem::PURPLE), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::PINK),L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 3, DyePowderItem::MAGENTA), // + L"zzzg", + new ItemInstance(Item::dye_powder, 1, DyePowderItem::BLUE), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::RED), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::PINK),L'D'); + + r->addShapelessRecipy(new ItemInstance(Item::dye_powder, 4, DyePowderItem::MAGENTA), // + L"zzzzg", + new ItemInstance(Item::dye_powder, 1, DyePowderItem::BLUE), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::RED), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::RED), + new ItemInstance(Item::dye_powder, 1, DyePowderItem::WHITE),L'D'); + + for (int i = 0; i < 16; i++) + { + r->addShapedRecipy(new ItemInstance(Tile::woolCarpet, 3, i), + L"sczg", + L"##", + L'#', new ItemInstance(Tile::cloth, 1, i), + L'D' + ); + } +} + diff --git a/Minecraft.World/ClothDyeRecipes.h b/Minecraft.World/ClothDyeRecipes.h new file mode 100644 index 00000000..c4283dfd --- /dev/null +++ b/Minecraft.World/ClothDyeRecipes.h @@ -0,0 +1,8 @@ +#pragma once + +class ClothDyeRecipes +{ + +public: + void addRecipes(Recipes *r); +}; diff --git a/Minecraft.World/ClothTile.cpp b/Minecraft.World/ClothTile.cpp new file mode 100644 index 00000000..70ba4837 --- /dev/null +++ b/Minecraft.World/ClothTile.cpp @@ -0,0 +1,38 @@ +#include "stdafx.h" +#include "net.minecraft.world.h" +#include "ClothTile.h" + +ClothTile::ClothTile() : Tile(35, Material::cloth) +{ + icons = NULL; +} + +Icon *ClothTile::getTexture(int face, int data) +{ + return icons[data]; +} + +int ClothTile::getSpawnResourcesAuxValue(int data) +{ + return data; +} + +int ClothTile::getTileDataForItemAuxValue(int auxValue) +{ + return (~auxValue & 0xf); +} + +int ClothTile::getItemAuxValueForTileData(int data) +{ + return (~data & 0xf); +} + +void ClothTile::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[16]; + + for (int i = 0; i < 16; i++) + { + icons[i] = iconRegister->registerIcon(L"cloth_" + _toString(i) ); + } +} \ No newline at end of file diff --git a/Minecraft.World/ClothTile.h b/Minecraft.World/ClothTile.h new file mode 100644 index 00000000..72a74a27 --- /dev/null +++ b/Minecraft.World/ClothTile.h @@ -0,0 +1,22 @@ +#pragma once +#include "Tile.h" +#include "Material.h" + +class ChunkRebuildData; + +class ClothTile : public Tile +{ + friend class ChunkRebuildData; +private: + Icon **icons; +public: + ClothTile(); + virtual Icon *getTexture(int face, int data); +protected: + virtual int getSpawnResourcesAuxValue(int data); +public: + static int getTileDataForItemAuxValue(int auxValue); + static int getItemAuxValueForTileData(int data); + //@Override + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/ClothTileItem.cpp b/Minecraft.World/ClothTileItem.cpp new file mode 100644 index 00000000..35f3bb96 --- /dev/null +++ b/Minecraft.World/ClothTileItem.cpp @@ -0,0 +1,68 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.tile.h" +#include "ItemInstance.h" +#include "DyePowderItem.h" +#include "ClothTileItem.h" + +const unsigned int ClothTileItem::COLOR_DESCS[] = +{ + IDS_TILE_CLOTH_BLACK, + IDS_TILE_CLOTH_RED, + IDS_TILE_CLOTH_GREEN, + IDS_TILE_CLOTH_BROWN, + IDS_TILE_CLOTH_BLUE, + IDS_TILE_CLOTH_PURPLE, + IDS_TILE_CLOTH_CYAN, + IDS_TILE_CLOTH_SILVER, + IDS_TILE_CLOTH_GRAY, + IDS_TILE_CLOTH_PINK, + IDS_TILE_CLOTH_LIME, + IDS_TILE_CLOTH_YELLOW, + IDS_TILE_CLOTH_LIGHT_BLUE, + IDS_TILE_CLOTH_MAGENTA, + IDS_TILE_CLOTH_ORANGE, + IDS_TILE_CLOTH_WHITE +}; + +const unsigned int ClothTileItem::CARPET_COLOR_DESCS[] = +{ + IDS_TILE_CARPET_BLACK, + IDS_TILE_CARPET_RED, + IDS_TILE_CARPET_GREEN, + IDS_TILE_CARPET_BROWN, + IDS_TILE_CARPET_BLUE, + IDS_TILE_CARPET_PURPLE, + IDS_TILE_CARPET_CYAN, + IDS_TILE_CARPET_SILVER, + IDS_TILE_CARPET_GRAY, + IDS_TILE_CARPET_PINK, + IDS_TILE_CARPET_LIME, + IDS_TILE_CARPET_YELLOW, + IDS_TILE_CARPET_LIGHT_BLUE, + IDS_TILE_CARPET_MAGENTA, + IDS_TILE_CARPET_ORANGE, + IDS_TILE_CARPET_WHITE +}; + +ClothTileItem::ClothTileItem(int id) : TileItem(id) +{ + setMaxDamage(0); + setStackedByData(true); +} + +Icon *ClothTileItem::getIcon(int itemAuxValue) +{ + return Tile::cloth->getTexture(2, ClothTile::getTileDataForItemAuxValue(itemAuxValue)); + +} + +int ClothTileItem::getLevelDataForAuxValue(int auxValue) +{ + return auxValue; +} + +unsigned int ClothTileItem::getDescriptionId(shared_ptr instance) +{ + if(getTileId() == Tile::woolCarpet_Id) return CARPET_COLOR_DESCS[ClothTile::getTileDataForItemAuxValue(instance->getAuxValue())]; + else return COLOR_DESCS[ClothTile::getTileDataForItemAuxValue(instance->getAuxValue())]; +} diff --git a/Minecraft.World/ClothTileItem.h b/Minecraft.World/ClothTileItem.h new file mode 100644 index 00000000..e909df43 --- /dev/null +++ b/Minecraft.World/ClothTileItem.h @@ -0,0 +1,17 @@ +#pragma once +using namespace std; + +#include "TileItem.h" + +class ClothTileItem : public TileItem +{ +public: + static const unsigned int COLOR_DESCS[]; + static const unsigned int CARPET_COLOR_DESCS[]; + + ClothTileItem(int id); + + virtual Icon *getIcon(int itemAuxValue); + virtual int getLevelDataForAuxValue(int auxValue); + virtual unsigned int getDescriptionId(shared_ptr instance); +}; \ No newline at end of file diff --git a/Minecraft.World/CoalItem.cpp b/Minecraft.World/CoalItem.cpp new file mode 100644 index 00000000..362689e0 --- /dev/null +++ b/Minecraft.World/CoalItem.cpp @@ -0,0 +1,22 @@ +using namespace std; + +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "CoalItem.h" + +CoalItem::CoalItem(int id) : Item( id ) +{ + setStackedByData(true); + setMaxDamage(0); +} + +unsigned int CoalItem::getDescriptionId(shared_ptr instance) +{ + if (instance->getAuxValue() == CHAR_COAL) + { + return IDS_ITEM_CHARCOAL; + } + return IDS_ITEM_COAL; +} diff --git a/Minecraft.World/CoalItem.h b/Minecraft.World/CoalItem.h new file mode 100644 index 00000000..24814548 --- /dev/null +++ b/Minecraft.World/CoalItem.h @@ -0,0 +1,17 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class ItemInstance; + +class CoalItem : public Item +{ +public: + static const int STONE_COAL = 0; + static const int CHAR_COAL = 1; + + CoalItem(int id); + + virtual unsigned int getDescriptionId(shared_ptr instance); +}; \ No newline at end of file diff --git a/Minecraft.World/CocoaTile.cpp b/Minecraft.World/CocoaTile.cpp new file mode 100644 index 00000000..1f575014 --- /dev/null +++ b/Minecraft.World/CocoaTile.cpp @@ -0,0 +1,176 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.h" +#include "net.minecraft.h" +#include "CocoaTile.h" + +const wstring CocoaTile::TEXTURE_AGES[] = { L"cocoa_0", L"cocoa_1", L"cocoa_2"}; + +CocoaTile::CocoaTile(int id) : DirectionalTile(id, Material::plant, isSolidRender() ) +{ + setTicking(true); +} + +Icon *CocoaTile::getTexture(int face, int data) +{ + return icons[2]; +} + +Icon *CocoaTile::getTextureForAge(int age) +{ + if (age < 0 || age >= COCOA_TEXTURES_LENGTH) + { + age = COCOA_TEXTURES_LENGTH - 1; + } + return icons[age]; +} + +void CocoaTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (!canSurvive(level, x, y, z)) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } + else if (level->random->nextInt(5) == 0) + { + int data = level->getData(x, y, z); + int age = getAge(data); + if (age < 2) + { + age++; + level->setData(x, y, z, (age << 2) | (getDirection(data))); + } + } +} + +bool CocoaTile::canSurvive(Level *level, int x, int y, int z) +{ + int dir = getDirection(level->getData(x, y, z)); + + x += Direction::STEP_X[dir]; + z += Direction::STEP_Z[dir]; + int attachedTo = level->getTile(x, y, z); + + return attachedTo == Tile::treeTrunk_Id && TreeTile::getWoodType(level->getData(x, y, z)) == TreeTile::JUNGLE_TRUNK; +} + +int CocoaTile::getRenderShape() +{ + return SHAPE_COCOA; +} + +bool CocoaTile::isCubeShaped() +{ + return false; +} + +bool CocoaTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +AABB *CocoaTile::getAABB(Level *level, int x, int y, int z) +{ + updateShape(level, x, y, z); + return DirectionalTile::getAABB(level, x, y, z); +} + +AABB *CocoaTile::getTileAABB(Level *level, int x, int y, int z) +{ + updateShape(level, x, y, z); + return DirectionalTile::getTileAABB(level, x, y, z); +} + +void CocoaTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) +{ + int data = level->getData(x, y, z); + int dir = getDirection(data); + int age = getAge(data); + + int width = 4 + age * 2; + int height = 5 + age * 2; + + float hWidth = width / 2.0f; + + switch (dir) + { + case Direction::SOUTH: + setShape((8.0f - hWidth) / 16.0f, (12.0f - height) / 16.0f, (15.0f - width) / 16.0f, (8.0f + hWidth) / 16.0f, (12.0f) / 16.0f, (15.0f) / 16.0f); + break; + case Direction::NORTH: + setShape((8.0f - hWidth) / 16.0f, (12.0f - height) / 16.0f, (1.0f) / 16.0f, (8.0f + hWidth) / 16.0f, (12.0f) / 16.0f, (1.0f + width) / 16.0f); + break; + case Direction::WEST: + setShape((1.0f) / 16.0f, (12.0f - height) / 16.0f, (8.0f - hWidth) / 16.0f, (1.0f + width) / 16.0f, (12.0f) / 16.0f, (8.0f + hWidth) / 16.0f); + break; + case Direction::EAST: + setShape((15.0f - width) / 16.0f, (12.0f - height) / 16.0f, (8.0f - hWidth) / 16.0f, (15.0f) / 16.0f, (12.0f) / 16.0f, (8.0f + hWidth) / 16.0f); + break; + } +} + +void CocoaTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by, shared_ptr itemInstance) +{ + int dir = (((Mth::floor(by->yRot * 4 / (360) + 0.5)) & 3) + 0) % 4; + level->setData(x, y, z, dir); +} + +int CocoaTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) +{ + if (face == Facing::UP || face == Facing::DOWN) + { + face = Facing::NORTH; + } + return Direction::DIRECTION_OPPOSITE[Direction::FACING_DIRECTION[face]]; +} + +void CocoaTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (!canSurvive(level, x, y, z)) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } +} + +int CocoaTile::getAge(int data) +{ + return (data & DirectionalTile::DIRECTION_INV_MASK) >> 2; +} + +void CocoaTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel) +{ + int age = getAge(data); + int count = 1; + if (age >= 2) + { + count = 3; + } + for (int i = 0; i < count; i++) + { + popResource(level, x, y, z, shared_ptr( new ItemInstance(Item::dye_powder, 1, DyePowderItem::BROWN) )); + } +} + +int CocoaTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Item::dye_powder_Id; +} + +int CocoaTile::cloneTileData(Level *level, int x, int y, int z) +{ + return DyePowderItem::BROWN; +} + +void CocoaTile::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[COCOA_TEXTURES_LENGTH]; + + for (int i = 0; i < COCOA_TEXTURES_LENGTH; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURE_AGES[i]); + } +} \ No newline at end of file diff --git a/Minecraft.World/CocoaTile.h b/Minecraft.World/CocoaTile.h new file mode 100644 index 00000000..267225ea --- /dev/null +++ b/Minecraft.World/CocoaTile.h @@ -0,0 +1,38 @@ +#pragma once + +#include "DirectionalTile.h" + +class CocoaTile : public DirectionalTile +{ +public: + static const int COCOA_TEXTURES_LENGTH = 3; + static const wstring TEXTURE_AGES[]; + +private: + Icon **icons; + +public: + using Tile::setPlacedBy; + + CocoaTile(int id); + + virtual Icon *getTexture(int face, int data); + virtual Icon *getTextureForAge(int age); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual bool canSurvive(Level *level, int x, int y, int z); + virtual int getRenderShape(); + virtual bool isCubeShaped(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual AABB *getTileAABB(Level *level, int x, int y, int z); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); + virtual void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by, shared_ptr itemInstance); + virtual int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + static int getAge(int data); + using DirectionalTile::spawnResources; + virtual void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel); + virtual int cloneTileId(Level *level, int x, int y, int z); + virtual int cloneTileData(Level *level, int x, int y, int z); + virtual void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/Color.cpp b/Minecraft.World/Color.cpp new file mode 100644 index 00000000..256e9017 --- /dev/null +++ b/Minecraft.World/Color.cpp @@ -0,0 +1,97 @@ +#include "stdafx.h" + +#include "Color.h" + +//Creates an opaque sRGB color with the specified red, green, and blue values in the range (0.0 - 1.0). +//Alpha is defaulted to 1.0. The actual color used in rendering depends on finding the best match given the color space +//available for a particular output device. +//Parameters: +//r - the red component +//g - the green component +//b - the blue component +//Throws: +//IllegalArgumentException - if r, g or b are outside of the range 0.0 to 1.0, inclusive +Color::Color( float r, float g, float b) +{ + assert( r >= 0.0f && r <= 1.0f ); + assert( g >= 0.0f && g <= 1.0f ); + assert( b >= 0.0f && b <= 1.0f ); + + //argb + colour = ( (0xFF<<24) | ( (int)(r*255)<<16 ) | ( (int)(g*255)<<8 ) | ( (int)(b*255) ) ); +} + +Color::Color( int r, int g, int b) +{ + colour = ( (0xFF<<24) | ( (r&0xff)<<16 ) | ( (g&0xff)<<8 ) | ( (b&0xff) ) ); +} + + +//Creates a Color object based on the specified values for the HSB color model. +//The s and b components should be floating-point values between zero and one (numbers in the range 0.0-1.0). +//The h component can be any floating-point number. The floor of this number is subtracted from it to create a fraction between 0 and 1. +//This fractional number is then multiplied by 360 to produce the hue angle in the HSB color model. +// +//Parameters: +//h - the hue component +//s - the saturation of the color +//b - the brightness of the color +//Returns: +//a Color object with the specified hue, saturation, and brightness. +Color Color::getHSBColor(float hue, float saturation, float brightness) +{ + int r = 0, g = 0, b = 0; + if (saturation == 0) + { + r = g = b = (int) (brightness * 255.0f + 0.5f); + } + else + { + float h = (hue - (float)floor(hue)) * 6.0f; + float f = h - (float)floor(h); + float p = brightness * (1.0f - saturation); + float q = brightness * (1.0f - saturation * f); + float t = brightness * (1.0f - (saturation * (1.0f - f))); + switch ((int) h) + { + case 0: + r = (int) (brightness * 255.0f + 0.5f); + g = (int) (t * 255.0f + 0.5f); + b = (int) (p * 255.0f + 0.5f); + break; + case 1: + r = (int) (q * 255.0f + 0.5f); + g = (int) (brightness * 255.0f + 0.5f); + b = (int) (p * 255.0f + 0.5f); + break; + case 2: + r = (int) (p * 255.0f + 0.5f); + g = (int) (brightness * 255.0f + 0.5f); + b = (int) (t * 255.0f + 0.5f); + break; + case 3: + r = (int) (p * 255.0f + 0.5f); + g = (int) (q * 255.0f + 0.5f); + b = (int) (brightness * 255.0f + 0.5f); + break; + case 4: + r = (int) (t * 255.0f + 0.5f); + g = (int) (p * 255.0f + 0.5f); + b = (int) (brightness * 255.0f + 0.5f); + break; + case 5: + r = (int) (brightness * 255.0f + 0.5f); + g = (int) (p * 255.0f + 0.5f); + b = (int) (q * 255.0f + 0.5f); + break; + } + } + + + return Color( r, g, b ); +} + +int Color::getRGB() +{ + return colour; +} \ No newline at end of file diff --git a/Minecraft.World/Color.h b/Minecraft.World/Color.h new file mode 100644 index 00000000..a9d31eca --- /dev/null +++ b/Minecraft.World/Color.h @@ -0,0 +1,15 @@ +#pragma once + +class Color +{ +private: + int colour; + +public: + //Creates an opaque sRGB color with the specified red, green, and blue values in the range (0.0 - 1.0). + Color( float r, float g, float b); + Color( int r, int g, int b); + + static Color getHSBColor(float h, float s, float b); + int getRGB(); +}; \ No newline at end of file diff --git a/Minecraft.World/ColoredTileItem.cpp b/Minecraft.World/ColoredTileItem.cpp new file mode 100644 index 00000000..37ea4c63 --- /dev/null +++ b/Minecraft.World/ColoredTileItem.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.tile.h" +#include "ColoredTileItem.h" + +ColoredTileItem::ColoredTileItem(int id, bool stackedByData) : TileItem(id) +{ + this->colorTile = Tile::tiles[getTileId()]; + + if (stackedByData) + { + setMaxDamage(0); + setStackedByData(true); + } +} + +ColoredTileItem::~ColoredTileItem() +{ + if(descriptionPostfixes.data != NULL) delete [] descriptionPostfixes.data; +} + +int ColoredTileItem::getColor(shared_ptr item, int spriteLayer) +{ + return colorTile->getColor(item->getAuxValue()); +} + +Icon *ColoredTileItem::getIcon(int auxValue) +{ + return colorTile->getTexture(0, auxValue); +} + +int ColoredTileItem::getLevelDataForAuxValue(int auxValue) +{ + return auxValue; +} + +ColoredTileItem *ColoredTileItem::setDescriptionPostfixes(intArray descriptionPostfixes) +{ + if(this->descriptionPostfixes.data != NULL) delete this->descriptionPostfixes.data; + this->descriptionPostfixes = intArray(descriptionPostfixes.length); + for(unsigned int i = 0; i < descriptionPostfixes.length; ++i ) + { + this->descriptionPostfixes[i] = descriptionPostfixes[i]; + } + + return this; +} + +unsigned int ColoredTileItem::getDescriptionId(shared_ptr instance) +{ + if (descriptionPostfixes.data == NULL) + { + return TileItem::getDescriptionId(instance); + } + int id = instance->getAuxValue(); + if (id >= 0 && id < descriptionPostfixes.length) + { + return descriptionPostfixes[id]; //TileItem::getDescriptionId(instance) + "." + descriptionPostfixes[id]; + } + return TileItem::getDescriptionId(instance); +} \ No newline at end of file diff --git a/Minecraft.World/ColoredTileItem.h b/Minecraft.World/ColoredTileItem.h new file mode 100644 index 00000000..cc3ef1e7 --- /dev/null +++ b/Minecraft.World/ColoredTileItem.h @@ -0,0 +1,25 @@ +#pragma once + +#include "TileItem.h" + +class ItemInstance; + +class ColoredTileItem : public TileItem +{ +private: + // Was const, but removing that so we don't have to make all the functions const as well! + Tile *colorTile; + intArray descriptionPostfixes; + +public: + using TileItem::getColor; + ColoredTileItem(int id, bool stackedByData); + ~ColoredTileItem(); + + virtual int getColor(shared_ptr item, int spriteLayer); + virtual Icon *getIcon(int auxValue); + virtual int getLevelDataForAuxValue(int auxValue); + + ColoredTileItem *setDescriptionPostfixes(intArray descriptionPostfixes); + virtual unsigned int getDescriptionId(shared_ptr instance); +}; diff --git a/Minecraft.World/Command.cpp b/Minecraft.World/Command.cpp new file mode 100644 index 00000000..f6d0e592 --- /dev/null +++ b/Minecraft.World/Command.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include "net.minecraft.commands.h" +#include "..\Minecraft.Client\MinecraftServer.h" +#include "..\Minecraft.Client\PlayerList.h" +#include "..\Minecraft.Client\ServerPlayer.h" +#include "Command.h" + +AdminLogCommand *Command::logger; + +bool Command::canExecute(shared_ptr source) +{ + return source->hasPermission(getId()); +} + +void Command::logAdminAction(shared_ptr source, ChatPacket::EChatPacketMessage messageType, const wstring& message, int customData, const wstring& additionalMessage) +{ + logAdminAction(source, 0, messageType, message, customData, additionalMessage); +} + +void Command::logAdminAction(shared_ptr source, int type, ChatPacket::EChatPacketMessage messageType, const wstring& message, int customData, const wstring& additionalMessage) +{ + if (logger != NULL) + { + logger->logAdminCommand(source, type, messageType, message, customData, additionalMessage); + } +} + +void Command::setLogger(AdminLogCommand *logger) +{ + Command::logger = logger; +} + +shared_ptr Command::getPlayer(PlayerUID playerId) +{ + shared_ptr player = MinecraftServer::getInstance()->getPlayers()->getPlayer(playerId); + + if (player == NULL) + { + return nullptr; + } + else + { + return player; + } +} \ No newline at end of file diff --git a/Minecraft.World/Command.h b/Minecraft.World/Command.h new file mode 100644 index 00000000..815c24ba --- /dev/null +++ b/Minecraft.World/Command.h @@ -0,0 +1,28 @@ +#pragma once + +// 4J Stu - Based loosely on the Java versions + +#include "CommandsEnum.h" +#include "ChatPacket.h" + +class AdminLogCommand; +class CommandSender; +class ServerPlayer; + +class Command +{ +private: + static AdminLogCommand *logger; + +public: + virtual EGameCommand getId() = 0; + virtual void execute(shared_ptr source, byteArray commandData) = 0; + virtual bool canExecute(shared_ptr source); + + static void logAdminAction(shared_ptr source, ChatPacket::EChatPacketMessage messageType, const wstring& message = L"", int customData = -1, const wstring& additionalMessage = L""); + static void logAdminAction(shared_ptr source, int type, ChatPacket::EChatPacketMessage messageType, const wstring& message = L"", int customData = -1, const wstring& additionalMessage = L""); + static void setLogger(AdminLogCommand *logger); + +protected: + shared_ptr getPlayer(PlayerUID playerId); +}; \ No newline at end of file diff --git a/Minecraft.World/CommandDispatcher.cpp b/Minecraft.World/CommandDispatcher.cpp new file mode 100644 index 00000000..68b74ca9 --- /dev/null +++ b/Minecraft.World/CommandDispatcher.cpp @@ -0,0 +1,34 @@ +#include "stdafx.h" +#include "net.minecraft.commands.h" +#include "CommandDispatcher.h" + +void CommandDispatcher::performCommand(shared_ptr sender, EGameCommand command, byteArray commandData) +{ + AUTO_VAR(it, commandsById.find(command)); + + if(it != commandsById.end()) + { + Command *command = it->second; + if (command->canExecute(sender)) + { + command->execute(sender, commandData); + } + else + { +#ifndef _CONTENT_PACKAGE + sender->sendMessage(L"\u00A7cYou do not have permission to use this command."); +#endif + } + } + else + { + app.DebugPrintf("Command %d not found!\n", command); + } +} + +Command *CommandDispatcher::addCommand(Command *command) +{ + commandsById[command->getId()] = command; + commands.insert(command); + return command; +} \ No newline at end of file diff --git a/Minecraft.World/CommandDispatcher.h b/Minecraft.World/CommandDispatcher.h new file mode 100644 index 00000000..600f1db1 --- /dev/null +++ b/Minecraft.World/CommandDispatcher.h @@ -0,0 +1,19 @@ +#pragma once + +class Command; +class CommandSender; + +class CommandDispatcher +{ +private: +#ifdef __ORBIS__ + unordered_map> commandsById; +#else + unordered_map commandsById; +#endif + unordered_set commands; + +public: + void performCommand(shared_ptr sender, EGameCommand command, byteArray commandData); + Command *addCommand(Command *command); +}; \ No newline at end of file diff --git a/Minecraft.World/CommandSender.h b/Minecraft.World/CommandSender.h new file mode 100644 index 00000000..21c25b53 --- /dev/null +++ b/Minecraft.World/CommandSender.h @@ -0,0 +1,12 @@ +#pragma once + +#include "CommandsEnum.h" +#include "ChatPacket.h" + +class CommandSender +{ +public: + //virtual int getUID() = 0; + virtual void sendMessage(const wstring& message, ChatPacket::EChatPacketMessage type = ChatPacket::e_ChatCustom, int customData = -1, const wstring& additionalMessage = L"") = 0; + virtual bool hasPermission(EGameCommand command) = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/CommandsEnum.h b/Minecraft.World/CommandsEnum.h new file mode 100644 index 00000000..46793e21 --- /dev/null +++ b/Minecraft.World/CommandsEnum.h @@ -0,0 +1,15 @@ +#pragma once + +enum EGameCommand +{ + eGameCommand_DefaultGameMode, + eGameCommand_EnchantItem, + eGameCommand_Experience, + eGameCommand_GameMode, + eGameCommand_Give, + eGameCommand_Kill, + eGameCommand_Time, + eGameCommand_ToggleDownfall, + eGameCommand_Teleport, + eGameCommand_COUNT +}; \ No newline at end of file diff --git a/Minecraft.World/CommonStats.cpp b/Minecraft.World/CommonStats.cpp new file mode 100644 index 00000000..8231070c --- /dev/null +++ b/Minecraft.World/CommonStats.cpp @@ -0,0 +1,298 @@ +#include "stdafx.h" +#include "Achievements.h" +#include "Item.h" +#include "Tile.h" +#include "CommonStats.h" + +Stat *CommonStats::get_stat(int i) +{ + return Stats::get(i); +} + +Stat* CommonStats::get_walkOneM() { return Stats::walkOneM; } + +Stat* CommonStats::get_swimOneM() { return Stats::swimOneM; } + +Stat* CommonStats::get_fallOneM() { return Stats::fallOneM; } + +Stat* CommonStats::get_climbOneM() { return Stats::climbOneM; } + +Stat* CommonStats::get_minecartOneM() { return Stats::minecartOneM; } + +Stat* CommonStats::get_boatOneM() { return Stats::boatOneM; } + +Stat* CommonStats::get_pigOneM() { return Stats::pigOneM; } + +Stat* CommonStats::get_portalsCreated() { return Stats::portalsCreated; } + +Stat* CommonStats::get_cowsMilked() { return Stats::cowsMilked; } + +Stat* CommonStats::get_netherLavaCollected() { return Stats::netherLavaCollected; } + +Stat* CommonStats::get_killsZombie() { return Stats::killsZombie; } + +Stat* CommonStats::get_killsSkeleton() { return Stats::killsSkeleton; } + +Stat* CommonStats::get_killsCreeper() { return Stats::killsCreeper; } + +Stat* CommonStats::get_killsSpider() { return Stats::killsSpider; } + +Stat* CommonStats::get_killsSpiderJockey() { return Stats::killsSpiderJockey; } + +Stat* CommonStats::get_killsZombiePigman() { return Stats::killsZombiePigman; } + +Stat* CommonStats::get_killsSlime() { return Stats::killsSlime; } + +Stat* CommonStats::get_killsGhast() { return Stats::killsGhast; } + +Stat* CommonStats::get_killsNetherZombiePigman() { return Stats::killsNetherZombiePigman; } + +Stat *CommonStats::get_breedEntity(eINSTANCEOF mobType) +{ + if (mobType == eTYPE_COW) return GenericStats::repopulation(); + else return NULL; +} + +Stat *CommonStats::get_tamedEntity(eINSTANCEOF mobType) +{ + if (mobType == eTYPE_OZELOT) return GenericStats::lionTamer(); + else if (mobType == eTYPE_WOLF) return Stats::befriendsWolf; + else return NULL; +} + +Stat *CommonStats::get_craftedEntity(eINSTANCEOF mobType) +{ + if (mobType == eTYPE_VILLAGERGOLEM) return GenericStats::bodyGuard(); + else return NULL; +} + +Stat *CommonStats::get_shearedEntity(eINSTANCEOF mobType) +{ + if (mobType == eTYPE_SHEEP) return GenericStats::haveAShearfulDay(); + else return NULL; +} + +Stat *CommonStats::get_totalBlocksMined() { return Stats::totalBlocksMined; } + +Stat* CommonStats::get_timePlayed() { return Stats::timePlayed; } + +Stat* CommonStats::get_blocksPlaced(int blockId) +{ +#if (defined _EXTENDED_ACHIEVEMENTS) && (!defined _XBOX_ONE) + return Stats::blocksPlaced[blockId]; +#else + return NULL; +#endif +} + +Stat *CommonStats::get_blocksMined(int blockId) { return Stats::blocksMined[blockId]; } + +Stat *CommonStats::get_itemsCollected(int itemId, int itemAux) +{ + // 4J-JEV: We don't need itemsCollected(emerald) so I'm using it to + // stor itemsBought(emerald) so I don't have to make yet another massive + // StatArray for Items Bought. +#if (defined _EXTENDED_ACHIEVEMENTS) && (!defined _XBOX_ONE) + if (itemId == Tile::cloth_Id) return Stats::rainbowCollection[itemAux]; +#endif + + if (itemId != Item::emerald_Id) return Stats::itemsCollected[itemId]; + else return NULL; +} + +Stat *CommonStats::get_itemsCrafted(int itemId) { return Stats::itemsCrafted[itemId]; } + +Stat *CommonStats::get_itemsSmelted(int itemId) { return Stats::itemsCrafted[itemId]; } + +Stat *CommonStats::get_itemsUsed(int itemId) +{ +#if (defined _EXTENDED_ACHIEVEMENTS) && (!defined _XBOX_ONE) + // 4J-JEV: I've done the same thing here, we can't place these items anyway. + if (itemId == Item::porkChop_cooked_Id) return Stats::blocksPlaced[itemId]; +#endif + + return NULL; +} + +Stat *CommonStats::get_itemsBought(int itemId) +{ + // 4J-JEV: We don't need itemsCollected(emerald) so I'm using it to + // stor itemsBought(emerald) so I don't have to make yet another massive + // StatArray for Items Bought. + + if (itemId == Item::emerald_Id) return Stats::itemsCollected[itemId]; + else return NULL; +} + +Stat *CommonStats::get_killsEnderdragon() { return Stats::killsEnderdragon; } + +Stat *CommonStats::get_completeTheEnd() { return Stats::completeTheEnd; } + +Stat *CommonStats::get_enteredBiome(int biomeId) +{ +#if (defined _EXTENDED_ACHIEVEMENTS) && (!defined _XBOX_ONE) + return Stats::biomesVisisted[biomeId]; +#else + return NULL; +#endif +} + +Stat *CommonStats::get_achievement(eAward achievementId) +{ + switch (achievementId) + { + case eAward_TakingInventory: return (Stat *) Achievements::openInventory; + case eAward_GettingWood: return (Stat *) Achievements::mineWood; + case eAward_Benchmarking: return (Stat *) Achievements::buildWorkbench; + case eAward_TimeToMine: return (Stat *) Achievements::buildPickaxe; + case eAward_HotTopic: return (Stat *) Achievements::buildFurnace; + case eAward_AquireHardware: return (Stat *) Achievements::acquireIron; + case eAward_TimeToFarm: return (Stat *) Achievements::buildHoe; + case eAward_BakeBread: return (Stat *) Achievements::makeBread; + case eAward_TheLie: return (Stat *) Achievements::bakeCake; + case eAward_GettingAnUpgrade: return (Stat *) Achievements::buildBetterPickaxe; + case eAward_DeliciousFish: return (Stat *) Achievements::cookFish; + case eAward_OnARail: return (Stat *) Achievements::onARail; + case eAward_TimeToStrike: return (Stat *) Achievements::buildSword; + case eAward_MonsterHunter: return (Stat *) Achievements::killEnemy; + case eAward_CowTipper: return (Stat *) Achievements::killCow; + case eAward_WhenPigsFly: return (Stat *) Achievements::flyPig; + case eAward_LeaderOfThePack: return (Stat *) Achievements::leaderOfThePack; + case eAward_MOARTools: return (Stat *) Achievements::MOARTools; + case eAward_DispenseWithThis: return (Stat *) Achievements::dispenseWithThis; + case eAward_InToTheNether: return (Stat *) Achievements::InToTheNether; + case eAward_mine100Blocks: return (Stat *) Achievements::mine100Blocks; + case eAward_kill10Creepers: return (Stat *) Achievements::kill10Creepers; + case eAward_eatPorkChop: return (Stat *) Achievements::eatPorkChop; + case eAward_play100Days: return (Stat *) Achievements::play100Days; + case eAward_arrowKillCreeper: return (Stat *) Achievements::arrowKillCreeper; + case eAward_socialPost: return (Stat *) Achievements::socialPost; + +#ifndef _XBOX + case eAward_snipeSkeleton: return (Stat *) Achievements::snipeSkeleton; + case eAward_diamonds: return (Stat *) Achievements::diamonds; + case eAward_portal: return (Stat *) NULL; // TODO + case eAward_ghast: return (Stat *) Achievements::ghast; + case eAward_blazeRod: return (Stat *) Achievements::blazeRod; + case eAward_potion: return (Stat *) Achievements::potion; + case eAward_theEnd: return (Stat *) Achievements::theEnd; + case eAward_winGame: return (Stat *) Achievements::winGame; + case eAward_enchantments: return (Stat *) Achievements::enchantments; +#endif + +#ifdef _EXTENDED_ACHIEVEMENTS + case eAward_overkill: return (Stat *) Achievements::overkill; // Restored old ach. + case eAward_bookcase: return (Stat *) Achievements::bookcase; // Restored old ach. + + case eAward_adventuringTime: return (Stat *) Achievements::adventuringTime; + case eAward_repopulation: return (Stat *) Achievements::repopulation; + case eAward_diamondsToYou: return (Stat *) Achievements::diamondsToYou; + //case eAward_passingTheTime: return (Stat *) Achievements::passingTheTime; + //case eAward_archer: return (Stat *) Achievements::archer; + case eAward_theHaggler: return (Stat *) Achievements::theHaggler; + case eAward_potPlanter: return (Stat *) Achievements::potPlanter; + case eAward_itsASign: return (Stat *) Achievements::itsASign; + case eAward_ironBelly: return (Stat *) Achievements::ironBelly; + case eAward_haveAShearfulDay: return (Stat *) Achievements::haveAShearfulDay; + case eAward_rainbowCollection: return (Stat *) Achievements::rainbowCollection; + case eAward_stayinFrosty: return (Stat *) Achievements::stayinFrosty; + case eAward_chestfulOfCobblestone: return (Stat *) Achievements::chestfulOfCobblestone; + case eAward_renewableEnergy: return (Stat *) Achievements::renewableEnergy; + case eAward_musicToMyEars: return (Stat *) Achievements::musicToMyEars; + case eAward_bodyGuard: return (Stat *) Achievements::bodyGuard; + case eAward_ironMan: return (Stat *) Achievements::ironMan; + case eAward_zombieDoctor: return (Stat *) Achievements::zombieDoctor; + case eAward_lionTamer: return (Stat *) Achievements::lionTamer; +#endif + + default: return (Stat *) NULL; + } +} + +byteArray CommonStats::getParam_walkOneM(int distance) +{ + return makeParam(distance); +} + +byteArray CommonStats::getParam_swimOneM(int distance) +{ + return makeParam(distance); +} + +byteArray CommonStats::getParam_fallOneM(int distance) +{ + return makeParam(distance); +} + +byteArray CommonStats::getParam_climbOneM(int distance) +{ + return makeParam(distance); +} + +byteArray CommonStats::getParam_minecartOneM(int distance) +{ + return makeParam(distance); +} + +byteArray CommonStats::getParam_boatOneM(int distance) +{ + return makeParam(distance); +} + +byteArray CommonStats::getParam_pigOneM(int distance) +{ + return makeParam(distance); +} + +byteArray CommonStats::getParam_blocksMined(int id, int data, int count) +{ + return makeParam(count); +} + +byteArray CommonStats::getParam_itemsCollected(int id, int aux, int count) +{ + return makeParam(count); +} + +byteArray CommonStats::getParam_itemsCrafted(int id, int aux, int count) +{ + return makeParam(count); +} + +byteArray CommonStats::getParam_itemsSmelted(int id, int aux, int count) +{ + return makeParam(count); +} + +byteArray CommonStats::getParam_itemsUsed(int id, int aux, int count) +{ + return makeParam(count); +} + +byteArray CommonStats::getParam_itemsBought(int id, int aux, int count) +{ + return makeParam(count); +} + +byteArray CommonStats::getParam_time(int timediff) +{ + return makeParam(timediff); +} + +byteArray CommonStats::getParam_noArgs() +{ + return makeParam(); +} + +byteArray CommonStats::makeParam(int count) +{ + byteArray out( sizeof(int) ); + memcpy(out.data,&count,sizeof(int)); + return out; +} + +int CommonStats::readParam(byteArray paramBlob) +{ + if (paramBlob.length == sizeof(int)) return *( (int*) paramBlob.data ); + else return 1; +} \ No newline at end of file diff --git a/Minecraft.World/CommonStats.h b/Minecraft.World/CommonStats.h new file mode 100644 index 00000000..3cf079a1 --- /dev/null +++ b/Minecraft.World/CommonStats.h @@ -0,0 +1,80 @@ +#pragma once + +#include "GenericStats.h" + +class CommonStats : public GenericStats +{ +protected: + using GenericStats::getParam_itemsUsed; + + virtual Stat *get_stat(int i); + + virtual Stat* get_walkOneM(); + virtual Stat* get_swimOneM(); + virtual Stat* get_fallOneM(); + virtual Stat* get_climbOneM(); + virtual Stat* get_minecartOneM(); + virtual Stat* get_boatOneM(); + virtual Stat* get_pigOneM(); + virtual Stat* get_portalsCreated(); + virtual Stat* get_cowsMilked(); + virtual Stat* get_netherLavaCollected(); + + virtual Stat* get_killsZombie(); + virtual Stat* get_killsSkeleton(); + virtual Stat* get_killsCreeper(); + virtual Stat* get_killsSpider(); + virtual Stat* get_killsSpiderJockey(); + virtual Stat* get_killsZombiePigman(); + virtual Stat* get_killsSlime(); + virtual Stat* get_killsGhast(); + virtual Stat* get_killsNetherZombiePigman(); + + virtual Stat* get_totalBlocksMined(); + virtual Stat* get_timePlayed(); + + virtual Stat* get_breedEntity(eINSTANCEOF entityId); + virtual Stat* get_tamedEntity(eINSTANCEOF entityId); + virtual Stat* get_craftedEntity(eINSTANCEOF entityId); + virtual Stat* get_shearedEntity(eINSTANCEOF entityId); + + virtual Stat* get_blocksPlaced(int blockId); + virtual Stat* get_blocksMined(int blockId); + virtual Stat* get_itemsCollected(int itemId, int itemAux); + virtual Stat* get_itemsCrafted(int itemId); + virtual Stat* get_itemsSmelted(int itemId); + virtual Stat* get_itemsUsed(int itemId); + virtual Stat* get_itemsBought(int itemId); + + virtual Stat* get_killsEnderdragon(); + virtual Stat* get_completeTheEnd(); + + virtual Stat* get_enteredBiome(int biomeId); + + virtual Stat* get_achievement(eAward achievementId); + + virtual byteArray getParam_walkOneM(int distance); + virtual byteArray getParam_swimOneM(int distance); + virtual byteArray getParam_fallOneM(int distance); + virtual byteArray getParam_climbOneM(int distance); + virtual byteArray getParam_minecartOneM(int distance); + virtual byteArray getParam_boatOneM(int distance); + virtual byteArray getParam_pigOneM(int distance); + + virtual byteArray getParam_blocksMined(int id, int data, int count); + virtual byteArray getParam_itemsCollected(int id, int aux, int count); + virtual byteArray getParam_itemsCrafted(int id, int aux, int count); + virtual byteArray getParam_itemsSmelted(int id, int aux, int count); + virtual byteArray getParam_itemsUsed(int id, int aux, int count); + virtual byteArray getParam_itemsBought(int id, int aux, int count); + + virtual byteArray getParam_time(int timediff); + + virtual byteArray getParam_noArgs(); + +protected: + static byteArray makeParam(int count = 1); + +public: + static int readParam(byteArray paramBlob); +}; \ No newline at end of file diff --git a/Minecraft.World/CompassItem.cpp b/Minecraft.World/CompassItem.cpp new file mode 100644 index 00000000..57ad65d0 --- /dev/null +++ b/Minecraft.World/CompassItem.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "CompassItem.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\MultiPlayerLocalPlayer.h" +#include "net.minecraft.world.h" + +#ifdef __PSVITA__ +const wstring CompassItem::TEXTURE_PLAYER_ICON[XUSER_MAX_COUNT] = {L"compassP0"}; +#else +const wstring CompassItem::TEXTURE_PLAYER_ICON[XUSER_MAX_COUNT] = {L"compassP0",L"compassP1",L"compassP2",L"compassP3"}; +#endif + +CompassItem::CompassItem(int id) : Item(id) +{ + icons = NULL; +} + +// 4J Added so that we can override the icon id used to calculate the texture UV's for each player + +Icon *CompassItem::getIcon(int auxValue) +{ + Icon *icon = Item::getIcon(auxValue); + Minecraft *pMinecraft = Minecraft::GetInstance(); + + if( pMinecraft->player != NULL && auxValue == 0 ) + { + icon = icons[pMinecraft->player->GetXboxPad()]; + } + return icon; +} + +void CompassItem::registerIcons(IconRegister *iconRegister) +{ + Item::registerIcons(iconRegister); + + icons = new Icon *[XUSER_MAX_COUNT]; + + for (int i = 0; i < XUSER_MAX_COUNT; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURE_PLAYER_ICON[i]); + } +} diff --git a/Minecraft.World/CompassItem.h b/Minecraft.World/CompassItem.h new file mode 100644 index 00000000..b975144a --- /dev/null +++ b/Minecraft.World/CompassItem.h @@ -0,0 +1,19 @@ +#pragma once +// 4J Added so that we can override the icon id used to calculate the texture UV's for each player + +#include "Item.h" + +class CompassItem : public Item +{ +private: + Icon **icons; + static const wstring TEXTURE_PLAYER_ICON[XUSER_MAX_COUNT]; + +public: + CompassItem(int id); + + virtual Icon *getIcon(int auxValue); + + //@Override + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/ComplexItem.cpp b/Minecraft.World/ComplexItem.cpp new file mode 100644 index 00000000..9cf09b87 --- /dev/null +++ b/Minecraft.World/ComplexItem.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" +#include "net.minecraft.network.packet.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "ComplexItem.h" + +ComplexItem::ComplexItem(int id) : Item(id) +{ +} + +bool ComplexItem::isComplex() +{ + return true; +} + +shared_ptr ComplexItem::getUpdatePacket(shared_ptr itemInstance, Level *level, shared_ptr player) +{ + return nullptr; +} \ No newline at end of file diff --git a/Minecraft.World/ComplexItem.h b/Minecraft.World/ComplexItem.h new file mode 100644 index 00000000..28aba791 --- /dev/null +++ b/Minecraft.World/ComplexItem.h @@ -0,0 +1,18 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class Player; +class Packet; +class Level; + +class ComplexItem : public Item +{ +protected: + ComplexItem(int id); + +public: + virtual bool isComplex(); + virtual shared_ptr getUpdatePacket(shared_ptr itemInstance, Level *level, shared_ptr player); +}; diff --git a/Minecraft.World/ComplexItemDataPacket.cpp b/Minecraft.World/ComplexItemDataPacket.cpp new file mode 100644 index 00000000..4c365bbd --- /dev/null +++ b/Minecraft.World/ComplexItemDataPacket.cpp @@ -0,0 +1,57 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "ComplexItemDataPacket.h" + + + +ComplexItemDataPacket::~ComplexItemDataPacket() +{ + delete [] data.data; +} + +ComplexItemDataPacket::ComplexItemDataPacket() +{ + shouldDelay = true; + itemType = 0; +} + +ComplexItemDataPacket::ComplexItemDataPacket(short itemType, short itemId, charArray data) +{ + shouldDelay = true; + this->itemType = itemType; + this->itemId = itemId; + // Take copy of array passed in as we want the packets to have full ownership of any data they reference + this->data = charArray(data.length); + memcpy(this->data.data, data.data, data.length); +} + +void ComplexItemDataPacket::read(DataInputStream *dis) //throws IOException +{ + itemType = dis->readShort(); + itemId = dis->readShort(); + + data = charArray(dis->readShort() & 0xffff); + dis->readFully(data); +} + +void ComplexItemDataPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeShort(itemType); + dos->writeShort(itemId); + dos->writeShort(data.length); + + byteArray ba( (byte*)data.data, data.length ); + dos->write(ba); +} + +void ComplexItemDataPacket::handle(PacketListener *listener) +{ + listener->handleComplexItemData( shared_from_this() ); +} + +int ComplexItemDataPacket::getEstimatedSize() +{ + return 2+2+2+ data.length; +} diff --git a/Minecraft.World/ComplexItemDataPacket.h b/Minecraft.World/ComplexItemDataPacket.h new file mode 100644 index 00000000..52de8f49 --- /dev/null +++ b/Minecraft.World/ComplexItemDataPacket.h @@ -0,0 +1,27 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class ComplexItemDataPacket : public Packet, public enable_shared_from_this +{ +public: + short itemType; + short itemId; + charArray data; + + ComplexItemDataPacket(); + ~ComplexItemDataPacket(); + ComplexItemDataPacket(short itemType, short itemId, charArray data); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ComplexItemDataPacket()); } + virtual int getId() { return 131; } +}; + + diff --git a/Minecraft.World/CompoundContainer.cpp b/Minecraft.World/CompoundContainer.cpp new file mode 100644 index 00000000..29bf60dd --- /dev/null +++ b/Minecraft.World/CompoundContainer.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" + +#include "CompoundContainer.h" + +CompoundContainer::CompoundContainer(int name, shared_ptr c1, shared_ptr c2) +{ + this->name = name; + if (c1 == NULL) c1 = c2; + if (c2 == NULL) c2 = c1; + this->c1 = c1; + this->c2 = c2; +} + +unsigned int CompoundContainer::getContainerSize() +{ + return c1->getContainerSize() + c2->getContainerSize(); +} + +int CompoundContainer::getName() +{ + return name; +} + +shared_ptr CompoundContainer::getItem(unsigned int slot) +{ + if (slot >= c1->getContainerSize()) return c2->getItem(slot - c1->getContainerSize()); + else return c1->getItem(slot); +} + +shared_ptr CompoundContainer::removeItem(unsigned int slot, int i) +{ + if (slot >= c1->getContainerSize()) return c2->removeItem(slot - c1->getContainerSize(), i); + else return c1->removeItem(slot, i); +} + +shared_ptr CompoundContainer::removeItemNoUpdate(int slot) +{ + if (slot >= c1->getContainerSize()) return c2->removeItemNoUpdate(slot - c1->getContainerSize()); + else return c1->removeItemNoUpdate(slot); +} + +void CompoundContainer::setItem(unsigned int slot, shared_ptr item) +{ + if (slot >= c1->getContainerSize()) c2->setItem(slot - c1->getContainerSize(), item); + else c1->setItem(slot, item); +} + +int CompoundContainer::getMaxStackSize() +{ + return c1->getMaxStackSize(); +} + +void CompoundContainer::setChanged() +{ + c1->setChanged(); + c2->setChanged(); +} + +bool CompoundContainer::stillValid(shared_ptr player) +{ + return c1->stillValid(player) && c2->stillValid(player); +} + +void CompoundContainer::startOpen() +{ + c1->startOpen(); + c2->startOpen(); +} + +void CompoundContainer::stopOpen() +{ + c1->stopOpen(); + c2->stopOpen(); +} \ No newline at end of file diff --git a/Minecraft.World/CompoundContainer.h b/Minecraft.World/CompoundContainer.h new file mode 100644 index 00000000..b0b8b934 --- /dev/null +++ b/Minecraft.World/CompoundContainer.h @@ -0,0 +1,36 @@ +#pragma once +using namespace std; + +#include "Container.h" + +class Player; + +class CompoundContainer : public Container +{ +private: + int name; + shared_ptr c1, c2; + +public: + CompoundContainer(int name, shared_ptr c1, shared_ptr c2); + + unsigned int getContainerSize(); + + int getName(); + + shared_ptr getItem(unsigned int slot); + + shared_ptr removeItem(unsigned int slot, int i); + shared_ptr removeItemNoUpdate(int slot); + + void setItem(unsigned int slot, shared_ptr item); + + int getMaxStackSize(); + + void setChanged(); + + bool stillValid(shared_ptr player); + + virtual void startOpen(); + virtual void stopOpen(); +}; \ No newline at end of file diff --git a/Minecraft.World/CompoundTag.h b/Minecraft.World/CompoundTag.h new file mode 100644 index 00000000..32c31583 --- /dev/null +++ b/Minecraft.World/CompoundTag.h @@ -0,0 +1,292 @@ +#pragma once +#include "Tag.h" +#include "ListTag.h" +#include "ByteTag.h" +#include "DoubleTag.h" +#include "FloatTag.h" +#include "IntTag.h" +#include "LongTag.h" +#include "ShortTag.h" +#include "StringTag.h" +#include "ByteArrayTag.h" +#include "IntArrayTag.h" + +class CompoundTag : public Tag +{ +private: + unordered_map tags; + +public: + CompoundTag() : Tag(L"") {} + CompoundTag(const wstring &name) : Tag(name) {} + + void write(DataOutput *dos) + { + AUTO_VAR(itEnd, tags.end()); + for( unordered_map::iterator it = tags.begin(); it != itEnd; it++ ) + { + Tag::writeNamedTag(it->second, dos); + } + dos->writeByte(Tag::TAG_End); + } + + + void load(DataInput *dis) + { + tags.clear(); + Tag *tag; + while ((tag = Tag::readNamedTag(dis))->getId() != Tag::TAG_End) + { + tags[tag->getName()] = tag; + } + delete tag; + } + + vector *getAllTags() // 4J - was collection + { + // 4J - was return tags.values(); + vector *ret = new vector; + + AUTO_VAR(itEnd, tags.end()); + for( unordered_map::iterator it = tags.begin(); it != itEnd; it++ ) + { + ret->push_back(it->second); + } + return ret; + } + + byte getId() + { + return TAG_Compound; + } + + void put(wchar_t *name, Tag *tag) + { + tags[name] = tag->setName(wstring( name )); + } + + void putByte(wchar_t * name, byte value) + { + tags[name] = (new ByteTag(name,value)); + } + + void putShort(wchar_t * name, short value) + { + tags[name] = (new ShortTag(name,value)); + } + + void putInt(wchar_t * name, int value) + { + tags[name] = (new IntTag(name,value)); + } + + void putLong(wchar_t * name, __int64 value) + { + tags[name] = (new LongTag(name,value)); + } + + void putFloat(wchar_t * name, float value) + { + tags[name] = (new FloatTag(name,value)); + } + + void putDouble(wchar_t * name, double value) + { + tags[name] = (new DoubleTag(name,value)); + } + + void putString(wchar_t *name, const wstring& value) + { + tags[name] = (new StringTag(name,value)); + } + + void putByteArray(wchar_t * name, byteArray value) + { + tags[name] = (new ByteArrayTag(name,value)); + } + + void putIntArray(wchar_t * name, intArray value) + { + tags[name] = (new IntArrayTag(name, value)); + } + + void putCompound(wchar_t * name, CompoundTag *value) + { + tags[name] = value->setName( wstring( name ) ); + } + + void putBoolean(wchar_t * string, bool val) + { + putByte(string, val?(byte)1:0); + } + + Tag *get(wchar_t *name) + { + AUTO_VAR(it, tags.find(name)); + if(it != tags.end()) return it->second; + return NULL; + } + + bool contains(wchar_t * name) + { + return tags.find(name) != tags.end(); + } + + byte getByte(wchar_t * name) + { + if (tags.find(name) == tags.end()) return (byte)0; + return ((ByteTag *) tags[name])->data; + } + + short getShort(wchar_t * name) + { + if (tags.find(name) == tags.end()) return (short)0; + return ((ShortTag *) tags[name])->data; + } + + int getInt(wchar_t * name) + { + if (tags.find(name) == tags.end()) return (int)0; + return ((IntTag *) tags[name])->data; + } + + __int64 getLong(wchar_t * name) + { + if (tags.find(name) == tags.end()) return (__int64)0; + return ((LongTag *) tags[name])->data; + } + + float getFloat(wchar_t * name) + { + if (tags.find(name) == tags.end()) return (float)0; + return ((FloatTag *) tags[name])->data; + } + + double getDouble(wchar_t * name) + { + if (tags.find(name) == tags.end()) return (double)0; + return ((DoubleTag *) tags[name])->data; + } + + wstring getString(wchar_t * name) + { + if (tags.find(name) == tags.end()) return wstring( L"" ); + return ((StringTag *) tags[name])->data; + } + + byteArray getByteArray(wchar_t * name) + { + if (tags.find(name) == tags.end()) return byteArray(); + return ((ByteArrayTag *) tags[name])->data; + } + + intArray getIntArray(wchar_t * name) + { + if (tags.find(name) == tags.end()) return intArray(0); + return ((IntArrayTag *) tags[name])->data; + } + + CompoundTag *getCompound(wchar_t * name) + { + if (tags.find(name) == tags.end()) return new CompoundTag(name); + return (CompoundTag *) tags[name]; + } + + ListTag *getList(wchar_t * name) + { + if (tags.find(name) == tags.end()) return new ListTag(name); + return (ListTag *) tags[name]; + } + + bool getBoolean(wchar_t *string) + { + return getByte(string)!=0; + } + + void remove(const wstring &name) + { + AUTO_VAR(it, tags.find(name)); + if(it != tags.end()) tags.erase(it); + //tags.remove(name); + } + + wstring toString() + { + static const int bufSize = 32; + static wchar_t buf[bufSize]; + swprintf(buf,bufSize,L"%d entries",tags.size()); + return wstring( buf ); + } + + void print(char *prefix, ostream out) + { + /* + Tag::print(prefix, out); + out << prefix << "{" << endl; + + char *newPrefix = new char[ strlen(prefix) + 4 ]; + strcpy( newPrefix, prefix); + strcat( newPrefix, " "); + + AUTO_VAR(itEnd, tags.end()); + for( unordered_map::iterator it = tags.begin(); it != itEnd; it++ ) + { + it->second->print(newPrefix, out); + } + delete[] newPrefix; + out << prefix << "}" << endl; + */ + } + + bool isEmpty() + { + return tags.empty(); + } + + virtual ~CompoundTag() + { + AUTO_VAR(itEnd, tags.end()); + for( AUTO_VAR(it, tags.begin()); it != itEnd; it++ ) + { + delete it->second; + } + } + + Tag *copy() + { + CompoundTag *tag = new CompoundTag(getName()); + + AUTO_VAR(itEnd, tags.end()); + for( AUTO_VAR(it, tags.begin()); it != itEnd; it++ ) + { + tag->put((wchar_t *)it->first.c_str(), it->second->copy()); + } + return tag; + } + + bool equals(Tag *obj) + { + if (Tag::equals(obj)) + { + CompoundTag *o = (CompoundTag *) obj; + + if(tags.size() == o->tags.size()) + { + bool equal = true; + AUTO_VAR(itEnd, tags.end()); + for( AUTO_VAR(it, tags.begin()); it != itEnd; it++ ) + { + AUTO_VAR(itFind, o->tags.find(it->first)); + if(itFind == o->tags.end() || !it->second->equals(itFind->second) ) + { + equal = false; + break; + } + } + return equal; + //return tags.entrySet().equals(o.tags.entrySet()); + } + } + return false; + } +}; diff --git a/Minecraft.World/CompressedTileStorage.cpp b/Minecraft.World/CompressedTileStorage.cpp new file mode 100644 index 00000000..896bd58d --- /dev/null +++ b/Minecraft.World/CompressedTileStorage.cpp @@ -0,0 +1,1361 @@ +#include "stdafx.h" +#include "CompressedTileStorage.h" + +#ifdef __PSVITA__ +#define PSVITA_PRECOMPUTED_TABLE +#endif + +#ifdef __PS3__ +#include "..\SPU_Tasks\CompressedTileStorage_compress\CompressedTileStorage_compress.h" +#include "C4JSpursJob.h" +static const int sc_maxCompressTiles = 64; +static CompressedTileStorage_compress_dataIn g_compressTileDataIn[sc_maxCompressTiles] __attribute__((__aligned__(16))); +static int g_currentCompressTiles = 0; +#endif //__PS3__ + +// Note: See header for an overview of this class + +int CompressedTileStorage::deleteQueueIndex; +XLockFreeStack CompressedTileStorage::deleteQueue[3]; + +CRITICAL_SECTION CompressedTileStorage::cs_write; + +#ifdef PSVITA_PRECOMPUTED_TABLE +// AP - this will create a precomputed table to speed up getData +static int *CompressedTile_StorageIndexTable = NULL; + +void CompressedTileStorage_InitTable() +{ + if( CompressedTile_StorageIndexTable == NULL ) + { + CompressedTile_StorageIndexTable = (int*) malloc(sizeof(int) * 64); + for(int j = 0;j < 64;j += 1 ) + { + int index = ( ( j & 0x30 ) << 7) | ( ( j & 0x0c ) << 5 ) | ( j & 0x03 ); + CompressedTile_StorageIndexTable[j] = index; + } + } +} +#endif + +CompressedTileStorage::CompressedTileStorage() +{ + indicesAndData = NULL; + allocatedSize = 0; + +#ifdef PSVITA_PRECOMPUTED_TABLE + CompressedTileStorage_InitTable(); +#endif +} + +CompressedTileStorage::CompressedTileStorage(CompressedTileStorage *copyFrom) +{ + EnterCriticalSection(&cs_write); + allocatedSize = copyFrom->allocatedSize; + if(allocatedSize > 0) + { + indicesAndData = (unsigned char *)XPhysicalAlloc(allocatedSize, MAXULONG_PTR, 4096, PAGE_READWRITE);//(unsigned char *)malloc(allocatedSize); + XMemCpy(indicesAndData, copyFrom->indicesAndData, allocatedSize); + } + else + { + indicesAndData = NULL; + } + LeaveCriticalSection(&cs_write); + +#ifdef PSVITA_PRECOMPUTED_TABLE + CompressedTileStorage_InitTable(); +#endif +} + +CompressedTileStorage::CompressedTileStorage(byteArray initFrom, unsigned int initOffset) +{ + indicesAndData = NULL; + allocatedSize = 0; + + // We need 32768 bytes for a fully uncompressed chunk, plus 1024 for the index. Rounding up to nearest 4096 bytes for allocation + indicesAndData = (unsigned char *)XPhysicalAlloc(32768+4096, MAXULONG_PTR, 4096, PAGE_READWRITE); + + unsigned short *indices = (unsigned short *)indicesAndData; + unsigned char *data = indicesAndData + 1024; + + int offset = 0; + for( int i = 0; i < 512; i++ ) + { + indices[i] = INDEX_TYPE_0_OR_8_BIT | ( offset << 1 ); + + if( initFrom.data ) + { + for( int j = 0; j < 64; j++ ) + { + *data++ = initFrom[getIndex(i,j) + initOffset]; + } + } + else + { + for( int j = 0; j < 64; j++ ) + { + *data++ = 0; + } + } + + offset += 64; + } + + allocatedSize = 32768 + 1024; // This is used for copying (see previous ctor), and as such it only needs to be the actual size of the data used rather than the one rounded up to a page size actually allocated + +#ifdef PSVITA_PRECOMPUTED_TABLE + CompressedTileStorage_InitTable(); +#endif +} + +bool CompressedTileStorage::isCompressed() +{ + return allocatedSize != 32768 + 1024; +} + +CompressedTileStorage::CompressedTileStorage(bool isEmpty) +{ + indicesAndData = NULL; + allocatedSize = 0; + + // Empty and already compressed, so we only need 1K. Rounding up to nearest 4096 bytes for allocation +#ifdef __PS3__ + // XPhysicalAlloc just maps to malloc on PS3, so allocate the smallest amount + indicesAndData = (unsigned char *)XPhysicalAlloc(1024, MAXULONG_PTR, 4096, PAGE_READWRITE); +#else + indicesAndData = (unsigned char *)XPhysicalAlloc(4096, MAXULONG_PTR, 4096, PAGE_READWRITE); +#endif //__PS3__ + unsigned short *indices = (unsigned short *)indicesAndData; + //unsigned char *data = indicesAndData + 1024; + + //int offset = 0; + for( int i = 0; i < 512; i++ ) + { + indices[i] = INDEX_TYPE_0_OR_8_BIT | INDEX_TYPE_0_BIT_FLAG; + } + + allocatedSize = 1024; // This is used for copying (see previous ctor), and as such it only needs to be the actual size of the data used rather than the one rounded up to a page size actually allocated + +#ifdef PSVITA_PRECOMPUTED_TABLE + CompressedTileStorage_InitTable(); +#endif +} + +bool CompressedTileStorage::isRenderChunkEmpty(int y) // y == 0, 16, 32... 112 (representing a 16 byte range) +{ + int block; + unsigned short *blockIndices = (unsigned short *)indicesAndData; + + for( int x = 0; x < 16; x += 4 ) + for( int z = 0; z < 16; z += 4 ) + { + getBlock(&block, x, y, z); + __uint64 *comp = (__uint64 *)&blockIndices[block]; + // Are the 4 y regions stored here all zero? (INDEX_TYPE_0_OR_8_BIT | INDEX_TYPE_0_BIT_FLAG ) + if( ( *comp ) != 0x0007000700070007L ) return false; + } + return true; +} + +bool CompressedTileStorage::isSameAs(CompressedTileStorage *other) +{ + EnterCriticalSection(&cs_write); + if( allocatedSize != other->allocatedSize ) + { + LeaveCriticalSection(&cs_write); + return false; + } + + // Attempt to compare as much as we can in 64-byte chunks (8 groups of 8 bytes) + int quickCount = allocatedSize / 64; + __int64 *pOld = (__int64 *)indicesAndData; + __int64 *pNew = (__int64 *)other->indicesAndData; + for( int i = 0; i < quickCount; i++ ) + { + __int64 d0 = pOld[0] ^ pNew[0]; + __int64 d1 = pOld[1] ^ pNew[1]; + __int64 d2 = pOld[2] ^ pNew[2]; + __int64 d3 = pOld[3] ^ pNew[3]; + __int64 d4 = pOld[4] ^ pNew[4]; + __int64 d5 = pOld[5] ^ pNew[5]; + __int64 d6 = pOld[6] ^ pNew[6]; + __int64 d7 = pOld[7] ^ pNew[7]; + d0 |= d1; + d2 |= d3; + d4 |= d5; + d6 |= d7; + d0 |= d2; + d4 |= d6; + if( d0 | d4 ) + { + LeaveCriticalSection(&cs_write); + return false; + } + pOld += 8; + pNew += 8; + } + + // Now test anything remaining just byte at a time + unsigned char *pucOld = (unsigned char *)pOld; + unsigned char *pucNew = (unsigned char *)pNew; + for( int i = 0; i < allocatedSize - (quickCount * 64); i++ ) + { + if( *pucOld++ != *pucNew++ ) + { + LeaveCriticalSection(&cs_write); + return false; + } + } + + LeaveCriticalSection(&cs_write); + return true; +} + +CompressedTileStorage::~CompressedTileStorage() +{ +#if 1 + if(indicesAndData) XPhysicalFree(indicesAndData); +#else + if( (unsigned int)indicesAndData >= MM_PHYSICAL_4KB_BASE ) + { + if(indicesAndData) XPhysicalFree(indicesAndData); + } + else + { + if(indicesAndData) free(indicesAndData); + } +#endif +} + +// Get an index into the normal ordering of tiles for the java game, given a block index (0 to 511) and a tile index (0 to 63) +inline int CompressedTileStorage::getIndex(int block, int tile) +{ + // bits for index into data is: xxxxzzzzyyyyyyy + // we want block(b) & tile(t) spread out as: + // from: ______bbbbbbbbb + // to: bb__bb__bbbbb__ + // + // from: _________tttttt + // to: __tt__tt_____tt + + int index = ( ( block & 0x180) << 6 ) | ( ( block & 0x060 ) << 4 ) | ( ( block & 0x01f ) << 2 ); + index |= ( ( tile & 0x30 ) << 7) | ( ( tile & 0x0c ) << 5 ) | ( tile & 0x03 ); + + return index; +} + +// Get the block and tile (reversing getIndex above) for a given x, y, z coordinate +// +// bits for index into data is: xxxxzzzzyyyyyyy +// bbttbbttbbbbbtt +// +// so x is: ___________xxxx +// and maps to this bit of b ______bb_______ +// and this bit of t _________tt____ +// +// y is: ________yyyyyyy +// and maps to this bit of b __________bbbbb +// and this bit of t _____________tt +// +// and z is: ___________zzzz +// and maps to this bit of b ________bb_____ +// and this bit of t ___________tt__ +// + +inline void CompressedTileStorage::getBlockAndTile(int *block, int *tile, int x, int y, int z) +{ + *block = ( ( x & 0x0c ) << 5 ) | ( ( z & 0x0c ) << 3 ) | ( y >> 2 ); + *tile = ( ( x & 0x03 ) << 4 ) | ( ( z & 0x03 ) << 2 ) | ( y & 0x03 ); +} + +inline void CompressedTileStorage::getBlock(int *block, int x, int y, int z) +{ + *block = ( ( x & 0x0c ) << 5 ) | ( ( z & 0x0c ) << 3 ) | ( y >> 2 ); +} + +// Set all tile values from a data array of length 32768 (128 x 16 x 16). +void CompressedTileStorage::setData(byteArray dataIn, unsigned int inOffset) +{ + unsigned short _blockIndices[512]; + + EnterCriticalSection(&cs_write); + unsigned char *data = dataIn.data + inOffset; + + // Is the destination fully uncompressed? If so just write our data in - this happens when writing schematics and we don't want this setting of data to trigger compression + if( allocatedSize == ( 32768 + 1024 ) ) + { + //unsigned short *indices = (unsigned short *)indicesAndData; + unsigned char *dataOut = indicesAndData + 1024; + + for( int i = 0; i < 512; i++ ) + { + for( int j = 0; j < 64; j++ ) + { + *dataOut++ = data[getIndex(i,j)]; + } + } + LeaveCriticalSection(&cs_write); + return; + } + + int offsets[512]; + int memToAlloc = 0; +// static int type0 = 0, type1 = 0, type2 = 0, type4 = 0, type8 = 0, chunkTotal = 0; + + // Loop round all blocks + for( int i = 0; i < 512; i++ ) + { + offsets[i] = memToAlloc; + // Count how many unique tile types are in the block - if unpacked_data isn't set then there isn't any data so we can't compress any further and require no storage. + // Store flags for each tile type used in an array of 4 64-bit flags. + +#ifdef __PSVITA__ + // AP - Vita isn't so great at shifting 64bits. The top biggest CPU time sink after profiling is __ashldi3 (64bit shift) at 3% + // Let's use 32bit instead + unsigned int usedFlags[8] = {0,0,0,0,0,0,0,0}; + __int32 i32_1 = 1; + for( int j = 0; j < 64; j++ ) // This loop of 64 is to go round the 4 x 4 tiles in the block + { + int tile = data[getIndex(i,j)]; + if( tile < (64<<2) ) + { + usedFlags[tile & 7] |= ( i32_1 << ( tile >> 3 ) ); + } + } + int count = 0; + for( int tile = 0; tile < 256; tile++ ) // This loop of 256 is to go round the 256 possible values that the tiles might have had to find how many are actually used + { + if( usedFlags[tile & 7] & ( i32_1 << ( tile >> 3 ) ) ) + { + count++; + } + } +#else + __uint64 usedFlags[4] = {0,0,0,0}; + __int64 i64_1 = 1; // MGH - instead of 1i64, which is MS specific + for( int j = 0; j < 64; j++ ) // This loop of 64 is to go round the 4 x 4 tiles in the block + { + int tile = data[getIndex(i,j)]; + + usedFlags[tile & 3] |= ( i64_1 << ( tile >> 2 ) ); + } + int count = 0; + for( int tile = 0; tile < 256; tile++ ) // This loop of 256 is to go round the 256 possible values that the tiles might have had to find how many are actually used + { + if( usedFlags[tile & 3] & ( i64_1 << ( tile >> 2 ) ) ) + { + count++; + } + } +#endif + if( count == 1 ) + { + _blockIndices[i] = INDEX_TYPE_0_OR_8_BIT | INDEX_TYPE_0_BIT_FLAG; +// type0++; + } + else if( count == 2 ) + { + _blockIndices[i] = INDEX_TYPE_1_BIT; + memToAlloc += 10; // 8 bytes + 2 tile index +// type1++; + } + else if ( count <= 4 ) + { + _blockIndices[i] = INDEX_TYPE_2_BIT; + memToAlloc += 20; // 16 bytes + 4 tile index +// type2++; + } + else if ( count <= 16 ) + { + _blockIndices[i] = INDEX_TYPE_4_BIT; + memToAlloc += 48; // 32 bytes + 16 tile index +// type4++; + } + else + { + _blockIndices[i] = INDEX_TYPE_0_OR_8_BIT; + memToAlloc = ( memToAlloc + 3 ) & 0xfffc; // Make sure we are 4-byte aligned for 8-bit storage + memToAlloc += 64; +// type8++; + } + } + +// chunkTotal++; +// printf("%d: %d (0) %d (1) %d (2) %d (4) %d (8)\n", chunkTotal, type0 / chunkTotal, type1 / chunkTotal, type2 / chunkTotal, type4 / chunkTotal, type8 / chunkTotal); + + memToAlloc += 1024; // For the indices + unsigned char *newIndicesAndData = (unsigned char *)XPhysicalAlloc(memToAlloc, MAXULONG_PTR, 4096, PAGE_READWRITE);//(unsigned char *)malloc( memToAlloc ); + unsigned char *pucData = newIndicesAndData + 1024; + unsigned short usDataOffset = 0; + unsigned short *newIndices = (unsigned short *) newIndicesAndData; + + // Now pass through again actually making the final compressed data + for( int i = 0; i < 512; i++ ) + { + unsigned short indexTypeNew = _blockIndices[i] & INDEX_TYPE_MASK; + newIndices[i] = indexTypeNew; + + if( indexTypeNew == INDEX_TYPE_0_OR_8_BIT ) + { + if( _blockIndices[i] & INDEX_TYPE_0_BIT_FLAG ) + { + newIndices[i] = INDEX_TYPE_0_OR_8_BIT | INDEX_TYPE_0_BIT_FLAG | (((unsigned short)data[getIndex(i,0)]) << INDEX_TILE_SHIFT); + } + else + { + usDataOffset = (usDataOffset + 3 ) & 0xfffc; + for( int j = 0; j < 64; j++ ) // This loop of 64 is to go round the 4 x 4 x 4 tiles in the block + { + pucData[usDataOffset + j] = data[getIndex(i,j)]; + } + newIndices[i] |= ( usDataOffset & INDEX_OFFSET_MASK) << INDEX_OFFSET_SHIFT; + usDataOffset += 64; + } + } + else + { + // Need to repack - TODO - from here onwards! + unsigned char ucMappings[256] = {0}; + for( int j = 0; j < 256; j++ ) + { + ucMappings[j] = 255; + } + + unsigned char *repacked = NULL; + + int bitspertile = 1 << indexTypeNew; // will be 1, 2 or 4 (from index values of 0, 1, 2) + int tiletypecount = 1 << bitspertile; // will be 2, 4 or 16 (from index values of 0, 1, 2) + //int tiletypemask = tiletypecount - 1; // will be 1, 3 or 15 (from index values of 0, 1, 2) + int tiledatasize = 8 << indexTypeNew; // will be 8, 16 or 32 (from index values of 0, 1, 2) + int indexshift = 3 - indexTypeNew; // will be 3, 2 or 1 (from index values of 0, 1, 2) + int indexmask_bits = 7 >> indexTypeNew; // will be 7, 3 or 1 (from index values of 0, 1, 2) + int indexmask_bytes = 62 >> indexshift; // will be 7, 15 or 31 (from index values of 0, 1, 2) + + unsigned char *tile_types = pucData + usDataOffset; + repacked = tile_types + tiletypecount; + XMemSet(tile_types, 255, tiletypecount); + XMemSet(repacked, 0,tiledatasize); + newIndices[i] |= ( usDataOffset & INDEX_OFFSET_MASK) << INDEX_OFFSET_SHIFT; + usDataOffset += tiletypecount + tiledatasize; + int count = 0; + for( int j = 0; j < 64; j++ ) + { + int tile = data[getIndex(i,j)]; + if( ucMappings[tile] == 255 ) + { + ucMappings[tile] = count; + tile_types[count++] = tile; + } + int idx = (j >> indexshift) & indexmask_bytes; + int bit = ( j & indexmask_bits ) * bitspertile; + repacked[idx] |= ucMappings[tile] << bit; + } + } + } + + if( indicesAndData ) + { + queueForDelete( indicesAndData ); + } + indicesAndData = newIndicesAndData; + allocatedSize = memToAlloc; + LeaveCriticalSection(&cs_write); +} + +#ifdef PSVITA_PRECOMPUTED_TABLE + +// AP - When called in pairs from LevelChunk::getBlockData this version of getData reduces the time from ~5.2ms to ~1.6ms on the Vita +// Gets all tile values into an array of length 32768. +void CompressedTileStorage::getData(byteArray retArray, unsigned int retOffset) +{ + unsigned short *blockIndices = (unsigned short *)indicesAndData; + unsigned char *data = indicesAndData + 1024; + + int k = 0; + unsigned char *Array = &retArray.data[retOffset]; + int *Table = CompressedTile_StorageIndexTable; + for( int i = 0; i < 512; i++ ) + { + int indexType = blockIndices[i] & INDEX_TYPE_MASK; + + int index = ( ( i & 0x180) << 6 ) | ( ( i & 0x060 ) << 4 ) | ( ( i & 0x01f ) << 2 ); + unsigned char *NewArray = &Array[index]; + + if( indexType == INDEX_TYPE_0_OR_8_BIT ) + { + if( blockIndices[i] & INDEX_TYPE_0_BIT_FLAG ) + { + unsigned char val = ( blockIndices[i] >> INDEX_TILE_SHIFT ) & INDEX_TILE_MASK; + for( int j = 0; j < 64; j++ ) + { + NewArray[Table[j]] = val; + } + } + else + { + // 8-bit reads are just directly read from the 64 long array of values stored for the block + unsigned char *packed = data + ( ( blockIndices[i] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK ); + + for( int j = 0; j < 64; j++ ) + { + NewArray[Table[j]] = packed[j]; + } + } + } + else + { + // 1, 2, or 4 bits per block packed format + + int bitspertile = 1 << indexType; // will be 1, 2 or 4 (from index values of 0, 1, 2) + int tiletypecount = 1 << bitspertile; // will be 2, 4 or 16 (from index values of 0, 1, 2) + int tiletypemask = tiletypecount - 1; // will be 1, 3 or 15 (from index values of 0, 1, 2) + int indexshift = 3 - indexType; // will be 3, 2 or 1 (from index values of 0, 1, 2) + int indexmask_bits = 7 >> indexType; // will be 7, 3 or 1 (from index values of 0, 1, 2) + int indexmask_bytes = 62 >> indexshift; // will be 7, 15 or 31 (from index values of 0, 1, 2) + + unsigned char *tile_types = data + ( ( blockIndices[i] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK ); + unsigned char *packed = tile_types + tiletypecount; + + for( int j = 0; j < 64; j++ ) + { + int idx = ( j >> indexshift ) & indexmask_bytes; + int bit = ( j & indexmask_bits ) << indexType; + NewArray[Table[j]] = tile_types[( packed[idx] >> bit ) & tiletypemask]; + } + } + } +} + +#else + +// Gets all tile values into an array of length 32768. +void CompressedTileStorage::getData(byteArray retArray, unsigned int retOffset) +{ + unsigned short *blockIndices = (unsigned short *)indicesAndData; + unsigned char *data = indicesAndData + 1024; + + for( int i = 0; i < 512; i++ ) + { + int indexType = blockIndices[i] & INDEX_TYPE_MASK; + if( indexType == INDEX_TYPE_0_OR_8_BIT ) + { + if( blockIndices[i] & INDEX_TYPE_0_BIT_FLAG ) + { + for( int j = 0; j < 64; j++ ) + { + retArray[getIndex(i,j) + retOffset] = ( blockIndices[i] >> INDEX_TILE_SHIFT ) & INDEX_TILE_MASK; + } + } + else + { + // 8-bit reads are just directly read from the 64 long array of values stored for the block + unsigned char *packed = data + ( ( blockIndices[i] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK ); + + for( int j = 0; j < 64; j++ ) + { + retArray[getIndex(i,j) + retOffset] = packed[j]; + } + } + } + else + { + // 1, 2, or 4 bits per block packed format + + int bitspertile = 1 << indexType; // will be 1, 2 or 4 (from index values of 0, 1, 2) + int tiletypecount = 1 << bitspertile; // will be 2, 4 or 16 (from index values of 0, 1, 2) + int tiletypemask = tiletypecount - 1; // will be 1, 3 or 15 (from index values of 0, 1, 2) + int indexshift = 3 - indexType; // will be 3, 2 or 1 (from index values of 0, 1, 2) + int indexmask_bits = 7 >> indexType; // will be 7, 3 or 1 (from index values of 0, 1, 2) + int indexmask_bytes = 62 >> indexshift; // will be 7, 15 or 31 (from index values of 0, 1, 2) + + unsigned char *tile_types = data + ( ( blockIndices[i] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK ); + unsigned char *packed = tile_types + tiletypecount; + + for( int j = 0; j < 64; j++ ) + { + int idx = ( j >> indexshift ) & indexmask_bytes; + int bit = ( j & indexmask_bits ) * bitspertile; + retArray[getIndex(i,j) + retOffset] = tile_types[( packed[idx] >> bit ) & tiletypemask]; + } + } + } +} + +#endif + +// Get an individual tile value +int CompressedTileStorage::get(int x, int y, int z) +{ + if(!indicesAndData) return 0; + + unsigned short *blockIndices = (unsigned short *)indicesAndData; + unsigned char *data = indicesAndData + 1024; + + int block, tile; + getBlockAndTile( &block, &tile, x, y, z ); + int indexType = blockIndices[block] & INDEX_TYPE_MASK; + + if( indexType == INDEX_TYPE_0_OR_8_BIT ) + { + if( blockIndices[block] & INDEX_TYPE_0_BIT_FLAG ) + { + // 0 bit reads are easy - the value is packed in the index + return ( blockIndices[block] >> INDEX_TILE_SHIFT ) & INDEX_TILE_MASK; + } + else + { + // 8-bit reads are just directly read from the 64 long array of values stored for the block + unsigned char *packed = data + ( ( blockIndices[block] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK ); + return packed[tile]; + } + } + else + { + int bitspertile = 1 << indexType; // will be 1, 2 or 4 (from index values of 0, 1, 2) + int tiletypecount = 1 << bitspertile; // will be 2, 4 or 16 (from index values of 0, 1, 2) + int tiletypemask = tiletypecount - 1; // will be 1, 3 or 15 (from index values of 0, 1, 2) + int indexshift = 3 - indexType; // will be 3, 2 or 1 (from index values of 0, 1, 2) + int indexmask_bits = 7 >> indexType; // will be 7, 3 or 1 (from index values of 0, 1, 2) + int indexmask_bytes = 62 >> indexshift; // will be 7, 15 or 31 (from index values of 0, 1, 2) + + unsigned char *tile_types = data + ( ( blockIndices[block] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK ); + unsigned char *packed = tile_types + tiletypecount; + int idx = ( tile >> indexshift ) & indexmask_bytes; + int bit = ( tile & indexmask_bits ) * bitspertile; + return tile_types[( packed[idx] >> bit ) & tiletypemask]; + } + return 0; +} + +// Set an individual tile value +void CompressedTileStorage::set(int x, int y, int z, int val) +{ + EnterCriticalSection(&cs_write); + assert(val !=255 ); + int block, tile; + getBlockAndTile( &block, &tile, x, y, z ); + + // 2 passes - first pass will try and store within the current levels of compression, then if that fails will + // upgrade the block we are writing to (so more bits can be stored) to achieve the storage required + for( int pass = 0; pass < 2; pass++ ) + { + unsigned short *blockIndices = (unsigned short *)indicesAndData; + unsigned char *data = indicesAndData + 1024; + + int indexType = blockIndices[block] & INDEX_TYPE_MASK; + + if( indexType == INDEX_TYPE_0_OR_8_BIT ) + { + if( blockIndices[block] & INDEX_TYPE_0_BIT_FLAG ) + { + // 0 bits - if its the value already, we're done, otherwise continue on to upgrade storage + if ( val == ( ( blockIndices[block] >> INDEX_TILE_SHIFT ) & INDEX_TILE_MASK ) ) + { + LeaveCriticalSection(&cs_write); + return; + } + } + else + { + // 8 bits - just store directly and we're done + unsigned char *packed = data + ( ( blockIndices[block] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK ); + packed[ tile ] = val; + LeaveCriticalSection(&cs_write); + return; + } + } + else + { + int bitspertile = 1 << indexType; // will be 1, 2 or 4 (from index values of 0, 1, 2) + int tiletypecount = 1 << bitspertile; // will be 2, 4 or 16 (from index values of 0, 1, 2) + int tiletypemask = tiletypecount - 1; // will be 1, 3 or 15 (from index values of 0, 1, 2) + int indexshift = 3 - indexType; // will be 3, 2 or 1 (from index values of 0, 1, 2) + int indexmask_bits = 7 >> indexType; // will be 7, 3 or 1 (from index values of 0, 1, 2) + int indexmask_bytes = 62 >> indexshift; // will be 7, 15 or 31 (from index values of 0, 1, 2) + + unsigned char *tile_types = data + ( ( blockIndices[block] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK ); + + for( int i = 0; i < tiletypecount; i++ ) + { + if( ( tile_types[i] == val ) || ( tile_types[i] == 255 ) ) + { + tile_types[i] = val; + unsigned char *packed = tile_types + tiletypecount; + int idx = ( tile >> indexshift ) & indexmask_bytes; + int bit = ( tile & indexmask_bits ) * bitspertile; + packed[idx] &= ~( tiletypemask << bit ); + packed[idx] |= i << bit; + LeaveCriticalSection(&cs_write); + return; + } + } + } + if( pass == 0 ) + { + compress(block); + } + }; + LeaveCriticalSection(&cs_write); +} + +// Sets a region of tile values with the data at offset position in the array dataIn - external ordering compatible with java DataLayer +int CompressedTileStorage::setDataRegion(byteArray dataIn, int x0, int y0, int z0, int x1, int y1, int z1, int offset, tileUpdatedCallback callback, void *param, int yparam) +{ + unsigned char *pucIn = &dataIn.data[offset]; + + if( callback ) + { + for( int x = x0; x < x1; x++ ) + { + for( int z = z0; z < z1; z++ ) + { + for( int y = y0; y < y1; y++ ) + { + if(get(x, y, z) != *pucIn) + { + set(x, y, z, *pucIn); + callback(x, y, z, param, yparam); + } + pucIn++; + } + } + } + } + else + { + for( int x = x0; x < x1; x++ ) + { + for( int z = z0; z < z1; z++ ) + { + for( int y = y0; y < y1; y++ ) + { + set(x, y, z, *pucIn++); + } + } + } + } + ptrdiff_t count = pucIn - &dataIn.data[offset]; + + return (int)count; +} + +// Tests whether setting data would actually change anything +bool CompressedTileStorage::testSetDataRegion(byteArray dataIn, int x0, int y0, int z0, int x1, int y1, int z1, int offset) +{ + unsigned char *pucIn = &dataIn.data[offset]; + for( int x = x0; x < x1; x++ ) + { + for( int z = z0; z < z1; z++ ) + { + for( int y = y0; y < y1; y++ ) + { + if(get(x, y, z) != *pucIn++) + { + return true; + } + } + } + } + return false; +} + +// Updates the data at offset position dataInOut with a region of tile information - external ordering compatible with java DataLayer +int CompressedTileStorage::getDataRegion(byteArray dataInOut, int x0, int y0, int z0, int x1, int y1, int z1, int offset) +{ + unsigned char *pucOut = &dataInOut.data[offset]; + for( int x = x0; x < x1; x++ ) + { + for( int z = z0; z < z1; z++ ) + { + for( int y = y0; y < y1; y++ ) + { + *pucOut++ = get(x, y, z); + } + } + } + ptrdiff_t count = pucOut - &dataInOut.data[offset]; + + return (int)count; +} + +void CompressedTileStorage::staticCtor() +{ + InitializeCriticalSectionAndSpinCount(&cs_write, 5120); + for( int i = 0; i < 3; i++ ) + { + deleteQueue[i].Initialize(); + } +} + +void CompressedTileStorage::queueForDelete(unsigned char *data) +{ + // Add this into a queue for deleting. This shouldn't be actually deleted until tick has been called twice from when + // the data went into the queue. + if( data ) + { + deleteQueue[deleteQueueIndex].Push( data ); + } +} + +void CompressedTileStorage::tick() +{ + // We have 3 queues for deleting. Always delete from the next one after where we are writing to, so it should take 2 ticks + // before we ever delete something, from when the request to delete it came in + int freeIndex = ( deleteQueueIndex + 1 ) % 3; + +// printf("Free queue: %d, %d\n",deleteQueue[freeIndex].GetEntryCount(),deleteQueue[freeIndex].GetAllocated()); + unsigned char *toFree = NULL; + do + { + toFree = deleteQueue[freeIndex].Pop(); +// if( toFree ) printf("Deleting 0x%x\n", toFree); +#if 1 + if( toFree ) XPhysicalFree(toFree); +#else + // Determine correct means to free this data - could have been allocated either with XPhysicalAlloc or malloc + if( (unsigned int)toFree >= MM_PHYSICAL_4KB_BASE ) + { + XPhysicalFree(toFree); + } + else + { + free(toFree); + } +#endif + } while( toFree ); + + deleteQueueIndex = ( deleteQueueIndex + 1 ) % 3; +} + +#ifdef __PS3__ +void CompressedTileStorage::compress_SPU(int upgradeBlock/*=-1*/) +{ + EnterCriticalSection(&cs_write); + static unsigned char compBuffer[32768+4096] __attribute__((__aligned__(16))); + CompressedTileStorage_compress_dataIn& dataIn = g_compressTileDataIn[0]; + dataIn.allocatedSize = allocatedSize; + dataIn.indicesAndData = indicesAndData; + dataIn.newIndicesAndData = compBuffer; + dataIn.upgradeBlock = upgradeBlock; + + static C4JSpursJobQueue::Port p("CompressedTileStorage::compress_SPU"); + C4JSpursJob_CompressedTileStorage_compress compressJob(&dataIn); + p.submitJob(&compressJob); + p.waitForCompletion(); + + if(dataIn.neededCompressed) + { + unsigned char *newIndicesAndData = (unsigned char *)XPhysicalAlloc(dataIn.newAllocatedSize, MAXULONG_PTR, 4096, PAGE_READWRITE);//(unsigned char *)malloc( memToAlloc ); + memcpy(newIndicesAndData, compBuffer, dataIn.newAllocatedSize); + queueForDelete( indicesAndData ); + indicesAndData = newIndicesAndData; + allocatedSize = dataIn.newAllocatedSize; + } + + LeaveCriticalSection(&cs_write); + +} +#endif + +// Compresses the data currently stored in one of two ways: +// (1) Attempt to compresses every block as much as possible (if upgradeBlock is -1) +// (2) Copy all blocks as-is apart from the block specified by upgradeBlock ( if > -1 ), which is changed to be the next-most-accomodating storage from its current state +void CompressedTileStorage::compress(int upgradeBlock/*=-1*/) +{ +#if defined __PS3__ && !defined DISABLE_SPU_CODE + compress_SPU(upgradeBlock); + return; +#endif + + unsigned char tempdata[64]; + unsigned short _blockIndices[512]; + + // If this is already fully compressed, early out + if( ( allocatedSize == 1024 ) && ( upgradeBlock == -1 ) ) return; + + bool needsCompressed = ( upgradeBlock > -1 ); // If an upgrade block is specified, we'll always need to recompress - otherwise default to false + + EnterCriticalSection(&cs_write); + + unsigned short *blockIndices = (unsigned short *)indicesAndData; + unsigned char *data = indicesAndData + 1024; + + int memToAlloc = 0; + for( int i = 0; i < 512; i++ ) + { + unsigned short indexType = blockIndices[i] & INDEX_TYPE_MASK; + + unsigned char *unpacked_data = NULL; + unsigned char *packed_data; + + // First task is to find out what type of storage each block needs. Need to unpack each where required. + // Note that we don't need to fully unpack the data at this stage since we are only interested in working out how many unique types of tiles are in each block, not + // what those actual tile ids are. + if( upgradeBlock == -1 ) + { + if( indexType == INDEX_TYPE_0_OR_8_BIT ) + { + // Note that we are only interested in data that can be packed further, so we don't need to consider things that are already at their most compressed + // (ie with INDEX_TYPE_0_BIT_FLAG set) + if( ( blockIndices[i] & INDEX_TYPE_0_BIT_FLAG ) == 0 ) + { + unpacked_data = data + ( ( blockIndices[i] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK ); + } + } + else + { + int bitspertile = 1 << indexType; // will be 1, 2 or 4 (from index values of 0, 1, 2) + int tiletypecount = 1 << bitspertile; // will be 2, 4 or 16 + int tiletypemask = tiletypecount - 1; // will be 1, 3 or 15 + int indexshift = 3 - indexType; // will be 3, 2 or 1 (from index values of 0, 1, 2) + int indexmask_bits = 7 >> indexType; // will be 7, 3 or 1 (from index values of 0, 1, 2) + int indexmask_bytes = 62 >> indexshift; // will be 7, 15 or 31 (from index values of 0, 1, 2) + + unpacked_data = tempdata; + packed_data = data + ( ( blockIndices[i] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK ) + tiletypecount; + + for( int j = 0; j < 64; j++ ) + { + int idx = (j >> indexshift) & indexmask_bytes; + int bit = ( j & indexmask_bits ) * bitspertile; + + unpacked_data[j] = ( packed_data[idx] >> bit ) & tiletypemask; // Doesn't need the actual data for each tile, just unique values + } + } + + if( unpacked_data ) + { + // Now count how many unique tile types are in the block - if unpacked_data isn't set then there isn't any data so we can't compress any further and require no storage. + // Store flags for each tile type used in an array of 4 64-bit flags. + +#ifdef __PSVITA__ + // AP - Vita isn't so great at shifting 64bits. The top biggest CPU time sink after profiling is __ashldi3 (64bit shift) at 3% + // lets use 32bit values instead + unsigned int usedFlags[8] = {0,0,0,0,0,0,0,0}; + __int32 i32_1 = 1; + for( int j = 0; j < 64; j++ ) // This loop of 64 is to go round the 4x4x4 tiles in the block + { + int tiletype = unpacked_data[j]; + usedFlags[tiletype & 7] |= ( i32_1 << ( tiletype >> 3 ) ); + } + // count the number of bits set using the 'Hammering Weight' method. This reduces ::compress total thread cpu consumption from 10% to 4% + unsigned int count = 0; + for( int Index = 0;Index < 8;Index += 1 ) + { + unsigned int i = usedFlags[Index]; + i = i - ((i >> 1) & 0x55555555); + i = (i & 0x33333333) + ((i >> 2) & 0x33333333); + count += (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; + } +#else + + __uint64 usedFlags[4] = {0,0,0,0}; + __int64 i64_1 = 1; // MGH - instead of 1i64, which is MS specific + for( int j = 0; j < 64; j++ ) // This loop of 64 is to go round the 4x4x4 tiles in the block + { + int tiletype = unpacked_data[j]; + usedFlags[tiletype & 3] |= ( i64_1 << ( tiletype >> 2 ) ); + } + int count = 0; + for( int tiletype = 0; tiletype < 256; tiletype++ ) // This loop of 256 is to go round the 256 possible values that the tiles might have had to find how many are actually used + { + if( usedFlags[tiletype & 3] & ( i64_1 << ( tiletype >> 2 ) ) ) + { + count++; + } + } +#endif + + if( count == 1 ) + { + _blockIndices[i] = INDEX_TYPE_0_OR_8_BIT | INDEX_TYPE_0_BIT_FLAG; + + // We'll need to compress if this isn't the same type as before. If it *was* a 0-bit one though, then unpacked_data wouldn't have been set and we wouldn't be here + needsCompressed = true; + } + else if( count == 2 ) + { + _blockIndices[i] = INDEX_TYPE_1_BIT; + if( indexType != INDEX_TYPE_1_BIT ) needsCompressed = true; + memToAlloc += 10; // 8 bytes + 2 tile index + } + else if ( count <= 4 ) + { + _blockIndices[i] = INDEX_TYPE_2_BIT; + if( indexType != INDEX_TYPE_2_BIT ) needsCompressed = true; + memToAlloc += 20; // 16 bytes + 4 tile index + } + else if ( count <= 16 ) + { + _blockIndices[i] = INDEX_TYPE_4_BIT; + if( indexType != INDEX_TYPE_4_BIT ) needsCompressed = true; + memToAlloc += 48; // 32 bytes + 16 tile index + } + else + { + _blockIndices[i] = INDEX_TYPE_0_OR_8_BIT; + memToAlloc = ( memToAlloc + 3 ) & 0xfffc; // Make sure we are 4-byte aligned for 8-bit storage + memToAlloc += 64; + } + } + else + { + // Already will be 0 bits, so we can't do any further compression - just copy the index over. + _blockIndices[i] = blockIndices[i]; + } + } + else + { + if( i == upgradeBlock ) + { + // INDEX_TYPE_1_BIT (0) -> INDEX_TYPE_2_BIT (1) + // INDEX_TYPE_2_BIT (1) -> INDEX_TYPE_4_BIT (2) + // INDEX_TYPE_4_BIT (2) -> INDEX_TYPE_0_OR_8_BIT (3) (new will be 8-bit) + // INDEX_TYPE_0_OR_8_BIT (3) -> INDEX_TYPE_1_BIT (0) (assuming old was 0-bit) + _blockIndices[i] = ( ( blockIndices[i] & INDEX_TYPE_MASK ) + 1 ) & INDEX_TYPE_MASK; + } + else + { + // Copy over the index, without the offset. + _blockIndices[i] = blockIndices[i] & INDEX_TYPE_MASK; + if( _blockIndices[i] == INDEX_TYPE_0_OR_8_BIT ) + { + _blockIndices[i] |= ( blockIndices[i] & INDEX_TYPE_0_BIT_FLAG ); + } + } + switch(_blockIndices[i]) + { + case INDEX_TYPE_1_BIT: + memToAlloc += 10; + break; + case INDEX_TYPE_2_BIT: + memToAlloc += 20; + break; + case INDEX_TYPE_4_BIT: + memToAlloc += 48; + break; + case INDEX_TYPE_0_OR_8_BIT: + memToAlloc = ( memToAlloc + 3 ) & 0xfffc; // Make sure we are 4-byte aligned for 8-bit storage + memToAlloc += 64; + break; + // Note that INDEX_TYPE_8_BIT|INDEX_TYPE_0_BIT_FLAG not in here as it doesn't need any further allocation + } + } + } + + // If we need to do something here, then lets allocate some memory + if( needsCompressed ) + { + memToAlloc += 1024; // For the indices + unsigned char *newIndicesAndData = (unsigned char *)XPhysicalAlloc(memToAlloc, MAXULONG_PTR, 4096, PAGE_READWRITE);//(unsigned char *)malloc( memToAlloc ); + if( newIndicesAndData == NULL ) + { + DWORD lastError = GetLastError(); +#ifndef _DURANGO + MEMORYSTATUS memStatus; + GlobalMemoryStatus(&memStatus); + __debugbreak(); +#endif + } + unsigned char *pucData = newIndicesAndData + 1024; + unsigned short usDataOffset = 0; + unsigned short *newIndices = (unsigned short *) newIndicesAndData; + + // Now pass through again actually making the final compressed data + for( int i = 0; i < 512; i++ ) + { + unsigned short indexTypeNew = _blockIndices[i] & INDEX_TYPE_MASK; + unsigned short indexTypeOld = blockIndices[i] & INDEX_TYPE_MASK; + newIndices[i] = indexTypeNew; + + // Is the type unmodifed? Then can just copy over + bool done = false; + if( indexTypeOld == indexTypeNew ) + { + unsigned char *packed_data; + if( indexTypeOld == INDEX_TYPE_0_OR_8_BIT ) + { + if( ( blockIndices[i] & INDEX_TYPE_0_BIT_FLAG ) == ( _blockIndices[i] & INDEX_TYPE_0_BIT_FLAG ) ) + { + if( blockIndices[i] & INDEX_TYPE_0_BIT_FLAG ) + { + newIndices[i] = blockIndices[i]; + } + else + { + packed_data = data + ( ( blockIndices[i] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK); + usDataOffset = (usDataOffset + 3 ) & 0xfffc; + XMemCpy( pucData + usDataOffset, packed_data, 64 ); + newIndices[i] |= ( usDataOffset & INDEX_OFFSET_MASK) << INDEX_OFFSET_SHIFT; + usDataOffset += 64; + } + done = true; + } + } + else + { + packed_data = data + ( ( blockIndices[i] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK); + + int dataSize = 8 << indexTypeOld; // 8, 16 or 32 bytes of per-tile storage + dataSize += 1 << ( 1 << indexTypeOld ); // 2, 4 or 16 bytes to store each tile type + newIndices[i] |= ( usDataOffset & INDEX_OFFSET_MASK) << INDEX_OFFSET_SHIFT; + XMemCpy( pucData + usDataOffset, packed_data, dataSize ); + usDataOffset += dataSize; + done = true; + } + } + + + // If we're not done, then we actually need to recompress this block. First of all decompress from its current format. + if( !done ) + { + unsigned char *unpacked_data = NULL; + unsigned char *tile_types = NULL; + unsigned char *packed_data = NULL; + if( indexTypeOld == INDEX_TYPE_0_OR_8_BIT ) + { + if( blockIndices[i] & INDEX_TYPE_0_BIT_FLAG ) + { + unpacked_data = tempdata; + int value = ( blockIndices[i] >> INDEX_TILE_SHIFT ) & INDEX_TILE_MASK; + XMemSet( tempdata, value, 64 ); + } + else + { + unpacked_data = data + ( ( blockIndices[i] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK ); + } + } + else + { + int bitspertile = 1 << indexTypeOld; // will be 1, 2 or 4 (from index values of 0, 1, 2) + int tiletypecount = 1 << bitspertile; // will be 2, 4 or 16 + int tiletypemask = tiletypecount - 1; // will be 1, 3 or 15 + int indexshift = 3 - indexTypeOld; // will be 3, 2 or 1 (from index values of 0, 1, 2) + int indexmask_bits = 7 >> indexTypeOld; // will be 7, 3 or 1 (from index values of 0, 1, 2) + int indexmask_bytes = 62 >> indexshift; // will be 7, 15 or 31 (from index values of 0, 1, 2) + + unpacked_data = tempdata; + tile_types = data + ( ( blockIndices[i] >> INDEX_OFFSET_SHIFT ) & INDEX_OFFSET_MASK ); + packed_data = tile_types + tiletypecount; + for( int j = 0; j < 64; j++ ) + { + int idx = ( j >> indexshift ) & indexmask_bytes; + int bit = ( j & indexmask_bits ) * bitspertile; + + unpacked_data[j] = tile_types[(packed_data[idx] >> bit) & tiletypemask]; + } + } + + // And finally repack + unsigned char ucMappings[256] = {0}; +#ifdef __PSVITA__ + memset(ucMappings, 255, 256); +#else + for( int j = 0; j < 256; j++ ) + { + ucMappings[j] = 255; + } +#endif + + unsigned char *repacked = NULL; + + if( indexTypeNew == INDEX_TYPE_0_OR_8_BIT ) + { + if( _blockIndices[i] & INDEX_TYPE_0_BIT_FLAG ) + { + newIndices[i] = INDEX_TYPE_0_OR_8_BIT | INDEX_TYPE_0_BIT_FLAG | (((unsigned short)unpacked_data[0]) << INDEX_TILE_SHIFT); + } + else + { + usDataOffset = (usDataOffset + 3 ) & 0xfffc; // Make sure offset is 4 byte aligned + XMemCpy( pucData + usDataOffset, unpacked_data, 64 ); + newIndices[i] |= ( usDataOffset & INDEX_OFFSET_MASK) << INDEX_OFFSET_SHIFT; + usDataOffset += 64; + } + } + else + { + int bitspertile = 1 << indexTypeNew; // will be 1, 2 or 4 (from index values of 0, 1, 2) + int tiletypecount = 1 << bitspertile; // will be 2, 4 or 16 (from index values of 0, 1, 2) + int tiletypemask = tiletypecount - 1; // will be 1, 3 or 15 (from index values of 0, 1, 2) + int tiledatasize = 8 << indexTypeNew; // will be 8, 16 or 32 (from index values of 0, 1, 2) + int indexshift = 3 - indexTypeNew; // will be 3, 2 or 1 (from index values of 0, 1, 2) + int indexmask_bits = 7 >> indexTypeNew; // will be 7, 3 or 1 (from index values of 0, 1, 2) + int indexmask_bytes = 62 >> indexshift; // will be 7, 15 or 31 (from index values of 0, 1, 2) + + tile_types = pucData + usDataOffset; + repacked = tile_types + tiletypecount; + XMemSet(tile_types, 255, tiletypecount); + XMemSet(repacked, 0,tiledatasize); + newIndices[i] |= ( usDataOffset & INDEX_OFFSET_MASK) << INDEX_OFFSET_SHIFT; + usDataOffset += tiletypecount + tiledatasize; + int count = 0; + for( int j = 0; j < 64; j++ ) + { + int tile = unpacked_data[j]; + if( ucMappings[tile] == 255 ) + { + ucMappings[tile] = count; + tile_types[count++] = tile; + } + int idx = (j >> indexshift) & indexmask_bytes; + int bit = ( j & indexmask_bits ) * bitspertile; + repacked[idx] |= ucMappings[tile] << bit; + } + } + } + } + + queueForDelete( indicesAndData ); + indicesAndData = newIndicesAndData; + allocatedSize = memToAlloc; + } + LeaveCriticalSection(&cs_write); +} + +int CompressedTileStorage::getAllocatedSize(int *count0, int *count1, int *count2, int *count4, int *count8) +{ + *count0 = 0; + *count1 = 0; + *count2 = 0; + *count4 = 0; + *count8 = 0; + + unsigned short *blockIndices = (unsigned short *)indicesAndData; + for(int i = 0; i < 512; i++ ) + { + unsigned short idxType = blockIndices[i] & INDEX_TYPE_MASK; + if( idxType == INDEX_TYPE_1_BIT ) + { + (*count1)++; + } + else if( idxType == INDEX_TYPE_2_BIT ) + { + (*count2)++; + } + else if( idxType == INDEX_TYPE_4_BIT ) + { + (*count4)++; + } + else if( idxType == INDEX_TYPE_0_OR_8_BIT ) + { + if( blockIndices[i] & INDEX_TYPE_0_BIT_FLAG ) + { + (*count0)++; + } + else + { + (*count8)++; + } + } + } + return allocatedSize; +} + +int CompressedTileStorage::getHighestNonEmptyY() +{ + unsigned short *blockIndices = (unsigned short *)indicesAndData; + unsigned int highestYBlock = 0; + bool found = false; + + // The 512 "blocks" (4x4x4 tiles) are arranged in 32 layers + for(int yBlock = 31; yBlock >= 0; --yBlock) + { + // Each layer has 16 blocks + for(unsigned int xzBlock = 0; xzBlock < 16; ++xzBlock) + { + // Blocks are ordered in columns + int index = yBlock + (xzBlock * 32); + + int indexType = blockIndices[index] & INDEX_TYPE_MASK; + if( indexType == INDEX_TYPE_0_OR_8_BIT && blockIndices[index] & INDEX_TYPE_0_BIT_FLAG ) + { + int val = ( blockIndices[index] >> INDEX_TILE_SHIFT ) & INDEX_TILE_MASK; + if(val != 0) + { + highestYBlock = yBlock; + found = true; + break; + } + } + else + { + highestYBlock = yBlock; + found = true; + break; + } + } + + if(found) break; + } + + int highestNonEmptyY = -1; + if(found) + { + // Multiply by the number of vertical tiles in a block, and then add that again to be at the top of the block + highestNonEmptyY = (highestYBlock * 4) + 4; + } + return highestNonEmptyY; +} + +void CompressedTileStorage::write(DataOutputStream *dos) +{ + dos->writeInt(allocatedSize); + if(indicesAndData) + { + if(LOCALSYTEM_ENDIAN == BIGENDIAN) + { + // The first 1024 bytes are an array of shorts, so we need to reverse the endianness + byteArray indicesCopy(1024); + memcpy(indicesCopy.data, indicesAndData, 1024); + reverseIndices(indicesCopy.data); + dos->write(indicesCopy); + delete [] indicesCopy.data; + + // Write the rest of the data + if(allocatedSize > 1024) + { + byteArray dataWrapper(indicesAndData + 1024, allocatedSize - 1024); + dos->write(dataWrapper); + } + } + else + { + byteArray wrapper(indicesAndData, allocatedSize); + dos->write(wrapper); + } + } +} + +void CompressedTileStorage::read(DataInputStream *dis) +{ + allocatedSize = dis->readInt(); + if(allocatedSize > 0) + { + // This delete should be safe to do in a non-thread safe way as the chunk is fully read before any external reference is available to it from another thread + if( indicesAndData ) + { + XPhysicalFree(indicesAndData); + } + indicesAndData = (unsigned char *)XPhysicalAlloc(allocatedSize, MAXULONG_PTR, 4096, PAGE_READWRITE); + + byteArray wrapper(indicesAndData, allocatedSize); + dis->readFully(wrapper); + if(LOCALSYTEM_ENDIAN == BIGENDIAN) + { + reverseIndices(indicesAndData); + } + + compress(); + } +} + +void CompressedTileStorage::reverseIndices(unsigned char *indices) +{ + unsigned short *blockIndices = (unsigned short *)indices; + for( int i = 0; i < 512; i++ ) + { + System::ReverseUSHORT(&blockIndices[i]); + } +} \ No newline at end of file diff --git a/Minecraft.World/CompressedTileStorage.h b/Minecraft.World/CompressedTileStorage.h new file mode 100644 index 00000000..3d433547 --- /dev/null +++ b/Minecraft.World/CompressedTileStorage.h @@ -0,0 +1,111 @@ +#pragma once +#include "xmcore.h" + +// This class is used for the compressed storage of tile data. Unlike the SparseLightingStorage class, data is split into 512 blocks of 4x4x4 tiles. Then within each block, the +// data is compressed as described below, with a selection of bits per tile available, in a method similar to a palettised image. + +// There are two elements to the storage... an index array (fixed size), and the actual storage required... + +// The index: +// (1) Each index takes up 2 bytes, one for each 4x4x4 block ie 512 X 2 = 1024 bytes in total +// (2) The type of index is determined by the least significant 2 bits, the other 14 bits represent an offset for the data, stored divided by 2 +// 0 - the data for this block is represented at 1 bit per tile. Data pointed to is 2 bytes describing the 2 possible tiles stored in this block, followed by 32 bytes of data (total 34 bytes) +// 1 - the data for this block is represented at 2 bit per tile. Data pointed to is 4 bytes describing the 4 possible tiles stored in this block, followed by 64 bytes of data (total 68 bytes) +// 2 - the data for this block is represented at 4 bit per tile. Data pointed to is 16 bytes describing the 16 possible tiles stored in this block, followed by 128 bytes of data (total 144 bytes) +// 3 - if bit 2 is 0, then this block is represented at 8 bits per tile. Data pointed to is 64 bytes, offset must be a multiple of 4 (since bit 2 can also be thought of as being +// the low bit of the offset (divided by 2 as in the other cases), and is zero) +// - if bit 2 is 1, then this block is represented at 0 bits per tile. The upper 8 bits of the index store the tile value that is used by the entire block. +// So: +// oooooooooooooo00 - 1 bit per tile, offset oooooooooooooo0 +// oooooooooooooo01 - 2 bits per tile, offset oooooooooooooo0 +// oooooooooooooo10 - 4 bits per tile, offset oooooooooooooo0 +// ooooooooooooo011 - 8 bits per tile, offset ooooooooooooo00 +// tttttttt-----111 - 0 bits per tile - tile is tttttttt + +// Some notes on the logic of all of this... +// (1) Large numbers of blocks in the world really don't need to be stored at a full 8 bits per tile. +// In a worst-case scenario, all planes would be 256 bytes and we'd have to store offsets of up to 32704 ( 64 x 511). This would require 15 bits per offset to store, but since in all cases +// the data can be stored with a 2 byte alignment, we can store offsets divided by 2, freeing up 2 bits to store the type of index for each plane. This allows us to encode 4 types, but we really have +// 5 types (0, 1, 2, 4 or 8 bits per tile). Since the 8-bit per tile planes are likely to be very rare, we can free up an extra bit in those by making their offset 4-byte aligned, and +// then use the extra bit to determine whether its a 0 or 8-bit per tile index. In the 0 bit case, we can use the bits used for the offset to store the actual tile value represented throughout the plane. +// (2) The compression is done per 4x4x4 block rather than planes like the lighting, as that gives many more regions that have a small number of tile types than per plane, and can therefore +// be compressed using less bits per tile. This is at the expense of a larger index, and more overhead from storing the tile types in each block (since there are more blocks than planes). However +// on balance this still was found to give much better compression - around 12.5% vs 19% by doing things per plane. +// (3) Another compromise is being made on how the memory is allocated. This is all currently done with physical allocations to bypass the general heap manager, in particular to allow the freeing +// of memory to actually free whole memory pages cleanly rather than leaving them as managed by the heap manager. The downside to this is that all storage is done in whole 4K pages. Annoyingly, +// a lot of our compressed chunks are just on the edge of fitting in 4K, so an awful lot of them end up being 8K when they are just a small amount over. However, in testing absolutely no chunks +// were seen that got close to going over 8K compressed, so doing things this way then we at least know that we are reliably getting 25% compression, and freeing things up cleanly. +// Note: see the comments on the getIndex and getBlockAndTile for an explanation of how the blocks themselves are organised in terms of mapping a chunk-wide x/y/z into a block and tile index. + +//#define BLOCK_COMPRESSION_STATS +class TileCompressData_SPU; +class CompressedTileStorage +{ + friend class TileCompressData_SPU; +private: + unsigned char *indicesAndData; +public: + int allocatedSize; +private: + + static const int INDEX_OFFSET_MASK = 0x7ffe; + static const int INDEX_OFFSET_SHIFT = 1; + static const int INDEX_TILE_MASK = 0x00ff; + static const int INDEX_TILE_SHIFT = 8; + static const int INDEX_TYPE_MASK = 0x0003; + static const int INDEX_TYPE_1_BIT = 0x0000; + static const int INDEX_TYPE_2_BIT = 0x0001; + static const int INDEX_TYPE_4_BIT = 0x0002; + static const int INDEX_TYPE_0_OR_8_BIT = 0x0003; + static const int INDEX_TYPE_0_BIT_FLAG = 0x0004; + + static const unsigned int MM_PHYSICAL_4KB_BASE = 0xE0000000; // Start of where 4KB page sized physical allocations are made +public: + CompressedTileStorage(); + CompressedTileStorage(CompressedTileStorage *copyFrom); // ctor with deep copy + CompressedTileStorage(byteArray dataIn, unsigned int initOffset); // Construct with data in passed in array of length 32768 (128 x 16 x 16) + CompressedTileStorage(bool isEmpty); + ~CompressedTileStorage(); + bool isSameAs(CompressedTileStorage *other); + bool isRenderChunkEmpty(int y); // Determine if 16x16x16 render-sized chunk is actually empty +private: + inline static int getIndex(int block, int tile); + inline static void getBlockAndTile(int *block, int *tile, int x, int y, int z); + inline static void getBlock(int *block, int x, int y, int z); +public: + void setData(byteArray dataIn, unsigned int inOffset); // Set all tile values from a data array of length 32768 (128 x 16 x 16). + void getData(byteArray retArray, unsigned int retOffset); // Gets all tile values into an array of length 32768. + int get(int x, int y, int z); // Get an individual tile value + void set(int x, int y, int z, int val); // Set an individual tile value + typedef void (*tileUpdatedCallback)(int x, int y , int z, void *param, int yparam); + int setDataRegion(byteArray dataIn, int x0, int y0, int z0, int x1, int y1, int z1, int offset, tileUpdatedCallback callback, void *param, int yparam); // Sets a region of tile values with the data at offset position in the array dataIn - external ordering compatible with java DataLayer + bool testSetDataRegion(byteArray dataIn, int x0, int y0, int z0, int x1, int y1, int z1, int offset); // Tests whether setting data would actually change anything + int getDataRegion(byteArray dataInOut, int x0, int y0, int z0, int x1, int y1, int z1, int offset); // Updates the data at offset position dataInOut with a region of tile information - external ordering compatible with java DataLayer + + static void staticCtor(); + + void compress(int upgradeBlock = -1); +#ifdef __PS3__ + void compress_SPU(int upgradeBlock = -1); +#endif + +public: + void queueForDelete(unsigned char *data); + + static void tick(); + static int deleteQueueIndex; + static XLockFreeStack deleteQueue[3]; + + static unsigned char compressBuffer[32768 + 256]; + + static CRITICAL_SECTION cs_write; + + int getAllocatedSize(int *count0, int *count1, int *count2, int *count4, int *count8); + int getHighestNonEmptyY(); + bool isCompressed(); + + void write(DataOutputStream *dos); + void read(DataInputStream *dis); + void reverseIndices(unsigned char *indices); +}; + diff --git a/Minecraft.World/Connection.cpp b/Minecraft.World/Connection.cpp new file mode 100644 index 00000000..4392fe54 --- /dev/null +++ b/Minecraft.World/Connection.cpp @@ -0,0 +1,675 @@ +#include "stdafx.h" +#include "InputOutputStream.h" +#include "Socket.h" +#include "Connection.h" +#include "ThreadName.h" +#include "compression.h" +#include "..\Minecraft.Client\PS3\PS3Extras\ShutdownManager.h" + +// This should always be enabled, except for debugging use +#ifndef _DEBUG +#define CONNECTION_ENABLE_TIMEOUT_DISCONNECT 1 +#endif + +int Connection::readThreads = 0; +int Connection::writeThreads = 0; + +int Connection::readSizes[256]; +int Connection::writeSizes[256]; + + + +void Connection::_init() +{ +// printf("Con:0x%x init\n",this); + InitializeCriticalSection(&writeLock); + InitializeCriticalSection(&threadCounterLock); + InitializeCriticalSection(&incoming_cs); + + running = true; + quitting = false; + disconnected = false; + disconnectReason = DisconnectPacket::eDisconnect_None; + noInputTicks = 0; + estimatedRemaining = 0; + fakeLag = 0; + slowWriteDelay = 50; + + saqThreadID = 0; + closeThreadID = 0; + + tickCount = 0; + +} + +// 4J Jev, need to delete the critical section. +Connection::~Connection() +{ + // 4J Stu - Just to be sure, make sure the read and write threads terminate themselves before the connection object is destroyed + running = false; + if( dis ) dis->close(); // The input stream needs closed before the readThread, or the readThread + // may get stuck whilst blocking waiting on a read + readThread->WaitForCompletion(INFINITE); + writeThread->WaitForCompletion(INFINITE); + + DeleteCriticalSection(&writeLock); + DeleteCriticalSection(&threadCounterLock); + DeleteCriticalSection(&incoming_cs); + + delete m_hWakeReadThread; + delete m_hWakeWriteThread; + + // These should all have been destroyed in close() but no harm in checking again + delete byteArrayDos; + byteArrayDos = NULL; + delete baos; + baos = NULL; + if( bufferedDos ) + { + bufferedDos->deleteChildStream(); + delete bufferedDos; + bufferedDos = NULL; + } + delete dis; + dis = NULL; +} + +Connection::Connection(Socket *socket, const wstring& id, PacketListener *packetListener) // throws IOException +{ + _init(); + + this->socket = socket; + + address = socket->getRemoteSocketAddress(); + + this->packetListener = packetListener; + + //try { + socket->setSoTimeout(30000); + socket->setTrafficClass(IPTOS_THROUGHPUT | IPTOS_LOWDELAY); + + /* 4J JEV no catch + } catch (SocketException e) { + // catching this exception because it (apparently?) causes problems + // on OSX Tiger + System.err.println(e.getMessage()); + }*/ + + dis = new DataInputStream(socket->getInputStream(packetListener->isServerPacketListener())); + + sos = socket->getOutputStream(packetListener->isServerPacketListener()); + bufferedDos = new DataOutputStream(new BufferedOutputStream(sos, SEND_BUFFER_SIZE)); + baos = new ByteArrayOutputStream( SEND_BUFFER_SIZE ); + byteArrayDos = new DataOutputStream(baos); + + m_hWakeReadThread = new C4JThread::Event; + m_hWakeWriteThread = new C4JThread::Event; + + const char *szId = wstringtofilename(id); + char readThreadName[256]; + char writeThreadName[256]; + sprintf(readThreadName,"%s read\n",szId); + sprintf(writeThreadName,"%s write\n",szId); + + readThread = new C4JThread(runRead, (void*)this, readThreadName, READ_STACK_SIZE); + writeThread = new C4JThread(runWrite, this, writeThreadName, WRITE_STACK_SIZE); + readThread->SetProcessor(CPU_CORE_CONNECTIONS); + writeThread->SetProcessor(CPU_CORE_CONNECTIONS ); +#ifdef __ORBIS__ + readThread->SetPriority(THREAD_PRIORITY_BELOW_NORMAL); // On Orbis, this core is also used for Matching 2, and that priority of that seems to be always at default no matter what we set it to. Prioritise this below Matching 2. + writeThread->SetPriority(THREAD_PRIORITY_BELOW_NORMAL); // On Orbis, this core is also used for Matching 2, and that priority of that seems to be always at default no matter what we set it to. Prioritise this below Matching 2. +#endif + + readThread->Run(); + writeThread->Run(); + + + /* 4J JEV, java: + new Thread(wstring(id).append(L" read thread")) { + + }; + + writeThread = new Thread(id + " write thread") { + public void run() { + + }; + + readThread->start(); + writeThread->start(); + */ +} + + +void Connection::setListener(PacketListener *packetListener) +{ + this->packetListener = packetListener; +} + +void Connection::send(shared_ptr packet) +{ + if (quitting) return; + + MemSect(15); + // 4J Jev, synchronized (&writeLock) + EnterCriticalSection(&writeLock); + + estimatedRemaining += packet->getEstimatedSize() + 1; + if (packet->shouldDelay) + { + // 4J We have delayed it enough by putting it in the slow queue, so don't delay when we actually send it + packet->shouldDelay = false; + outgoing_slow.push(packet); + } + else + { + outgoing.push(packet); + } + + // 4J Jev, end synchronized. + LeaveCriticalSection(&writeLock); + MemSect(0); +} + + +void Connection::queueSend(shared_ptr packet) +{ + if (quitting) return; + estimatedRemaining += packet->getEstimatedSize() + 1; + outgoing_slow.push(packet); +} + +bool Connection::writeTick() +{ + bool didSomething = false; + + // 4J Stu - If the connection is closed and the output stream has been deleted + if(bufferedDos==NULL || byteArrayDos==NULL) + return didSomething; + + // try { + if (!outgoing.empty() && (fakeLag == 0 || System::currentTimeMillis() - outgoing.front()->createTime >= fakeLag)) + { + shared_ptr packet; + + EnterCriticalSection(&writeLock); + + packet = outgoing.front(); + outgoing.pop(); + estimatedRemaining -= packet->getEstimatedSize() + 1; + + LeaveCriticalSection(&writeLock); + + Packet::writePacket(packet, bufferedDos); + + +#ifndef _CONTENT_PACKAGE + // 4J Added for debugging + if( !socket->isLocal() ) + Packet::recordOutgoingPacket(packet); +#endif + + // 4J Stu - Changed this so that rather than writing to the network stream through a buffered stream we want to: + // a) Only push whole "game" packets to QNet, rather than amalgamated chunks of data that may include many packets, and partial packets + // b) To be able to change the priority and queue of a packet if required + //sos->writeWithFlags( baos->buf, 0, baos->size(), 0 ); + //baos->reset(); + + writeSizes[packet->getId()] += packet->getEstimatedSize() + 1; + didSomething = true; + } + + if ((slowWriteDelay-- <= 0) && !outgoing_slow.empty() && (fakeLag == 0 || System::currentTimeMillis() - outgoing_slow.front()->createTime >= fakeLag)) + { + shared_ptr packet; + + //synchronized (writeLock) { + + EnterCriticalSection(&writeLock); + + packet = outgoing_slow.front(); + outgoing_slow.pop(); + estimatedRemaining -= packet->getEstimatedSize() + 1; + + LeaveCriticalSection(&writeLock); + + // If the shouldDelay flag is still set at this point then we want to write it to QNet as a single packet with priority flags + // Otherwise just buffer the packet with other outgoing packets as the java game did + if(packet->shouldDelay) + { + Packet::writePacket(packet, byteArrayDos); + + // 4J Stu - Changed this so that rather than writing to the network stream through a buffered stream we want to: + // a) Only push whole "game" packets to QNet, rather than amalgamated chunks of data that may include many packets, and partial packets + // b) To be able to change the priority and queue of a packet if required + int flags = QNET_SENDDATA_LOW_PRIORITY | QNET_SENDDATA_SECONDARY; + sos->writeWithFlags( baos->buf, 0, baos->size(), flags ); + baos->reset(); + } + else + { + Packet::writePacket(packet, bufferedDos); + } + +#ifndef _CONTENT_PACKAGE + // 4J Added for debugging + if( !socket->isLocal() ) + Packet::recordOutgoingPacket(packet); +#endif + + writeSizes[packet->getId()] += packet->getEstimatedSize() + 1; + slowWriteDelay = 0; + didSomething = true; + } + /* 4J JEV, removed try/catch + } catch (Exception e) { + if (!disconnected) handleException(e); + return false; + } */ + + return didSomething; +} + + +void Connection::flush() +{ + // TODO 4J Stu - How to interrupt threads? Or do we need to change the multithreaded functions a bit more + //readThread.interrupt(); + //writeThread.interrupt(); + m_hWakeReadThread->Set(); + m_hWakeWriteThread->Set(); +} + + +bool Connection::readTick() +{ + bool didSomething = false; + + // 4J Stu - If the connection has closed and the input stream has been deleted + if(dis==NULL) + return didSomething; + + //try { + + shared_ptr packet = Packet::readPacket(dis, packetListener->isServerPacketListener()); + + if (packet != NULL) + { + readSizes[packet->getId()] += packet->getEstimatedSize() + 1; + EnterCriticalSection(&incoming_cs); + if(!quitting) + { + incoming.push(packet); + } + LeaveCriticalSection(&incoming_cs); + didSomething = true; + } + else + { +// printf("Con:0x%x readTick close EOS\n",this); + + // 4J Stu - Remove this line + // Fix for #10410 - UI: If the player is removed from a splitscreened host’s game, the next game that player joins will produce a message stating that the host has left. + //close(DisconnectPacket::eDisconnect_EndOfStream); + } + + + /* 4J JEV, removed try/catch + } catch (Exception e) { + if (!disconnected) handleException(e); + return false; + } */ + + return didSomething; +} + + +/* 4J JEV, removed try/catch +void handleException(Exception e) +{ +e.printStackTrace(); +close("disconnect.genericReason", "Internal exception: " + e.toString()); +}*/ + + +void Connection::close(DisconnectPacket::eDisconnectReason reason, ...) +{ +// printf("Con:0x%x close\n",this); + if (!running) return; +// printf("Con:0x%x close doing something\n",this); + disconnected = true; + + va_list input; + va_start( input, reason ); + + disconnectReason = reason;//va_arg( input, const wstring ); + + vector objs = vector(); + void *i = NULL; + while (i != NULL) + { + i = va_arg( input, void* ); + objs.push_back(i); + } + + if( objs.size() ) + { + disconnectReasonObjects = &objs[0]; + } + else + { + disconnectReasonObjects = NULL; + } + + // int count = 0, sum = 0, i = first; + // va_list marker; + // + // va_start( marker, first ); + // while( i != -1 ) + // { + // sum += i; + // count++; + // i = va_arg( marker, int); + // } + // va_end( marker ); + // return( sum ? (sum / count) : 0 ); + + +// CreateThread(NULL, 0, runClose, this, 0, &closeThreadID); + + running = false; + + if( dis ) dis->close(); // The input stream needs closed before the readThread, or the readThread + // may get stuck whilst blocking waiting on a read + + // Make sure that the read & write threads are dead before we go and kill the streams that they depend on + readThread->WaitForCompletion(INFINITE); + writeThread->WaitForCompletion(INFINITE); + + delete dis; + dis = NULL; + if( bufferedDos ) + { + bufferedDos->close(); + bufferedDos->deleteChildStream(); + delete bufferedDos; + bufferedDos = NULL; + } + if( byteArrayDos ) + { + byteArrayDos->close(); + delete byteArrayDos; + byteArrayDos = NULL; + } + if( socket ) + { + socket->close(packetListener->isServerPacketListener()); + socket = NULL; + } +} + +void Connection::tick() +{ + if (estimatedRemaining > 1 * 1024 * 1024) + { + close(DisconnectPacket::eDisconnect_Overflow); + } + EnterCriticalSection(&incoming_cs); + bool empty = incoming.empty(); + LeaveCriticalSection(&incoming_cs); + if (empty) + { +#if CONNECTION_ENABLE_TIMEOUT_DISCONNECT + if (noInputTicks++ == MAX_TICKS_WITHOUT_INPUT) + { + close(DisconnectPacket::eDisconnect_TimeOut); + } +#endif + } + // 4J Stu - Moved this a bit later in the function to stop the race condition of Disconnect packets not being processed when local client leaves + //else if( socket && socket->isClosing() ) + //{ + // close(DisconnectPacket::eDisconnect_Closed); + //} + else + { + noInputTicks = 0; + + } + + // 4J Added - Send a KeepAlivePacket every now and then to ensure that our read and write threads don't timeout + tickCount++; + if (tickCount % 20 == 0) + { + send( shared_ptr( new KeepAlivePacket() ) ); + } + + // 4J Stu - 1.8.2 changed from 100 to 1000 + int max = 1000; + + // 4J-PB - NEEDS CHANGED!!! + // If we can call connection.close from within a packet->handle, then we can lockup because the loop below has locked incoming_cs, and the connection.close will flag the read and write threads for the connection to close. + // they are running on other threads, and will try to lock incoming_cs + // We got this with a pre-login packet of a player who wasn't allowed to play due to parental controls, so was kicked out + // This has been changed to use a eAppAction_ExitPlayerPreLogin which will run in the main loop, so the connection will not be ticked at that point + + + EnterCriticalSection(&incoming_cs); + // 4J Stu - If disconnected, then we shouldn't process incoming packets + std::vector< shared_ptr > packetsToHandle; + while (!disconnected && !g_NetworkManager.IsLeavingGame() && g_NetworkManager.IsInSession() && !incoming.empty() && max-- >= 0) + { + shared_ptr packet = incoming.front(); + packetsToHandle.push_back(packet); + incoming.pop(); + } + LeaveCriticalSection(&incoming_cs); + + // MGH - moved the packet handling outside of the incoming_cs block, as it was locking up sometimes when disconnecting + for(int i=0; igetId()); + packetsToHandle[i]->handle(packetListener); + PIXEndNamedEvent(); + } + flush(); + + // 4J Stu - Moved this a bit later in the function to stop the race condition of Disconnect packets not being processed when local client leaves + if( socket && socket->isClosing() ) + { + close(DisconnectPacket::eDisconnect_Closed); + } + + // 4J - split the following condition (used to be disconnect && iscoming.empty()) so we can wrap the access in a critical section + if (disconnected) + { + EnterCriticalSection(&incoming_cs); + bool empty = incoming.empty(); + LeaveCriticalSection(&incoming_cs); + if( empty ) + { + packetListener->onDisconnect(disconnectReason, disconnectReasonObjects); + disconnected = false; // 4J added - don't keep sending this every tick + } + } +} + +SocketAddress *Connection::getRemoteAddress() +{ + return (SocketAddress *) address; +} + +void Connection::sendAndQuit() +{ + if (quitting) + { + return; + } +// printf("Con:0x%x send & quit\n",this); + flush(); + quitting = true; + // TODO 4J Stu - How to interrupt threads? Or do we need to change the multithreaded functions a bit more + //readThread.interrupt(); + +#if 1 + // 4J - this used to be in a thread but not sure why, and is causing trouble for us if we kill the connection + // whilst the thread is still expecting to be able to send a packet a couple of seconds after starting it + if (running) + { + // 4J TODO writeThread.interrupt(); + close(DisconnectPacket::eDisconnect_Closed); + } +#else + CreateThread(NULL, 0, runSendAndQuit, this, 0, &saqThreadID); +#endif +} + +int Connection::countDelayedPackets() +{ + return (int)outgoing_slow.size(); +} + + +int Connection::runRead(void* lpParam) +{ + ShutdownManager::HasStarted(ShutdownManager::eConnectionReadThreads); + Connection *con = (Connection *)lpParam; + + if (con == NULL) + { +#ifdef __PS3__ + ShutdownManager::HasFinished(ShutdownManager::eConnectionReadThreads); +#endif + return 0; + } + + Compression::UseDefaultThreadStorage(); + + CRITICAL_SECTION *cs = &con->threadCounterLock; + + EnterCriticalSection(cs); + con->readThreads++; + LeaveCriticalSection(cs); + + //try { + + MemSect(19); + while (con->running && !con->quitting && ShutdownManager::ShouldRun(ShutdownManager::eConnectionReadThreads)) + { + while (con->readTick()) + ; + + // try { + //Sleep(100L); + // TODO - 4J Stu - 1.8.2 changes these sleeps to 2L, but not sure whether we should do that as well + con->m_hWakeReadThread->WaitForSignal(100L); + } + MemSect(0); + + /* 4J JEV, removed try/catch + } catch (InterruptedException e) { + } + } + } finally { + synchronized (threadCounterLock) { + readThreads--; + } + } */ + + ShutdownManager::HasFinished(ShutdownManager::eConnectionReadThreads); + return 0; +} + +int Connection::runWrite(void* lpParam) +{ + ShutdownManager::HasStarted(ShutdownManager::eConnectionWriteThreads); + Connection *con = dynamic_cast((Connection *) lpParam); + + if (con == NULL) + { + ShutdownManager::HasFinished(ShutdownManager::eConnectionWriteThreads); + return 0; + } + + Compression::UseDefaultThreadStorage(); + + CRITICAL_SECTION *cs = &con->threadCounterLock; + + EnterCriticalSection(cs); + con->writeThreads++; + LeaveCriticalSection(cs); + + // 4J Stu - Adding this to force us to run through the writeTick at least once after the event is fired + // Otherwise there is a race between the calling thread setting the running flag and this loop checking the condition + DWORD waitResult = WAIT_TIMEOUT; + + while ((con->running || waitResult == WAIT_OBJECT_0 ) && ShutdownManager::ShouldRun(ShutdownManager::eConnectionWriteThreads)) + { + while (con->writeTick()) + ; + + //Sleep(100L); + // TODO - 4J Stu - 1.8.2 changes these sleeps to 2L, but not sure whether we should do that as well + waitResult = con->m_hWakeWriteThread->WaitForSignal(100L); + + if (con->bufferedDos != NULL) con->bufferedDos->flush(); + //if (con->byteArrayDos != NULL) con->byteArrayDos->flush(); + } + + + // 4J was in a finally block. + EnterCriticalSection(cs); + con->writeThreads--; + LeaveCriticalSection(cs); + + ShutdownManager::HasFinished(ShutdownManager::eConnectionWriteThreads); + return 0; +} + +int Connection::runClose(void* lpParam) +{ + Connection *con = dynamic_cast((Connection *) lpParam); + + if (con == NULL) return 0; + + //try { + + Sleep(2000); + if (con->running) + { + // 4J TODO writeThread.interrupt(); + con->close(DisconnectPacket::eDisconnect_Closed); + } + + /* 4J Jev, removed try/catch + } catch (Exception e) { + e.printStackTrace(); + } */ + + return 1; +} + +int Connection::runSendAndQuit(void* lpParam) +{ + Connection *con = dynamic_cast((Connection *) lpParam); +// printf("Con:0x%x runSendAndQuit\n",con); + + if (con == NULL) return 0; + + //try { + + Sleep(2000); + if (con->running) + { + // 4J TODO writeThread.interrupt(); + con->close(DisconnectPacket::eDisconnect_Closed); +// printf("Con:0x%x runSendAndQuit close\n",con); + } + +// printf("Con:0x%x runSendAndQuit end\n",con); + /* 4J Jev, removed try/catch + } catch (Exception e) { + e.printStackTrace(); + } */ + + return 0; +} diff --git a/Minecraft.World/Connection.h b/Minecraft.World/Connection.h new file mode 100644 index 00000000..9db00512 --- /dev/null +++ b/Minecraft.World/Connection.h @@ -0,0 +1,145 @@ +#pragma once +using namespace std; + +#include "stdafx.h" +#include +#include "System.h" +#include "DataInputStream.h" +#include "DataOutputStream.h" +#include "net.minecraft.network.packet.h" +#include "C4JThread.h" + +#include "Socket.h" + +// 4J JEV, size of the threads (bytes). +#define READ_STACK_SIZE 0 +#define WRITE_STACK_SIZE 0 + +class ByteArrayOutputStream; + +class Connection +{ + friend DWORD WINAPI runRead(LPVOID lpParam); + friend DWORD WINAPI runWrite(LPVOID lpParam); + friend DWORD WINAPI runSendAndQuit(LPVOID lpParam); + friend DWORD WINAPI runClose(LPVOID lpParam); + +private: + static const int SEND_BUFFER_SIZE = 1024 * 5; + +public: + static int readThreads, writeThreads; + +private: + static const int MAX_TICKS_WITHOUT_INPUT = 20 * 60; + +public: + static const int IPTOS_LOWCOST = 0x02; + static const int IPTOS_RELIABILITY = 0x04; + static const int IPTOS_THROUGHPUT = 0x08; + static const int IPTOS_LOWDELAY = 0x10; + +private: + Socket *socket; + const SocketAddress *address; + DataInputStream *dis; + DataOutputStream *bufferedDos; // 4J This is the same type of dos the java game has + + // 4J Added + DataOutputStream *byteArrayDos; // 4J This dos allows us to write individual packets to the socket + ByteArrayOutputStream *baos; + Socket::SocketOutputStream *sos; + + bool running; + + queue > incoming; // 4J - was using synchronizedList... + CRITICAL_SECTION incoming_cs; // ... now has this critical section + queue > outgoing; // 4J - was using synchronizedList - but don't think it is required as usage is wrapped in writeLock critical section + queue > outgoing_slow; // 4J - was using synchronizedList - but don't think it is required as usage is wrapped in writeLock critical section + + + PacketListener *packetListener; + bool quitting; + + + C4JThread* readThread; + C4JThread* writeThread; + + C4JThread::Event* m_hWakeReadThread; + C4JThread::Event* m_hWakeWriteThread; + + DWORD saqThreadID, closeThreadID; + + bool disconnected; + DisconnectPacket::eDisconnectReason disconnectReason; + void **disconnectReasonObjects; // 4J a pointer to an array. + + int noInputTicks; + int estimatedRemaining; + + int tickCount; // 4J Added + +public: + static int readSizes[256]; + static int writeSizes[256]; + + int fakeLag; + +private: + void _init(); + + // 4J Jev, these might be better of as private + CRITICAL_SECTION threadCounterLock; + CRITICAL_SECTION writeLock; + +public: + // 4J Jev, need to delete the critical section. + ~Connection(); + Connection(Socket *socket, const wstring& id, PacketListener *packetListener); // throws IOException + + void setListener(PacketListener *packetListener); + void send(shared_ptr packet); + +public: + void queueSend(shared_ptr packet); + +private: + int slowWriteDelay; + + bool writeTick(); + +public: + void flush(); + +private: + bool readTick(); + +private: + + /* 4J JEV, removed try/catch + void handleException(Exception e) + { + e.printStackTrace(); + close("disconnect.genericReason", "Internal exception: " + e.toString()); + }*/ + +public: + void close(DisconnectPacket::eDisconnectReason reason, ...); + + void tick(); + + SocketAddress *getRemoteAddress(); + + void sendAndQuit(); + + int countDelayedPackets(); + + Socket *getSocket() { return socket; } + +private: + static int runRead(void* lpParam); + static int runWrite(void* lpParam); + static int runClose(void* lpParam); + static int runSendAndQuit(void* lpParam); + +}; diff --git a/Minecraft.World/ConsoleSaveFile.h b/Minecraft.World/ConsoleSaveFile.h new file mode 100644 index 00000000..39c1ec89 --- /dev/null +++ b/Minecraft.World/ConsoleSaveFile.h @@ -0,0 +1,57 @@ +#pragma once + +#include "FileHeader.h" +#include "ConsoleSavePath.h" + +class ConsoleSaveFile +{ +public: + virtual ~ConsoleSaveFile() {}; + + virtual FileEntry *createFile( const ConsoleSavePath &fileName ) = 0; + virtual void deleteFile( FileEntry *file ) = 0; + virtual void setFilePointer( FileEntry *file,LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,DWORD dwMoveMethod) = 0; + virtual BOOL writeFile( FileEntry *file, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten) = 0; + virtual BOOL zeroFile(FileEntry *file, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten) = 0; + virtual BOOL readFile( FileEntry *file, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead ) = 0; + virtual BOOL closeHandle( FileEntry *file ) = 0; + virtual void finalizeWrite() = 0; + virtual void tick() {}; + + virtual bool doesFileExist(ConsoleSavePath file) = 0; + + virtual void Flush(bool autosave, bool updateThumbnail = true) = 0; + +#ifndef _CONTENT_PACKAGE + virtual void DebugFlushToFile(void *compressedData = NULL, unsigned int compressedDataSize = 0) = 0; +#endif + virtual unsigned int getSizeOnDisk() = 0; + virtual wstring getFilename() = 0; + virtual vector *getFilesWithPrefix(const wstring &prefix) = 0; + virtual vector *getRegionFilesByDimension(unsigned int dimensionIndex) = 0; + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + virtual wstring getPlayerDataFilenameForLoad(const PlayerUID& pUID) = 0; + virtual wstring getPlayerDataFilenameForSave(const PlayerUID& pUID) = 0; + virtual vector *getValidPlayerDatFiles() = 0; +#endif //__PS3__ + + virtual int getSaveVersion() = 0; + virtual int getOriginalSaveVersion() = 0; + + virtual void LockSaveAccess() = 0; + virtual void ReleaseSaveAccess() = 0; + + virtual ESavePlatform getSavePlatform() = 0; + virtual bool isSaveEndianDifferent() = 0; + virtual void setLocalPlatform() = 0; + virtual void setPlatform(ESavePlatform plat) = 0; + virtual ByteOrder getSaveEndian() = 0; + virtual ByteOrder getLocalEndian() = 0; + virtual void setEndian(ByteOrder endian) = 0; + + virtual void ConvertRegionFile(File sourceFile) = 0; + virtual void ConvertToLocalPlatform() = 0; + + virtual void *getWritePointer(FileEntry *file) { return NULL; } +}; diff --git a/Minecraft.World/ConsoleSaveFileConverter.cpp b/Minecraft.World/ConsoleSaveFileConverter.cpp new file mode 100644 index 00000000..9a3d572d --- /dev/null +++ b/Minecraft.World/ConsoleSaveFileConverter.cpp @@ -0,0 +1,294 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.chunk.storage.h" +#include "net.minecraft.world.level.storage.h" +#include "ConsoleSaveFileIO.h" +#include "ConsoleSaveFileConverter.h" +#include "ProgressListener.h" + +void ConsoleSaveFileConverter::ProcessSimpleFile(ConsoleSaveFile *sourceSave, FileEntry *sourceFileEntry, ConsoleSaveFile *targetSave, FileEntry *targetFileEntry) +{ + DWORD numberOfBytesRead = 0; + DWORD numberOfBytesWritten = 0; + + byte *data = new byte[sourceFileEntry->getFileSize()]; + + // Read from source + sourceSave->readFile(sourceFileEntry, data, sourceFileEntry->getFileSize(), &numberOfBytesRead); + + // Write back to target + targetSave->writeFile(targetFileEntry, data, numberOfBytesRead, &numberOfBytesWritten); + + delete [] data; +} + +void ConsoleSaveFileConverter::ProcessStandardRegionFile(ConsoleSaveFile *sourceSave, File sourceFile, ConsoleSaveFile *targetSave, File targetFile) +{ + DWORD numberOfBytesWritten = 0; + DWORD numberOfBytesRead = 0; + + RegionFile sourceRegionFile(sourceSave, &sourceFile); + RegionFile targetRegionFile(targetSave, &targetFile); + + for(unsigned int x = 0; x < 32; ++x) + { + for(unsigned int z = 0; z < 32; ++z) + { + DataInputStream *dis = sourceRegionFile.getChunkDataInputStream(x,z); + + if(dis) + { + int read = dis->read(); + DataOutputStream *dos = targetRegionFile.getChunkDataOutputStream(x,z); + while(read != -1) + { + + dos->write( read & 0xff ); + + read = dis->read(); + } + dos->close(); + dos->deleteChildStream(); + delete dos; + } + + delete dis; + } + } +} + +void ConsoleSaveFileConverter::ConvertSave(ConsoleSaveFile *sourceSave, ConsoleSaveFile *targetSave, ProgressListener *progress) +{ + // Process level.dat + ConsoleSavePath ldatPath( wstring(L"level.dat") ); + FileEntry *sourceLdatFe = sourceSave->createFile( ldatPath ); + FileEntry *targetLdatFe = targetSave->createFile( ldatPath ); + app.DebugPrintf("Processing level.dat\n"); + ProcessSimpleFile(sourceSave, sourceLdatFe, targetSave, targetLdatFe); + + // Process game rules + { + ConsoleSavePath gameRulesPath( GAME_RULE_SAVENAME ); + if(sourceSave->doesFileExist(gameRulesPath) ) + { + FileEntry *sourceFe = sourceSave->createFile( gameRulesPath ); + FileEntry *targetFe = targetSave->createFile( gameRulesPath ); + app.DebugPrintf("Processing game rules\n"); + ProcessSimpleFile(sourceSave, sourceFe, targetSave, targetFe); + } + } + + // MGH added - find any player data files and copy them across +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + vector* playerFiles = sourceSave->getValidPlayerDatFiles(); +#else + vector *playerFiles = sourceSave->getFilesWithPrefix( DirectoryLevelStorage::getPlayerDir() ); +#endif + + if(playerFiles != NULL) + { + for(int fileIdx = 0; fileIdx < playerFiles->size();fileIdx++) + { + ConsoleSavePath sourcePlayerDatPath( playerFiles->at(fileIdx)->data.filename ); +#ifdef _XBOX_ONE + // 4J Stu - As the XUIDs on X360 and X1 are different, we don't want to transfer these over. However as the first player + // file should be the owner of the save, we can move their data over to the current players XUID + if(fileIdx > 0) break; + PlayerUID xuid; + ProfileManager.GetXUID(ProfileManager.GetPrimaryPad(), &xuid, false); + ConsoleSavePath targetPlayerDatPath( L"players/" + xuid.toString() + L".dat" ); +#else + ConsoleSavePath targetPlayerDatPath( playerFiles->at(fileIdx)->data.filename ); +#endif + { + FileEntry *sourceFe = sourceSave->createFile( sourcePlayerDatPath ); + FileEntry *targetFe = targetSave->createFile( targetPlayerDatPath ); + app.DebugPrintf("Processing player dat file %s\n", playerFiles->at(fileIdx)->data.filename); + ProcessSimpleFile(sourceSave, sourceFe, targetSave, targetFe); + + targetFe->data.lastModifiedTime = sourceFe->data.lastModifiedTime; + } + } + delete playerFiles; + } + + +#ifdef SPLIT_SAVES + int xzSize = LEVEL_LEGACY_WIDTH; + int hellScale = HELL_LEVEL_LEGACY_SCALE; + if ( sourceSave->doesFileExist( ldatPath ) ) + { + ConsoleSaveFileInputStream fis = ConsoleSaveFileInputStream(sourceSave, ldatPath); + CompoundTag *root = NbtIo::readCompressed(&fis); + CompoundTag *tag = root->getCompound(L"Data"); + LevelData ret(tag); + + xzSize = ret.getXZSize(); + hellScale = ret.getHellScale(); + + delete root; + } + + RegionFileCache sourceCache; + RegionFileCache targetCache; + + if(progress) + { + progress->progressStage(IDS_SAVETRANSFER_STAGE_CONVERTING); + } + + // Overworld + { + app.DebugPrintf("Processing the overworld\n"); + int halfXZSize = xzSize / 2; + + int progressTarget = (xzSize) * (xzSize); + int currentProgress = 0; + if(progress) progress->progressStagePercentage((currentProgress*100)/progressTarget); + + for(int x = -halfXZSize; x < halfXZSize; ++x) + { + for(int z = -halfXZSize; z < halfXZSize; ++z) + { + //app.DebugPrintf("Processing overworld chunk %d,%d\n",x,z); + DataInputStream *dis = sourceCache._getChunkDataInputStream(sourceSave,L"",x,z); + + if(dis) + { + int read = dis->read(); + DataOutputStream *dos = targetCache._getChunkDataOutputStream(targetSave,L"",x,z); + BufferedOutputStream bos(dos, 1024 * 1024); + while(read != -1) + { + + bos.write( read & 0xff ); + + read = dis->read(); + } + bos.flush(); + dos->close(); + dos->deleteChildStream(); + delete dos; + } + + delete dis; + + ++currentProgress; + if(progress) progress->progressStagePercentage( (currentProgress*100)/progressTarget); + + } + } + } + + // Nether + { + app.DebugPrintf("Processing the nether\n"); + int hellSize = xzSize / hellScale; + int halfXZSize = hellSize / 2; + + int progressTarget = (hellSize) * (hellSize); + int currentProgress = 0; + if(progress) progress->progressStagePercentage((currentProgress*100)/progressTarget); + + for(int x = -halfXZSize; x < halfXZSize; ++x) + { + for(int z = -halfXZSize; z < halfXZSize; ++z) + { + //app.DebugPrintf("Processing nether chunk %d,%d\n",x,z); + DataInputStream *dis = sourceCache._getChunkDataInputStream(sourceSave,L"DIM-1",x,z); + + if(dis) + { + int read = dis->read(); + DataOutputStream *dos = targetCache._getChunkDataOutputStream(targetSave,L"DIM-1",x,z); + BufferedOutputStream bos(dos, 1024 * 1024); + while(read != -1) + { + + bos.write( read & 0xff ); + + read = dis->read(); + } + bos.flush(); + dos->close(); + dos->deleteChildStream(); + delete dos; + } + + delete dis; + + ++currentProgress; + if(progress) progress->progressStagePercentage((currentProgress*100)/progressTarget); + } + } + } + + // End + { + app.DebugPrintf("Processing the end\n"); + int halfXZSize = END_LEVEL_MAX_WIDTH / 2; + + int progressTarget = (END_LEVEL_MAX_WIDTH) * (END_LEVEL_MAX_WIDTH); + int currentProgress = 0; + if(progress) progress->progressStagePercentage((currentProgress*100)/progressTarget); + + for(int x = -halfXZSize; x < halfXZSize; ++x) + { + for(int z = -halfXZSize; z < halfXZSize; ++z) + { + //app.DebugPrintf("Processing end chunk %d,%d\n",x,z); + DataInputStream *dis = sourceCache._getChunkDataInputStream(sourceSave,L"DIM1/",x,z); + + if(dis) + { + int read = dis->read(); + DataOutputStream *dos = targetCache._getChunkDataOutputStream(targetSave,L"DIM1/",x,z); + BufferedOutputStream bos(dos, 1024 * 1024); + while(read != -1) + { + + bos.write( read & 0xff ); + + read = dis->read(); + } + bos.flush(); + dos->close(); + dos->deleteChildStream(); + delete dos; + } + + delete dis; + + ++currentProgress; + if(progress) progress->progressStagePercentage((currentProgress*100)/progressTarget); + } + } + } + +#else + // 4J Stu - Old version that just changes the compression of chunks, not usable for XboxOne style split saves or compressed tile formats + // Process region files + vector *allFilesInSave = sourceSave->getFilesWithPrefix(wstring(L"")); + for(AUTO_VAR(it, allFilesInSave->begin()); it < allFilesInSave->end(); ++it) + { + FileEntry *fe = *it; + if( fe != sourceLdatFe ) + { + wstring fName( fe->data.filename ); + wstring suffix(L".mcr"); + if( fName.compare(fName.length() - suffix.length(), suffix.length(), suffix) == 0 ) + { +#ifndef _CONTENT_PACKAGE + wprintf(L"Processing a region file: %s\n", fe->data.filename); +#endif + ProcessStandardRegionFile(sourceSave, File(fe->data.filename), targetSave, File(fe->data.filename) ); + } + else + { +#ifndef _CONTENT_PACKAGE + wprintf(L"%s is not a region file, ignoring\n", fe->data.filename); +#endif + } + } + } +#endif +} \ No newline at end of file diff --git a/Minecraft.World/ConsoleSaveFileConverter.h b/Minecraft.World/ConsoleSaveFileConverter.h new file mode 100644 index 00000000..e258b652 --- /dev/null +++ b/Minecraft.World/ConsoleSaveFileConverter.h @@ -0,0 +1,16 @@ +#pragma once +#include "File.h" +class FileEntry; +class ConsoleSaveFile; +class ProgressRenderer; + +// 4J Stu - This code is taken from the standalone save converter tool, and modified slightly +class ConsoleSaveFileConverter +{ +private: + static void ProcessSimpleFile(ConsoleSaveFile *sourceSave, FileEntry *sourceFileEntry, ConsoleSaveFile *targetSave, FileEntry *targetFileEntry); + static void ProcessStandardRegionFile(ConsoleSaveFile *sourceSave, File sourceFile, ConsoleSaveFile *targetSave, File targetFile); + +public: + static void ConvertSave(ConsoleSaveFile *sourceSave, ConsoleSaveFile *targetSave, ProgressListener *progress); +}; \ No newline at end of file diff --git a/Minecraft.World/ConsoleSaveFileIO.h b/Minecraft.World/ConsoleSaveFileIO.h new file mode 100644 index 00000000..8dfa17f5 --- /dev/null +++ b/Minecraft.World/ConsoleSaveFileIO.h @@ -0,0 +1,7 @@ +#pragma once + +#include "FileHeader.h" +#include "ConsoleSaveFile.h" +#include "ConsoleSaveFileInputStream.h" +#include "ConsoleSaveFileOutputStream.h" +#include "ConsoleSavePath.h" \ No newline at end of file diff --git a/Minecraft.World/ConsoleSaveFileInputStream.cpp b/Minecraft.World/ConsoleSaveFileInputStream.cpp new file mode 100644 index 00000000..93d49956 --- /dev/null +++ b/Minecraft.World/ConsoleSaveFileInputStream.cpp @@ -0,0 +1,134 @@ +#include "stdafx.h" + +#include "File.h" +#include "ConsoleSaveFile.h" +#include "ConsoleSaveFileInputStream.h" + +ConsoleSaveFileInputStream::ConsoleSaveFileInputStream(ConsoleSaveFile *saveFile, const ConsoleSavePath &file) +{ + m_saveFile = saveFile; + m_file = m_saveFile->createFile( file ); + + m_saveFile->setFilePointer( m_file, 0, NULL, FILE_BEGIN ); +} + +ConsoleSaveFileInputStream::ConsoleSaveFileInputStream(ConsoleSaveFile *saveFile, FileEntry *file) +{ + m_saveFile = saveFile; + m_file = file; + + m_saveFile->setFilePointer( m_file, 0, NULL, FILE_BEGIN ); +} + +//Reads a byte of data from this input stream. This method blocks if no input is yet available. +//Returns: +//the next byte of data, or -1 if the end of the file is reached. +int ConsoleSaveFileInputStream::read() +{ + byte byteRead = 0; + DWORD numberOfBytesRead; + + BOOL result = m_saveFile->readFile( + m_file, + &byteRead, // data buffer + 1, // number of bytes to read + &numberOfBytesRead // number of bytes read + ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + return -1; + } + else if( numberOfBytesRead == 0 ) + { + // File pointer is past the end of the file + return -1; + } + + return byteRead; +} + +//Reads up to b.length bytes of data from this input stream into an array of bytes. This method blocks until some input is available. +//Parameters: +//b - the buffer into which the data is read. +//Returns: +//the total number of bytes read into the buffer, or -1 if there is no more data because the end of the file has been reached. +int ConsoleSaveFileInputStream::read(byteArray b) +{ + DWORD numberOfBytesRead; + + BOOL result = m_saveFile->readFile( + m_file, + &b.data, // data buffer + b.length, // number of bytes to read + &numberOfBytesRead // number of bytes read + ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + return -1; + } + else if( numberOfBytesRead == 0 ) + { + // File pointer is past the end of the file + return -1; + } + + return numberOfBytesRead; +} + +//Reads up to len bytes of data from this input stream into an array of bytes. If len is not zero, the method blocks until some input +//is available; otherwise, no bytes are read and 0 is returned. +//Parameters: +//b - the buffer into which the data is read. +//off - the start offset in the destination array b +//len - the maximum number of bytes read. +//Returns: +//the total number of bytes read into the buffer, or -1 if there is no more data because the end of the file has been reached. +int ConsoleSaveFileInputStream::read(byteArray b, unsigned int offset, unsigned int length) +{ + // 4J Stu - We don't want to read any more than the array buffer can hold + assert( length <= ( b.length - offset ) ); + + DWORD numberOfBytesRead; + + BOOL result = m_saveFile->readFile( + m_file, + &b[offset], // data buffer + length, // number of bytes to read + &numberOfBytesRead // number of bytes read + ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + return -1; + } + else if( numberOfBytesRead == 0 ) + { + // File pointer is past the end of the file + return -1; + } + + return numberOfBytesRead; +} + +//Closes this file input stream and releases any system resources associated with the stream. +//If this stream has an associated channel then the channel is closed as well. +void ConsoleSaveFileInputStream::close() +{ + if( m_saveFile != NULL ) + { + BOOL result = m_saveFile->closeHandle( m_file ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + } + + // Stop the dtor from trying to close it again + m_saveFile = NULL; + } +} diff --git a/Minecraft.World/ConsoleSaveFileInputStream.h b/Minecraft.World/ConsoleSaveFileInputStream.h new file mode 100644 index 00000000..876a9484 --- /dev/null +++ b/Minecraft.World/ConsoleSaveFileInputStream.h @@ -0,0 +1,26 @@ +#pragma once +// 4J Stu - Implements the Java InputStream but rather than writing directly to disc it writes through the save file + +#include "InputStream.h" + +#include "ConsoleSavePath.h" + +class ConsoleSaveFile; +class FileEntry; + +class ConsoleSaveFileInputStream : public InputStream +{ +public: + ConsoleSaveFileInputStream(ConsoleSaveFile *saveFile, const ConsoleSavePath &file); + ConsoleSaveFileInputStream(ConsoleSaveFile *saveFile, FileEntry *file); + virtual int read(); + virtual int read(byteArray b); + virtual int read(byteArray b, unsigned int offset, unsigned int length); + virtual void close(); + virtual __int64 skip(__int64 n) { return n; } + +private: + ConsoleSaveFile *m_saveFile; + FileEntry *m_file; + +}; \ No newline at end of file diff --git a/Minecraft.World/ConsoleSaveFileOriginal.cpp b/Minecraft.World/ConsoleSaveFileOriginal.cpp new file mode 100644 index 00000000..139d99ac --- /dev/null +++ b/Minecraft.World/ConsoleSaveFileOriginal.cpp @@ -0,0 +1,1059 @@ +#include "stdafx.h" +#include "StringHelpers.h" +#include "ConsoleSaveFileOriginal.h" +#include "File.h" +#include +#include "compression.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\MinecraftServer.h" +#include "..\Minecraft.Client\ServerLevel.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\LevelData.h" +#include "..\Minecraft.Client\Common\GameRules\LevelGenerationOptions.h" +#include "..\Minecraft.World\net.minecraft.world.level.chunk.storage.h" + + +#ifdef _XBOX +#define RESERVE_ALLOCATION MEM_RESERVE | MEM_LARGE_PAGES +#define COMMIT_ALLOCATION MEM_COMMIT | MEM_LARGE_PAGES +#else +#define RESERVE_ALLOCATION MEM_RESERVE +#define COMMIT_ALLOCATION MEM_COMMIT +#endif + +unsigned int ConsoleSaveFileOriginal::pagesCommitted = 0; +void *ConsoleSaveFileOriginal::pvHeap = NULL; + +ConsoleSaveFileOriginal::ConsoleSaveFileOriginal(const wstring &fileName, LPVOID pvSaveData /*= NULL*/, DWORD dFileSize /*= 0*/, bool forceCleanSave /*= false*/, ESavePlatform plat /*= SAVE_FILE_PLATFORM_LOCAL*/) +{ + InitializeCriticalSectionAndSpinCount(&m_lock,5120); + + // One time initialise of static stuff required for our storage + if( pvHeap == NULL ) + { + // Reserve a chunk of 64MB of virtual address space for our saves, using 64KB pages. + // We'll only be committing these as required to grow the storage we need, which will + // the storage to grow without having to use realloc. + + // AP - The Vita doesn't have virtual memory so a pretend system has been implemented in PSVitaStubs.cpp. + // All access to the memory must be done via the access function as the pointer returned from VirtualAlloc + // can't be used directly. + pvHeap = VirtualAlloc(NULL, MAX_PAGE_COUNT * CSF_PAGE_SIZE, RESERVE_ALLOCATION, PAGE_READWRITE ); + } + + pvSaveMem = pvHeap; + m_fileName = fileName; + + DWORD fileSize = dFileSize; + + // Load a save from the game rules + bool bLevelGenBaseSave = false; + LevelGenerationOptions *levelGen = app.getLevelGenerationOptions(); + if( pvSaveData == NULL && levelGen != NULL && levelGen->requiresBaseSave()) + { + pvSaveData = levelGen->getBaseSaveData(fileSize); + if(pvSaveData && fileSize != 0) bLevelGenBaseSave = true; + } + + if( pvSaveData == NULL || fileSize == 0) + fileSize = StorageManager.GetSaveSize(); + + if( forceCleanSave ) + fileSize = 0; + + DWORD heapSize = max( fileSize, (DWORD)(1024 * 1024 * 2)); // 4J Stu - Our files are going to be bigger than 2MB so allocate high to start with + + // Initially committ enough room to store headSize bytes (using CSF_PAGE_SIZE pages, so rounding up here). We should only ever have one save file at a time, + // and the pages should be decommitted in the dtor, so pages committed should always be zero at this point. + if( pagesCommitted != 0 ) + { +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + } + + unsigned int pagesRequired = ( heapSize + (CSF_PAGE_SIZE - 1 ) ) / CSF_PAGE_SIZE; + + void *pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE, COMMIT_ALLOCATION, PAGE_READWRITE); + if( pvRet == NULL ) + { +#ifndef _CONTENT_PACKAGE + // Out of physical memory + __debugbreak(); +#endif + } + pagesCommitted = pagesRequired; + + if( fileSize > 0) + { + bool AllocData = false; + if(pvSaveData != NULL) + { +#ifdef __PSVITA__ + // AP - use this to access the virtual memory + VirtualCopyTo(pvSaveMem, pvSaveData, fileSize); +#else + memcpy(pvSaveMem, pvSaveData, fileSize); + if(bLevelGenBaseSave) + { + levelGen->deleteBaseSaveData(); + } +#endif + } + else + { + unsigned int storageLength; +#ifdef __PSVITA__ + // create a buffer to hold the compressed data + pvSaveData = malloc(fileSize); + AllocData = true; + StorageManager.GetSaveData( pvSaveData, &storageLength ); +#else + StorageManager.GetSaveData( pvSaveMem, &storageLength ); +#endif +#ifdef __PS3__ + StorageManager.FreeSaveData(); +#endif + app.DebugPrintf("Filesize - %d, Adjusted size - %d\n",fileSize,storageLength); + fileSize = storageLength; + } + +#ifdef __PSVITA__ + if(plat == SAVE_FILE_PLATFORM_PSVITA) + { + // AP - decompress via the access function. This uses a special RLE format + VirtualDecompress((unsigned char *)pvSaveData+8, fileSize-8 ); + if( AllocData ) + { + // free the compressed data buffer if required + free( pvSaveData ); + } + else if(bLevelGenBaseSave) + { + levelGen->deleteBaseSaveData(); + } + } + else +#endif + { +#ifdef __PSVITA__ + void* pvSourceData = pvSaveData; +#else + void* pvSourceData = pvSaveMem; +#endif + int compressed = *(int*)pvSourceData; + if( compressed == 0 ) + { + unsigned int decompSize = *( (int*)pvSourceData+1 ); + if(isLocalEndianDifferent(plat)) System::ReverseULONG(&decompSize); + + // An invalid save, so clear the memory and start from scratch + if(decompSize == 0) + { + // 4J Stu - Saves created between 2/12/2011 and 7/12/2011 will have this problem + app.DebugPrintf("Invalid save data format\n"); + ZeroMemory( pvSourceData, fileSize ); + // Clear the first 8 bytes that reference the header + header.WriteHeader( pvSourceData ); + } + else + { + unsigned char *buf = new unsigned char[decompSize]; +#ifndef _XBOX + if(plat == SAVE_FILE_PLATFORM_PSVITA) + { + Compression::VitaVirtualDecompress(buf, &decompSize, (unsigned char *)pvSourceData+8, fileSize-8 ); + } + else +#endif + { + Compression::getCompression()->SetDecompressionType(plat); // if this save is from another platform, set the correct decompression type + Compression::getCompression()->Decompress(buf, &decompSize, (unsigned char *)pvSourceData+8, fileSize-8 ); + Compression::getCompression()->SetDecompressionType(SAVE_FILE_PLATFORM_LOCAL); // and then set the decompression back to the local machine's standard type + } + + // Only ReAlloc if we need to (we might already have enough) and align to 512 byte boundaries + DWORD currentHeapSize = pagesCommitted * CSF_PAGE_SIZE; + + DWORD desiredSize = decompSize; + + if( desiredSize > currentHeapSize ) + { + unsigned int pagesRequired = ( desiredSize + (CSF_PAGE_SIZE - 1 ) ) / CSF_PAGE_SIZE; + void *pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE, COMMIT_ALLOCATION, PAGE_READWRITE); + if( pvRet == NULL ) + { + // Out of physical memory + __debugbreak(); + } + pagesCommitted = pagesRequired; + } +#ifdef __PSVITA__ + VirtualCopyTo(pvSaveMem, buf, decompSize); +#else + memcpy(pvSaveMem, buf, decompSize); +#endif + delete[] buf; + } + } + } + + header.ReadHeader( pvSaveMem, plat ); + + } + else + { + // Clear the first 8 bytes that reference the header + header.WriteHeader( pvSaveMem ); + } +} + +ConsoleSaveFileOriginal::~ConsoleSaveFileOriginal() +{ + VirtualFree( pvHeap, MAX_PAGE_COUNT * CSF_PAGE_SIZE, MEM_DECOMMIT ); + pagesCommitted = 0; + // Make sure we don't have any thumbnail data still waiting round - we can't need it now we've destroyed the save file anyway +#if defined _XBOX + app.GetSaveThumbnail(NULL,NULL); +#elif defined __PS3__ + app.GetSaveThumbnail(NULL,NULL, NULL,NULL); +#endif + + DeleteCriticalSection(&m_lock); +} + +// Add the file to our table of internal files if not already there +// Open our actual save file ready for reading/writing, and the set the file pointer to the start of this file +FileEntry *ConsoleSaveFileOriginal::createFile( const ConsoleSavePath &fileName ) +{ + LockSaveAccess(); + FileEntry *file = header.AddFile( fileName.getName() ); + ReleaseSaveAccess(); + + return file; +} + +void ConsoleSaveFileOriginal::deleteFile( FileEntry *file ) +{ + if( file == NULL ) return; + + LockSaveAccess(); + + DWORD numberOfBytesRead = 0; + DWORD numberOfBytesWritten = 0; + + const int bufferSize = 4096; + int amountToRead = bufferSize; + byte buffer[bufferSize]; + DWORD bufferDataSize = 0; + + + char *readStartOffset = (char *)pvSaveMem + file->data.startOffset + file->getFileSize(); + + char *writeStartOffset = (char *)pvSaveMem + file->data.startOffset; + + char *endOfDataOffset = (char *)pvSaveMem + header.GetStartOfNextData(); + + while(true) + { + // Fill buffer from file + if( readStartOffset + bufferSize > endOfDataOffset ) + { + amountToRead = (int)(endOfDataOffset - readStartOffset); + } + else + { + amountToRead = bufferSize; + } + + if( amountToRead == 0 ) + break; + +#ifdef __PSVITA__ + // AP - use this to access the virtual memory + VirtualCopyFrom( buffer, readStartOffset, amountToRead ); +#else + memcpy( buffer, readStartOffset, amountToRead ); +#endif + numberOfBytesRead = amountToRead; + + bufferDataSize = amountToRead; + readStartOffset += numberOfBytesRead; + + // Write buffer to file +#ifdef __PSVITA__ + // AP - use this to access the virtual memory + VirtualCopyTo( (void *)writeStartOffset, buffer, bufferDataSize ); +#else + memcpy( (void *)writeStartOffset, buffer, bufferDataSize ); +#endif + numberOfBytesWritten = bufferDataSize; + + writeStartOffset += numberOfBytesWritten; + } + + header.RemoveFile( file ); + + finalizeWrite(); + + ReleaseSaveAccess(); +} + +void ConsoleSaveFileOriginal::setFilePointer(FileEntry *file,LONG lDistanceToMove,PLONG lpDistanceToMoveHigh,DWORD dwMoveMethod) +{ + LockSaveAccess(); + + file->currentFilePointer = file->data.startOffset + lDistanceToMove; + + if( dwMoveMethod == FILE_END) + { + file->currentFilePointer += file->getFileSize(); + } + + ReleaseSaveAccess(); +} + +// If this file needs to grow, move the data after along +void ConsoleSaveFileOriginal::PrepareForWrite( FileEntry *file, DWORD nNumberOfBytesToWrite ) +{ + int bytesToGrowBy = ( (file->currentFilePointer - file->data.startOffset) + nNumberOfBytesToWrite) - file->getFileSize(); + if( bytesToGrowBy <= 0 ) + return; + + // 4J Stu - Not forcing a minimum size, it is up to the caller to write data in sensible amounts + // This lets us keep some of the smaller files small + //if( bytesToGrowBy < 1024 ) + // bytesToGrowBy = 1024; + + // Move all the data beyond us + MoveDataBeyond(file, bytesToGrowBy); + + // Update our length + if( file->data.length < 0 ) + file->data.length = 0; + file->data.length += bytesToGrowBy; + + // Write the header with the updated data + finalizeWrite(); +} + +BOOL ConsoleSaveFileOriginal::writeFile(FileEntry *file,LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten) +{ + assert( pvSaveMem != NULL ); + if( pvSaveMem == NULL ) + { + return 0; + } + + LockSaveAccess(); + + PrepareForWrite( file, nNumberOfBytesToWrite ); + + char *writeStartOffset = (char *)pvSaveMem + file->currentFilePointer; + //printf("Write: pvSaveMem = %0xd, currentFilePointer = %d, writeStartOffset = %0xd\n", pvSaveMem, file->currentFilePointer, writeStartOffset); + +#ifdef __PSVITA__ + // AP - use this to access the virtual memory + VirtualCopyTo((void *)writeStartOffset, (void*)lpBuffer, nNumberOfBytesToWrite); +#else + memcpy( (void *)writeStartOffset, lpBuffer, nNumberOfBytesToWrite ); +#endif + *lpNumberOfBytesWritten = nNumberOfBytesToWrite; + + if(file->data.length < 0) + file->data.length = 0; + + file->currentFilePointer += *lpNumberOfBytesWritten; + + //wprintf(L"Wrote %d bytes to %s, new file pointer is %I64d\n", *lpNumberOfBytesWritten, file->data.filename, file->currentFilePointer); + + file->updateLastModifiedTime(); + + ReleaseSaveAccess(); + + return 1; +} + +BOOL ConsoleSaveFileOriginal::zeroFile(FileEntry *file, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten) +{ + assert( pvSaveMem != NULL ); + if( pvSaveMem == NULL ) + { + return 0; + } + + LockSaveAccess(); + + PrepareForWrite( file, nNumberOfBytesToWrite ); + + char *writeStartOffset = (char *)pvSaveMem + file->currentFilePointer; + //printf("Write: pvSaveMem = %0xd, currentFilePointer = %d, writeStartOffset = %0xd\n", pvSaveMem, file->currentFilePointer, writeStartOffset); + +#ifdef __PSVITA__ + // AP - use this to access the virtual memory + VirtualMemset( (void *)writeStartOffset, 0, nNumberOfBytesToWrite ); +#else + memset( (void *)writeStartOffset, 0, nNumberOfBytesToWrite ); +#endif + *lpNumberOfBytesWritten = nNumberOfBytesToWrite; + + if(file->data.length < 0) + file->data.length = 0; + + file->currentFilePointer += *lpNumberOfBytesWritten; + + //wprintf(L"Wrote %d bytes to %s, new file pointer is %I64d\n", *lpNumberOfBytesWritten, file->data.filename, file->currentFilePointer); + + file->updateLastModifiedTime(); + + ReleaseSaveAccess(); + + return 1; +} + +BOOL ConsoleSaveFileOriginal::readFile( FileEntry *file, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead) +{ + DWORD actualBytesToRead; + assert( pvSaveMem != NULL ); + if( pvSaveMem == NULL ) + { + return 0; + } + + LockSaveAccess(); + + char *readStartOffset = (char *)pvSaveMem + file->currentFilePointer; + //printf("Read: pvSaveMem = %0xd, currentFilePointer = %d, readStartOffset = %0xd\n", pvSaveMem, file->currentFilePointer, readStartOffset); + + assert( nNumberOfBytesToRead <= file->getFileSize() ); + + actualBytesToRead = nNumberOfBytesToRead; + if( file->currentFilePointer + nNumberOfBytesToRead > file->data.startOffset + file->data.length ) + { + actualBytesToRead = (file->data.startOffset + file->data.length) - file->currentFilePointer; + } + +#ifdef __PSVITA__ + // AP - use this to access the virtual memory + VirtualCopyFrom( lpBuffer, readStartOffset, actualBytesToRead ); +#else + memcpy( lpBuffer, readStartOffset, actualBytesToRead ); +#endif + + *lpNumberOfBytesRead = actualBytesToRead; + + file->currentFilePointer += *lpNumberOfBytesRead; + + //wprintf(L"Read %d bytes from %s, new file pointer is %I64d\n", *lpNumberOfBytesRead, file->data.filename, file->currentFilePointer); + + ReleaseSaveAccess(); + + return 1; +} + +BOOL ConsoleSaveFileOriginal::closeHandle( FileEntry *file ) +{ + LockSaveAccess(); + finalizeWrite(); + ReleaseSaveAccess(); + + return TRUE; +} + +void ConsoleSaveFileOriginal::finalizeWrite() +{ + LockSaveAccess(); + header.WriteHeader( pvSaveMem ); + ReleaseSaveAccess(); +} + +void ConsoleSaveFileOriginal::MoveDataBeyond(FileEntry *file, DWORD nNumberOfBytesToWrite) +{ + DWORD numberOfBytesRead = 0; + DWORD numberOfBytesWritten = 0; + + const DWORD bufferSize = 4096; + DWORD amountToRead = bufferSize; + //assert( nNumberOfBytesToWrite <= bufferSize ); + static byte buffer1[bufferSize]; + static byte buffer2[bufferSize]; + DWORD buffer1Size = 0; + DWORD buffer2Size = 0; + + // Only ReAlloc if we need to (we might already have enough) and align to 512 byte boundaries + DWORD currentHeapSize = pagesCommitted * CSF_PAGE_SIZE; + + DWORD desiredSize = header.GetFileSize() + nNumberOfBytesToWrite; + + if( desiredSize > currentHeapSize ) + { + unsigned int pagesRequired = ( desiredSize + (CSF_PAGE_SIZE - 1 ) ) / CSF_PAGE_SIZE; + void *pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE, COMMIT_ALLOCATION, PAGE_READWRITE); + if( pvRet == NULL ) + { + // Out of physical memory + __debugbreak(); + } + pagesCommitted = pagesRequired; + } + + // This is the start of where we want the space to be, and the start of the data that we need to move + char *spaceStartOffset = (char *)pvSaveMem + file->data.startOffset + file->getFileSize(); + + // This is the end of where we want the space to be + char *spaceEndOffset = spaceStartOffset + nNumberOfBytesToWrite; + + // This is the current end of the data that we want to move + char *beginEndOfDataOffset = (char *)pvSaveMem + header.GetStartOfNextData(); + + // This is where the end of the data is going to be + char *finishEndOfDataOffset = beginEndOfDataOffset + nNumberOfBytesToWrite; + + // This is where we are going to read from (with the amount we want to read subtracted before we read) + char *readStartOffset = beginEndOfDataOffset; + + // This is where we can safely write to (with the amount we want write subtracted before we write) + char *writeStartOffset = finishEndOfDataOffset; + + //printf("\n******* MOVEDATABEYOND *******\n"); + //printf("Space start: %d, space end: %d\n", spaceStartOffset - (char *)pvSaveMem, spaceEndOffset - (char *)pvSaveMem); + //printf("Current end of data: %d, new end of data: %d\n", beginEndOfDataOffset - (char *)pvSaveMem, finishEndOfDataOffset - (char *)pvSaveMem); + + // Optimisation for things that are being moved in whole region file sector (4K chunks). We could generalise this a bit more but seems safest at the moment to identify this particular type + // of move and code explicitly for this situation + if( ( nNumberOfBytesToWrite & 4095 ) == 0 ) + { + if( nNumberOfBytesToWrite > 0 ) + { + // Get addresses for start & end of the region we are copying from as uintptr_t, for easier maths + uintptr_t uiFromStart = (uintptr_t)spaceStartOffset; + uintptr_t uiFromEnd = (uintptr_t)beginEndOfDataOffset; + + // Round both of these values to get 4096 byte chunks that we will need to at least partially move + uintptr_t uiFromStartChunk = uiFromStart & ~((uintptr_t)4095); + uintptr_t uiFromEndChunk = (uiFromEnd - 1 ) & ~((uintptr_t)4095); + + // Loop through all the affected source 4096 chunks, going backwards so we don't overwrite anything we'll need in the future + for( uintptr_t uiCurrentChunk = uiFromEndChunk; uiCurrentChunk >= uiFromStartChunk; uiCurrentChunk -= 4096 ) + { + // Establish chunk we'll need to copy + uintptr_t uiCopyStart = uiCurrentChunk; + uintptr_t uiCopyEnd = uiCurrentChunk + 4096; + // Clamp chunk to the bounds of the full region we are trying to copy + if( uiCopyStart < uiFromStart ) + { + // Needs to be clampged against the start of our region + uiCopyStart = uiFromStart; + } + if ( uiCopyEnd > uiFromEnd ) + { + // Needs to be clamped to the end of our region + uiCopyEnd = uiFromEnd; + } +#ifdef __PSVITA__ + // AP - use this to access the virtual memory + VirtualMove( (void *)(uiCopyStart + nNumberOfBytesToWrite), ( void *)uiCopyStart, uiCopyEnd - uiCopyStart); +#else + XMemCpy( (void *)(uiCopyStart + nNumberOfBytesToWrite), ( void *)uiCopyStart, uiCopyEnd - uiCopyStart ); +#endif + } + } + } + else + { + while(true) + { + // Copy buffer 1 to buffer 2 + memcpy( buffer2, buffer1, buffer1Size); + buffer2Size = buffer1Size; + + // Fill buffer 1 from file + if( (readStartOffset - bufferSize) < spaceStartOffset ) + { + amountToRead = (DWORD)(readStartOffset - spaceStartOffset); + } + else + { + amountToRead = bufferSize; + } + + // Push the read point back by the amount of bytes that we are going to read + readStartOffset -= amountToRead; + + //printf("About to read %u from %d\n", amountToRead, readStartOffset - (char *)pvSaveMem ); +#ifdef __PSVITA__ + // AP - use this to access the virtual memory + VirtualCopyFrom(buffer1, readStartOffset, amountToRead); +#else + memcpy( buffer1, readStartOffset, amountToRead ); +#endif + numberOfBytesRead = amountToRead; + + buffer1Size = amountToRead; + + // Move back the write pointer by the amount of bytes we are going to write + writeStartOffset -= buffer2Size; + + // Write buffer 2 to file + if( (writeStartOffset + buffer2Size) <= finishEndOfDataOffset) + { + //printf("About to write %u to %d\n", buffer2Size, writeStartOffset - (char *)pvSaveMem ); +#ifdef __PSVITA__ + // AP - use this to access the virtual memory + VirtualCopyTo((void *)writeStartOffset, buffer2, buffer2Size); +#else + memcpy( (void *)writeStartOffset, buffer2, buffer2Size ); +#endif + numberOfBytesWritten = buffer2Size; + } + else + { + assert((writeStartOffset + buffer2Size) <= finishEndOfDataOffset); + numberOfBytesWritten = 0; + } + + if( numberOfBytesRead == 0 ) + { + //printf("\n************** MOVE COMPLETED *************** \n\n"); + assert( writeStartOffset == spaceEndOffset ); + break; + } + } + } + + header.AdjustStartOffsets( file, nNumberOfBytesToWrite ); +} + +bool ConsoleSaveFileOriginal::doesFileExist(ConsoleSavePath file) +{ + LockSaveAccess(); + bool exists = header.fileExists( file.getName() ); + ReleaseSaveAccess(); + + return exists; +} + +void ConsoleSaveFileOriginal::Flush(bool autosave, bool updateThumbnail ) +{ + LockSaveAccess(); + + finalizeWrite(); + + // Get the frequency of the timer + LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime; + float fElapsedTime = 0.0f; + QueryPerformanceFrequency( &qwTicksPerSec ); + float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart; + + unsigned int fileSize = header.GetFileSize(); + + // Assume that the compression will make it smaller so initially attempt to allocate the current file size + // We add 4 bytes to the start so that we can signal compressed data + // And another 4 bytes to store the decompressed data size + unsigned int compLength = fileSize+8; + + // 4J Stu - Added TU-1 interim + +#ifdef __PS3__ + // On PS3, don't compress the data as we can't really afford the extra memory this requires for the output buffer. Instead we'll be writing + // directly from the save data. + StorageManager.SetSaveData(pvSaveMem,fileSize); + byte *compData = (byte *)pvSaveMem; +#else + // Attempt to allocate the required memory + // We do not own this, it belongs to the StorageManager + byte *compData = (byte *)StorageManager.AllocateSaveData( compLength ); + +#ifdef __PSVITA__ + // AP - make sure we always allocate just what is needed so it will only SAVE what is needed. + // If we don't do this the StorageManager will save a file of uncompressed size unnecessarily. + compData = NULL; +#endif + + // If we failed to allocate then compData will be NULL + // Pre-calculate the compressed data size so that we can attempt to allocate a smaller buffer + if(compData == NULL) + { + // Length should be 0 here so that the compression call knows that we want to know the length back + compLength = 0; + + // Pre-calculate the buffer size required for the compressed data + PIXBeginNamedEvent(0,"Pre-calc save compression"); + // Save the start time + QueryPerformanceCounter( &qwTime ); +#ifdef __PSVITA__ + // AP - get the compressed size via the access function. This uses a special RLE format + VirtualCompress(NULL,&compLength,pvSaveMem,fileSize); +#else + Compression::getCompression()->Compress(NULL,&compLength,pvSaveMem,fileSize); +#endif + QueryPerformanceCounter( &qwNewTime ); + + qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart; + fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart)); + + app.DebugPrintf("Check buffer size: Elapsed time %f\n", fElapsedTime); + PIXEndNamedEvent(); + + // We add 4 bytes to the start so that we can signal compressed data + // And another 4 bytes to store the decompressed data size + compLength = compLength+8; + + // Attempt to allocate the required memory + compData = (byte *)StorageManager.AllocateSaveData( compLength ); + } +#endif + + if(compData != NULL) + { + // No compression on PS3 - see comment above +#ifndef __PS3__ + // Re-compress all save data before we save it to disk + PIXBeginNamedEvent(0,"Actual save compression"); + // Save the start time + QueryPerformanceCounter( &qwTime ); +#ifdef __PSVITA__ + // AP - compress via the access function. This uses a special RLE format + VirtualCompress(compData+8,&compLength,pvSaveMem,fileSize); +#else + Compression::getCompression()->Compress(compData+8,&compLength,pvSaveMem,fileSize); +#endif + QueryPerformanceCounter( &qwNewTime ); + + qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart; + fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart)); + + app.DebugPrintf("Compress: Elapsed time %f\n", fElapsedTime); + PIXEndNamedEvent(); + + ZeroMemory(compData,8); + int saveVer = 0; + memcpy( compData, &saveVer, sizeof(int) ); + memcpy( compData+4, &fileSize, sizeof(int) ); + + app.DebugPrintf("Save data compressed from %d to %d\n", fileSize, compLength); +#endif + + PBYTE pbThumbnailData=NULL; + DWORD dwThumbnailDataSize=0; + + PBYTE pbDataSaveImage=NULL; + DWORD dwDataSizeSaveImage=0; + +#if ( defined _XBOX || defined _DURANGO ) + app.GetSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize); +#elif ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ ) + app.GetSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize,&pbDataSaveImage,&dwDataSizeSaveImage); +#endif + + BYTE bTextMetadata[88]; + ZeroMemory(bTextMetadata,88); + + __int64 seed = 0; + bool hasSeed = false; + if(MinecraftServer::getInstance()!= NULL && MinecraftServer::getInstance()->levels[0]!=NULL) + { + seed = MinecraftServer::getInstance()->levels[0]->getLevelData()->getSeed(); + hasSeed = true; + } + + int iTextMetadataBytes = app.CreateImageTextData(bTextMetadata, seed, hasSeed, app.GetGameHostOption(eGameHostOption_All), Minecraft::GetInstance()->getCurrentTexturePackId()); + + INT saveOrCheckpointId = 0; + bool validSave = StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId); + TelemetryManager->RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), saveOrCheckpointId, compLength+8); + +#ifdef _XBOX + StorageManager.SaveSaveData( compLength+8,pbThumbnailData,dwThumbnailDataSize,bTextMetadata,iTextMetadataBytes ); + delete [] pbThumbnailData; +#ifndef _CONTENT_PACKAGE + if( app.DebugSettingsOn()) + { + if(app.GetWriteSavesToFolderEnabled() ) + { + DebugFlushToFile(compData, compLength+8); + } + } +#endif + } + else + { + // We have failed to allocate the memory required to save this file. Now what? + } + + ReleaseSaveAccess(); +#elif (defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ || defined _DURANGO || defined _WINDOWS64) + // set the icon and save image + StorageManager.SetSaveImages(pbThumbnailData,dwThumbnailDataSize,pbDataSaveImage,dwDataSizeSaveImage,bTextMetadata,iTextMetadataBytes); + app.DebugPrintf("Save thumbnail size %d\n",dwThumbnailDataSize); + + // save the data + StorageManager.SaveSaveData( &ConsoleSaveFileOriginal::SaveSaveDataCallback, this ); +#ifndef _CONTENT_PACKAGE + if( app.DebugSettingsOn()) + { + if(app.GetWriteSavesToFolderEnabled() ) + { + DebugFlushToFile(compData, compLength+8); + } + } +#endif + ReleaseSaveAccess(); + } +#else + } + ReleaseSaveAccess(); +#endif +} + +#if (defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ || defined _DURANGO || defined _WINDOWS64) + +int ConsoleSaveFileOriginal::SaveSaveDataCallback(LPVOID lpParam,bool bRes) +{ + ConsoleSaveFile *pClass=(ConsoleSaveFile *)lpParam; + + return 0; +} + +#endif + +#ifndef _CONTENT_PACKAGE +void ConsoleSaveFileOriginal::DebugFlushToFile(void *compressedData /*= NULL*/, unsigned int compressedDataSize /*= 0*/) +{ + LockSaveAccess(); + + finalizeWrite(); + + unsigned int fileSize = header.GetFileSize(); + + DWORD numberOfBytesWritten = 0; +#ifdef _XBOX + File targetFileDir(L"GAME:\\Saves"); +#else + File targetFileDir(L"Saves"); +#endif // _XBOX + + if(!targetFileDir.exists()) + targetFileDir.mkdir(); + + wchar_t *fileName = new wchar_t[XCONTENT_MAX_FILENAME_LENGTH+1]; + + SYSTEMTIME t; + GetSystemTime( &t ); + + //14 chars for the digits + //11 chars for the separators + suffix + //25 chars total + wstring cutFileName = m_fileName; + if(m_fileName.length() > XCONTENT_MAX_FILENAME_LENGTH - 25) + { + cutFileName = m_fileName.substr(0, XCONTENT_MAX_FILENAME_LENGTH - 25); + } + swprintf(fileName, XCONTENT_MAX_FILENAME_LENGTH+1, L"\\v%04d-%ls%02d.%02d.%02d.%02d.%02d.mcs",VER_PRODUCTBUILD,cutFileName.c_str(), t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); + +#ifdef _UNICODE + wstring wtemp = targetFileDir.getPath() + wstring(fileName); + LPCWSTR lpFileName = wtemp.c_str(); +#else + LPCSTR lpFileName = wstringtofilename( targetFileDir.getPath() + wstring(fileName) ); +#endif +#ifndef __PSVITA__ + HANDLE hSaveFile = CreateFile( lpFileName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, NULL); +#endif + + if(compressedData != NULL && compressedDataSize > 0) + { +#ifdef __PSVITA__ + // AP - Use the access function to save + VirtualWriteFile( lpFileName, compressedData, compressedDataSize, &numberOfBytesWritten, NULL); +#else + WriteFile( hSaveFile,compressedData,compressedDataSize,&numberOfBytesWritten,NULL); +#endif + assert(numberOfBytesWritten == compressedDataSize); + } + else + { +#ifdef __PSVITA__ + // AP - Use the access function to save + VirtualWriteFile( lpFileName, compressedData, compressedDataSize, &numberOfBytesWritten, NULL); +#else + WriteFile(hSaveFile,pvSaveMem,fileSize,&numberOfBytesWritten,NULL); +#endif + assert(numberOfBytesWritten == fileSize); + } +#ifndef __PSVITA__ + CloseHandle( hSaveFile ); +#endif + + delete[] fileName; + + ReleaseSaveAccess(); +} +#endif + +unsigned int ConsoleSaveFileOriginal::getSizeOnDisk() +{ + return header.GetFileSize(); +} + +wstring ConsoleSaveFileOriginal::getFilename() +{ + return m_fileName; +} + +vector *ConsoleSaveFileOriginal::getFilesWithPrefix(const wstring &prefix) +{ + return header.getFilesWithPrefix( prefix ); +} + +vector *ConsoleSaveFileOriginal::getRegionFilesByDimension(unsigned int dimensionIndex) +{ + return NULL; +} + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) +wstring ConsoleSaveFileOriginal::getPlayerDataFilenameForLoad(const PlayerUID& pUID) +{ + return header.getPlayerDataFilenameForLoad( pUID ); +} +wstring ConsoleSaveFileOriginal::getPlayerDataFilenameForSave(const PlayerUID& pUID) +{ + return header.getPlayerDataFilenameForSave( pUID ); +} +vector *ConsoleSaveFileOriginal::getValidPlayerDatFiles() +{ + return header.getValidPlayerDatFiles(); +} +#endif + +int ConsoleSaveFileOriginal::getSaveVersion() +{ + return header.getSaveVersion(); +} + +int ConsoleSaveFileOriginal::getOriginalSaveVersion() +{ + return header.getOriginalSaveVersion(); +} + +void ConsoleSaveFileOriginal::LockSaveAccess() +{ + EnterCriticalSection(&m_lock); +} + +void ConsoleSaveFileOriginal::ReleaseSaveAccess() +{ + LeaveCriticalSection(&m_lock); +} + +ESavePlatform ConsoleSaveFileOriginal::getSavePlatform() +{ + return header.getSavePlatform(); +} + +bool ConsoleSaveFileOriginal::isSaveEndianDifferent() +{ + return header.isSaveEndianDifferent(); +} + +void ConsoleSaveFileOriginal::setLocalPlatform() +{ + header.setLocalPlatform(); +} + +void ConsoleSaveFileOriginal::setPlatform(ESavePlatform plat) +{ + header.setPlatform(plat); +} + +ByteOrder ConsoleSaveFileOriginal::getSaveEndian() +{ + return header.getSaveEndian(); +} + +ByteOrder ConsoleSaveFileOriginal::getLocalEndian() +{ + return header.getLocalEndian(); +} + +void ConsoleSaveFileOriginal::setEndian(ByteOrder endian) +{ + header.setEndian(endian); +} + +bool ConsoleSaveFileOriginal::isLocalEndianDifferent( ESavePlatform plat ) +{ + return getLocalEndian() != header.getEndian(plat); +} + + +void ConsoleSaveFileOriginal::ConvertRegionFile(File sourceFile) +{ + DWORD numberOfBytesWritten = 0; + DWORD numberOfBytesRead = 0; + + RegionFile sourceRegionFile(this, &sourceFile); + + for(unsigned int x = 0; x < 32; ++x) + { + for(unsigned int z = 0; z < 32; ++z) + { + DataInputStream *dis = sourceRegionFile.getChunkDataInputStream(x,z); + + if(dis) + { + byteArray inData(1024*1024); + int read = dis->read(inData); + dis->close(); + dis->deleteChildStream(); + delete dis; + + DataOutputStream *dos = sourceRegionFile.getChunkDataOutputStream(x,z); + dos->write(inData, 0, read); + + + dos->close(); + dos->deleteChildStream(); + delete dos; + delete inData.data; + + } + + } + } + sourceRegionFile.writeAllOffsets(); // saves all the endian swapped offsets back out to the file (not all of these are written in the above processing). + +} + +void ConsoleSaveFileOriginal::ConvertToLocalPlatform() +{ + if(getSavePlatform() == SAVE_FILE_PLATFORM_LOCAL) + { + // already in the correct format + return; + } + // convert each of the region files to the local platform + vector *allFilesInSave = getFilesWithPrefix(wstring(L"")); + for(AUTO_VAR(it, allFilesInSave->begin()); it < allFilesInSave->end(); ++it) + { + FileEntry *fe = *it; + wstring fName( fe->data.filename ); + wstring suffix(L".mcr"); + if( fName.compare(fName.length() - suffix.length(), suffix.length(), suffix) == 0 ) + { + app.DebugPrintf("Processing a region file: %ls\n",fName.c_str()); + ConvertRegionFile(File(fe->data.filename) ); + } + else + { + app.DebugPrintf("%ls is not a region file, ignoring\n", fName.c_str()); + } + } + + setLocalPlatform(); // set the platform of this save to the local platform, now that it's been coverted +} + +void *ConsoleSaveFileOriginal::getWritePointer(FileEntry *file) +{ + return (char *)pvSaveMem + file->currentFilePointer;; +} diff --git a/Minecraft.World/ConsoleSaveFileOriginal.h b/Minecraft.World/ConsoleSaveFileOriginal.h new file mode 100644 index 00000000..fd35aa1d --- /dev/null +++ b/Minecraft.World/ConsoleSaveFileOriginal.h @@ -0,0 +1,94 @@ +#pragma once + +#include "FileHeader.h" +#include "ConsoleSavePath.h" +#include "ConsoleSaveFile.h" + +class ConsoleSaveFileOriginal : public ConsoleSaveFile +{ +private: + FileHeader header; + + wstring m_fileName; + +// HANDLE hHeap; + static void *pvHeap; + static unsigned int pagesCommitted; +#ifdef _LARGE_WORLDS + static const unsigned int CSF_PAGE_SIZE = 64 * 1024; + static const unsigned int MAX_PAGE_COUNT = 32 * 1024; // 2GB virtual allocation +#elif defined(__PS3__) + static const unsigned int CSF_PAGE_SIZE = 1024 * 1024; + static const unsigned int MAX_PAGE_COUNT = 64; +#else + static const unsigned int CSF_PAGE_SIZE = 64 * 1024; + static const unsigned int MAX_PAGE_COUNT = 1024; +#endif + LPVOID pvSaveMem; + + CRITICAL_SECTION m_lock; + + void PrepareForWrite( FileEntry *file, DWORD nNumberOfBytesToWrite ); + void MoveDataBeyond(FileEntry *file, DWORD nNumberOfBytesToWrite); + +public: +#if (defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ || defined _DURANGO || defined _WINDOWS64) + static int SaveSaveDataCallback(LPVOID lpParam,bool bRes); +#endif + ConsoleSaveFileOriginal(const wstring &fileName, LPVOID pvSaveData = NULL, DWORD fileSize = 0, bool forceCleanSave = false, ESavePlatform plat = SAVE_FILE_PLATFORM_LOCAL); + virtual ~ConsoleSaveFileOriginal(); + + // 4J Stu - Initial implementation is intended to have a similar interface to the standard Xbox file access functions + + virtual FileEntry *createFile( const ConsoleSavePath &fileName ); + virtual void deleteFile( FileEntry *file ); + + virtual void setFilePointer(FileEntry *file,LONG lDistanceToMove,PLONG lpDistanceToMoveHigh,DWORD dwMoveMethod); + virtual BOOL writeFile( FileEntry *file, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten ); + virtual BOOL zeroFile(FileEntry *file, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten); + virtual BOOL readFile( FileEntry *file, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead ); + virtual BOOL closeHandle( FileEntry *file ); + + virtual void finalizeWrite(); + + virtual bool doesFileExist(ConsoleSavePath file); + + virtual void Flush(bool autosave, bool updateThumbnail = true); + +#ifndef _CONTENT_PACKAGE + virtual void DebugFlushToFile(void *compressedData = NULL, unsigned int compressedDataSize = 0); +#endif + virtual unsigned int getSizeOnDisk(); + + virtual wstring getFilename(); + + virtual vector *getFilesWithPrefix(const wstring &prefix); + virtual vector *getRegionFilesByDimension(unsigned int dimensionIndex); + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + virtual wstring getPlayerDataFilenameForLoad(const PlayerUID& pUID); + virtual wstring getPlayerDataFilenameForSave(const PlayerUID& pUID); + virtual vector *getValidPlayerDatFiles(); +#endif //__PS3__ + + virtual int getSaveVersion(); + virtual int getOriginalSaveVersion(); + + virtual void LockSaveAccess(); + virtual void ReleaseSaveAccess(); + + virtual ESavePlatform getSavePlatform(); + virtual bool isSaveEndianDifferent(); + virtual void setLocalPlatform(); + virtual void setPlatform(ESavePlatform plat); + virtual ByteOrder getSaveEndian(); + virtual ByteOrder getLocalEndian(); + virtual void setEndian(ByteOrder endian); + virtual bool isLocalEndianDifferent(ESavePlatform plat); + + virtual void ConvertRegionFile(File sourceFile); + virtual void ConvertToLocalPlatform(); + +protected: + virtual void *getWritePointer(FileEntry *file); +}; \ No newline at end of file diff --git a/Minecraft.World/ConsoleSaveFileOutputStream.cpp b/Minecraft.World/ConsoleSaveFileOutputStream.cpp new file mode 100644 index 00000000..3d8bb3f7 --- /dev/null +++ b/Minecraft.World/ConsoleSaveFileOutputStream.cpp @@ -0,0 +1,130 @@ +#include "stdafx.h" +#include "File.h" +#include "ConsoleSaveFileOutputStream.h" + +#include "ConsoleSaveFile.h" + +//Creates a file output stream to write to the file represented by the specified File object. A new FileDescriptor object is +//created to represent this file connection. +//First, if there is a security manager, its checkWrite method is called with the path represented by the file argument as its argument. +// +//If the file exists but is a directory rather than a regular file, does not exist but cannot be created, or cannot be opened +//for any other reason then a FileNotFoundException is thrown. +// +//Parameters: +//file - the file to be opened for writing. +ConsoleSaveFileOutputStream::ConsoleSaveFileOutputStream(ConsoleSaveFile *saveFile, const ConsoleSavePath &file) +{ + m_saveFile = saveFile; + + m_file = m_saveFile->createFile(file); + + m_saveFile->setFilePointer( m_file, 0, NULL, FILE_BEGIN ); +} + +ConsoleSaveFileOutputStream::ConsoleSaveFileOutputStream(ConsoleSaveFile *saveFile, FileEntry *file) +{ + m_saveFile = saveFile; + + m_file = file; + + m_saveFile->setFilePointer( m_file, 0, NULL, FILE_BEGIN ); +} + +//Writes the specified byte to this file output stream. Implements the write method of OutputStream. +//Parameters: +//b - the byte to be written. +void ConsoleSaveFileOutputStream::write(unsigned int b) +{ + DWORD numberOfBytesWritten; + + byte value = (byte) b; + + BOOL result = m_saveFile->writeFile( + m_file, + &value, // data buffer + 1, // number of bytes to write + &numberOfBytesWritten // number of bytes written + ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + } + else if( numberOfBytesWritten == 0 ) + { + // File pointer is past the end of the file + } +} + +//Writes b.length bytes from the specified byte array to this file output stream. +//Parameters: +//b - the data. +void ConsoleSaveFileOutputStream::write(byteArray b) +{ + DWORD numberOfBytesWritten; + + BOOL result = m_saveFile->writeFile( + m_file, + &b.data, // data buffer + b.length, // number of bytes to write + &numberOfBytesWritten // number of bytes written + ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + } + else if( numberOfBytesWritten == 0 || numberOfBytesWritten != b.length ) + { + // File pointer is past the end of the file + } +} + +//Writes len bytes from the specified byte array starting at offset off to this file output stream. +//Parameters: +//b - the data. +//off - the start offset in the data. +//len - the number of bytes to write. +void ConsoleSaveFileOutputStream::write(byteArray b, unsigned int offset, unsigned int length) +{ + // 4J Stu - We don't want to write any more than the array buffer holds + assert( length <= ( b.length - offset ) ); + + DWORD numberOfBytesWritten; + + BOOL result = m_saveFile->writeFile( + m_file, + &b[offset], // data buffer + length, // number of bytes to write + &numberOfBytesWritten // number of bytes written + ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + } + else if( numberOfBytesWritten == 0 || numberOfBytesWritten != length ) + { + // File pointer is past the end of the file + } +} +// +//Closes this file output stream and releases any system resources associated with this stream. +//This file output stream may no longer be used for writing bytes. +//If this stream has an associated channel then the channel is closed as well. +void ConsoleSaveFileOutputStream::close() +{ + if( m_saveFile != NULL ) + { + BOOL result = m_saveFile->closeHandle( m_file ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + } + + // Stop the dtor from trying to close it again + m_saveFile = NULL; + } +} diff --git a/Minecraft.World/ConsoleSaveFileOutputStream.h b/Minecraft.World/ConsoleSaveFileOutputStream.h new file mode 100644 index 00000000..1fddeb73 --- /dev/null +++ b/Minecraft.World/ConsoleSaveFileOutputStream.h @@ -0,0 +1,25 @@ +#pragma once +// 4J Stu - Implements the Java InputStream but rather than writing directly to disc it writes through the save file + +#include "OutputStream.h" + +#include "ConsoleSavePath.h" + +class ConsoleSaveFile; +class FileEntry; + +class ConsoleSaveFileOutputStream : public OutputStream +{ +public: + ConsoleSaveFileOutputStream(ConsoleSaveFile *saveFile, const ConsoleSavePath &file); + ConsoleSaveFileOutputStream(ConsoleSaveFile *saveFile, FileEntry *file); + virtual void write(unsigned int b); + virtual void write(byteArray b); + virtual void write(byteArray b, unsigned int offset, unsigned int length); + virtual void close(); + virtual void flush() {} + +private: + ConsoleSaveFile *m_saveFile; + FileEntry *m_file; +}; \ No newline at end of file diff --git a/Minecraft.World/ConsoleSaveFileSplit.cpp b/Minecraft.World/ConsoleSaveFileSplit.cpp new file mode 100644 index 00000000..2d83f217 --- /dev/null +++ b/Minecraft.World/ConsoleSaveFileSplit.cpp @@ -0,0 +1,1711 @@ +#include "stdafx.h" +#include "StringHelpers.h" +#include "ConsoleSaveFileSplit.h" +#include "ConsoleSaveFileConverter.h" +#include "File.h" +#include +#include "compression.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\MinecraftServer.h" +#include "..\Minecraft.Client\ServerLevel.h" +#include "..\Minecraft.World\net.minecraft.world.level.h" +#include "..\Minecraft.World\LevelData.h" +#include "..\Minecraft.Client\Common\GameRules\LevelGenerationOptions.h" +#include "..\Minecraft.World\net.minecraft.world.level.chunk.storage.h" + +#define RESERVE_ALLOCATION MEM_RESERVE +#define COMMIT_ALLOCATION MEM_COMMIT + +unsigned int ConsoleSaveFileSplit::pagesCommitted = 0; +void *ConsoleSaveFileSplit::pvHeap = NULL; + +ConsoleSaveFileSplit::RegionFileReference::RegionFileReference(int index, unsigned int regionIndex, unsigned int length/*=0*/, unsigned char *data/*=NULL*/) +{ + fileEntry = new FileEntry(); + fileEntry->currentFilePointer = 0; + fileEntry->data.length = 0; + fileEntry->data.regionIndex = regionIndex; + this->data = 0; + this->index = index; + this->dirty = false; + this->dataCompressed = data; + this->dataCompressedSize = length; + this->lastWritten = 0; +} + +ConsoleSaveFileSplit::RegionFileReference::~RegionFileReference() +{ + free(data); + delete fileEntry; +} + +// Compress from data to dataCompressed. Uses a special compression method that is designed just to efficiently store runs of zeros, with little overhead on other stuff. +// Compresed format is a 4 byte uncompressed size, followed by data as follows: +// +// Byte value Meaning +// +// 1 - 255 Normal data +// 0 followed by 1 - 255 Run of 1 - 255 0s +// 0 followed by 0, followed by 256 to 65791 (as 2 bytes) Run of 256 to 65791 zeros + +void ConsoleSaveFileSplit::RegionFileReference::Compress() +{ + unsigned char *dataIn = data; + unsigned char *dataInLast = data + fileEntry->data.length; + +// int64_t startTime = System::currentTimeMillis(); + + // One pass through to work out storage space required for compressed data + unsigned int outputSize = 4; // 4 bytes required to store the uncompressed size for faster decompression + unsigned int runLength = 0; + while( dataIn != dataInLast ) + { + unsigned char thisByte = *dataIn++; + if( ( thisByte != 0 ) || ( runLength == ( 65535 + 256 ) ) ) + { + // We've got a non-zero value, or we've hit our maximum run length. + // If there was a preceeding run of zeros, encode that nwo + if( runLength != 0 ) + { + if( runLength < 256 ) + { + // Runs of 1 to 255 encoded as 0 followed by one byte of run length + outputSize += 2; + } + else + { + // Runs of 256 to 65791 encoded as two 0s followed by two bytes of run length - 256 + outputSize += 4; + } + // Run is now processed + runLength = 0; + } + // Now handle the current byte + if( thisByte == 0 ) + { + runLength++; + } + else + { + // Non-zero, just copy over to output + outputSize++; + } + } + else + { + // It's a zero - keep counting size of the run + runLength++; + } + } + // Handle any outstanding run + if ( runLength != 0 ) + { + if( runLength < 256 ) + { + // Runs of 1 to 255 encoded as 0 followed by one byte of run length + outputSize += 2; + } + else + { + // Runs of 256 to 65791 encoded as two 0s followed by two bytes of run length - 256 + outputSize += 4; + } + // Run is now processed + runLength = 0; + } + + // Now actually allocate & write the compress data. First 4 bytes store the uncompressed size + dataCompressed = (unsigned char *)malloc(outputSize); + *((unsigned int *)dataCompressed) = fileEntry->data.length; + unsigned char *dataOut = dataCompressed + 4; + dataIn = data; + + // Now same process as before, but actually writing + while( dataIn != dataInLast ) + { + unsigned char thisByte = *dataIn++; + if( ( thisByte != 0 ) || ( runLength == ( 65535 + 256 ) ) ) + { + // We've got a non-zero value, or we've hit our maximum run length. + // If there was a preceeding run of zeros, encode that nwo + if( runLength != 0 ) + { + if( runLength < 256 ) + { + // Runs of 1 to 255 encoded as 0 followed by one byte of run length + *dataOut++ = 0; + *dataOut++ = runLength; + } + else + { + // Runs of 256 to 65791 encoded as two 0s followed by two bytes of run length - 256 + *dataOut++ = 0; + *dataOut++ = 0; + unsigned int largeRunLength = runLength - 256; + *dataOut++ = ( largeRunLength >> 8 ) & 0xff; + *dataOut++ = ( largeRunLength ) & 0xff; + } + // Run is now processed + runLength = 0; + } + // Now handle the current byte + if( thisByte == 0 ) + { + runLength++; + } + else + { + // Non-zero, just copy over to output + *dataOut++ = thisByte; + } + } + else + { + // It's a zero - keep counting size of the run + runLength++; + } + } + // Handle any outstanding run + if( runLength != 0 ) + { + if( runLength < 256 ) + { + // Runs of 1 to 255 encoded as 0 followed by one byte of run length + *dataOut++ = 0; + *dataOut++ = runLength; + } + else + { + // Runs of 256 to 65791 encoded as two 0s followed by two bytes of run length - 256 + *dataOut++ = 0; + *dataOut++ = 0; + unsigned int largeRunLength = runLength - 256; + *dataOut++ = ( largeRunLength >> 8 ) & 0xff; + *dataOut++ = ( largeRunLength ) & 0xff; + } + // Run is now processed + runLength = 0; + } + assert(( dataOut - dataCompressed ) == outputSize ); + dataCompressedSize = outputSize; +// int64_t endTime = System::currentTimeMillis(); +// app.DebugPrintf("Compressing region file 0x%.8x from %d to %d bytes - %dms\n", fileEntry->data.regionIndex, fileEntry->data.length, dataCompressedSize, endTime - startTime); +} + +// Decompress from dataCompressed -> data. See comment in Compress method for format +void ConsoleSaveFileSplit::RegionFileReference::Decompress() +{ +// int64_t startTime = System::currentTimeMillis(); + fileEntry->data.length = *((unsigned int *)dataCompressed); + + // If this is unusually large, then test how big it would be when expanded before trying to allocate. Matching the expanded size + // is (currently) our means of knowing that this file is ok + if( fileEntry->data.length > 1 * 1024 * 1024 ) + { + unsigned int uncompressedSize = 0; + unsigned char *dataIn = dataCompressed + 4; + unsigned char *dataInLast = dataCompressed + dataCompressedSize; + + while (dataIn != dataInLast) + { + unsigned char thisByte = *dataIn++; + if( thisByte == 0 ) + { + thisByte = *dataIn++; + if( thisByte == 0 ) + { + unsigned int runLength = (*dataIn++) << 8; + runLength |= (*dataIn++); + runLength += 256; + uncompressedSize += runLength; + } + else + { + unsigned int runLength = thisByte; + uncompressedSize += runLength; + } + } + else + { + uncompressedSize++; + } + } + + if( fileEntry->data.length != uncompressedSize ) + { + // Treat as if it was an empty region file + fileEntry->data.length = 0; + assert(0); + return; + } + } + + + data = (unsigned char *)malloc(fileEntry->data.length); + unsigned char *dataIn = dataCompressed + 4; + unsigned char *dataInLast = dataCompressed + dataCompressedSize; + unsigned char *dataOut = data; + + while (dataIn != dataInLast) + { + unsigned char thisByte = *dataIn++; + if( thisByte == 0 ) + { + thisByte = *dataIn++; + if( thisByte == 0 ) + { + unsigned int runLength = (*dataIn++) << 8; + runLength |= (*dataIn++); + runLength += 256; + for( unsigned int i = 0; i < runLength; i++ ) + { + *dataOut++ = 0; + } + } + else + { + unsigned int runLength = thisByte; + for( unsigned int i = 0; i < runLength; i++ ) + { + *dataOut++ = 0; + } + } + } + else + { + *dataOut++ = thisByte; + } + } + // If we failed to correctly decompress, then treat as if it was an empty region file + if( ( dataOut - data ) != fileEntry->data.length ) + { + free(data); + fileEntry->data.length = 0; + data = NULL; + assert(0); + } +// int64_t endTime = System::currentTimeMillis(); +// app.DebugPrintf("Decompressing region file from 0x%.8x %d to %d bytes - %dms\n", fileEntry->data.regionIndex, dataCompressedSize, fileEntry->data.length, endTime - startTime);// +} + +unsigned int ConsoleSaveFileSplit::RegionFileReference::GetCompressedSize() +{ + unsigned char *dataIn = data; + unsigned char *dataInLast = data + fileEntry->data.length; + + unsigned int outputSize = 4; // 4 bytes required to store the uncompressed size for faster decompression + unsigned int runLength = 0; + while( dataIn != dataInLast ) + { + unsigned char thisByte = *dataIn++; + if( ( thisByte != 0 ) || ( runLength == ( 65535 + 256 ) ) ) + { + // We've got a non-zero value, or we've hit our maximum run length. + // If there was a preceeding run of zeros, encode that nwo + if( runLength != 0 ) + { + if( runLength < 256 ) + { + // Runs of 1 to 255 encoded as 0 followed by one byte of run length + outputSize += 2; + } + else + { + // Runs of 256 to 65791 encoded as two 0s followed by two bytes of run length - 256 + outputSize += 4; + } + // Run is now processed + runLength = 0; + } + // Now handle the current byte + if( thisByte == 0 ) + { + runLength++; + } + else + { + // Non-zero, just copy over to output + outputSize++; + } + } + else + { + // It's a zero - keep counting size of the run + runLength++; + } + } + // Handle any outstanding run + if ( runLength != 0 ) + { + if( runLength < 256 ) + { + // Runs of 1 to 255 encoded as 0 followed by one byte of run length + outputSize += 2; + } + else + { + // Runs of 256 to 65791 encoded as two 0s followed by two bytes of run length - 256 + outputSize += 4; + } + // Run is now processed + runLength = 0; + } + return outputSize; +} + +// Release dataCompressed +void ConsoleSaveFileSplit::RegionFileReference::ReleaseCompressed() +{ +// app.DebugPrintf("Releasing compressed data for region file from 0x%.8x\n", fileEntry->data.regionIndex ); + free(dataCompressed); + dataCompressed = NULL; + dataCompressedSize = NULL; +} + +FileEntry *ConsoleSaveFileSplit::GetRegionFileEntry(unsigned int regionIndex) +{ + // Is a region file - determine if we've got it as a separate file + AUTO_VAR(it, regionFiles.find(regionIndex) ); + if( it != regionFiles.end() ) + { + // Already got it + return it->second->fileEntry; + } + + int index = StorageManager.AddSubfile(regionIndex); + RegionFileReference *newRef = new RegionFileReference(index, regionIndex); + regionFiles[regionIndex] = newRef; + + return newRef->fileEntry; +} + +ConsoleSaveFileSplit::ConsoleSaveFileSplit(const wstring &fileName, LPVOID pvSaveData /*= NULL*/, DWORD dFileSize /*= 0*/, bool forceCleanSave /*= false*/, ESavePlatform plat /*= SAVE_FILE_PLATFORM_LOCAL*/) +{ + DWORD fileSize = dFileSize; + + // Load a save from the game rules + bool bLevelGenBaseSave = false; + LevelGenerationOptions *levelGen = app.getLevelGenerationOptions(); + if( pvSaveData == NULL && levelGen != NULL && levelGen->requiresBaseSave()) + { + pvSaveData = levelGen->getBaseSaveData(fileSize); + if(pvSaveData && fileSize != 0) bLevelGenBaseSave = true; + } + + if( pvSaveData == NULL || fileSize == 0) + fileSize = StorageManager.GetSaveSize(); + + if( forceCleanSave ) + fileSize = 0; + + _init(fileName, pvSaveData, fileSize, plat); + + if(bLevelGenBaseSave) + { + levelGen->deleteBaseSaveData(); + } +} + +ConsoleSaveFileSplit::ConsoleSaveFileSplit(ConsoleSaveFile *sourceSave, bool alreadySmallRegions, ProgressListener *progress) +{ + _init(sourceSave->getFilename(), NULL, 0, sourceSave->getSavePlatform()); + + header.setOriginalSaveVersion(sourceSave->getOriginalSaveVersion()); + header.setSaveVersion(sourceSave->getSaveVersion()); + + if(alreadySmallRegions) + { + + vector *sourceFiles = sourceSave->getFilesWithPrefix(L""); + + DWORD bytesWritten; + for(AUTO_VAR(it, sourceFiles->begin()); it != sourceFiles->end(); ++it) + { + FileEntry *sourceEntry = *it; + sourceSave->setFilePointer(sourceEntry,0,NULL,FILE_BEGIN); + + FileEntry *targetEntry = createFile(ConsoleSavePath(sourceEntry->data.filename)); + + writeFile(targetEntry, sourceSave->getWritePointer(sourceEntry), sourceEntry->getFileSize(), &bytesWritten); + } + + delete sourceFiles; + } + else + { + ConsoleSaveFileConverter::ConvertSave(sourceSave, this, progress); + } +} + +void ConsoleSaveFileSplit::_init(const wstring &fileName, LPVOID pvSaveData, DWORD fileSize, ESavePlatform plat) +{ + InitializeCriticalSectionAndSpinCount(&m_lock,5120); + + m_lastTickTime = 0; + + // One time initialise of static stuff required for our storage + if( pvHeap == NULL ) + { + // Reserve a chunk of 64MB of virtual address space for our saves, using 64KB pages. + // We'll only be committing these as required to grow the storage we need, which will + // the storage to grow without having to use realloc. + pvHeap = VirtualAlloc(NULL, MAX_PAGE_COUNT * CSF_PAGE_SIZE, RESERVE_ALLOCATION, PAGE_READWRITE ); + } + + pvSaveMem = pvHeap; + m_fileName = fileName; + + // Get details of region files. From this point on we are responsible for the memory that the storage manager initially allocated for them + unsigned int regionCount = StorageManager.GetSubfileCount(); + for( unsigned int i = 0; i < regionCount; i++ ) + { + unsigned int regionIndex; + unsigned char *regionDataCompressed; + unsigned int regionSizeCompressed; + + StorageManager.GetSubfileDetails(i, ®ionIndex, ®ionDataCompressed, ®ionSizeCompressed); + + RegionFileReference *regionFileRef = new RegionFileReference(i, regionIndex, regionSizeCompressed, regionDataCompressed); + if( regionSizeCompressed > 0 ) + { + regionFileRef->Decompress(); + } + else + { + regionFileRef->fileEntry->data.length = 0; + } + regionFileRef->ReleaseCompressed(); + regionFiles[regionIndex] = regionFileRef; + } + + DWORD heapSize = max( fileSize, (DWORD)(1024 * 1024 * 2)); // 4J Stu - Our files are going to be bigger than 2MB so allocate high to start with + + // Initially committ enough room to store headSize bytes (using CSF_PAGE_SIZE pages, so rounding up here). We should only ever have one save file at a time, + // and the pages should be decommitted in the dtor, so pages committed should always be zero at this point. + if( pagesCommitted != 0 ) + { +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + } + + unsigned int pagesRequired = ( heapSize + (CSF_PAGE_SIZE - 1 ) ) / CSF_PAGE_SIZE; + + void *pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE, COMMIT_ALLOCATION, PAGE_READWRITE); + if( pvRet == NULL ) + { +#ifndef _CONTENT_PACKAGE + // Out of physical memory + __debugbreak(); +#endif + } + pagesCommitted = pagesRequired; + + if( fileSize > 0) + { + if(pvSaveData != NULL) + { + memcpy(pvSaveMem, pvSaveData, fileSize); + } + else + { + unsigned int storageLength; + StorageManager.GetSaveData( pvSaveMem, &storageLength ); + app.DebugPrintf("Filesize - %d, Adjusted size - %d\n",fileSize,storageLength); + fileSize = storageLength; + } + + int compressed = *(int*)pvSaveMem; + if( compressed == 0 ) + { + unsigned int decompSize = *( (int*)pvSaveMem+1 ); + + // An invalid save, so clear the memory and start from scratch + if(decompSize == 0) + { + // 4J Stu - Saves created between 2/12/2011 and 7/12/2011 will have this problem + app.DebugPrintf("Invalid save data format\n"); + ZeroMemory( pvSaveMem, fileSize ); + // Clear the first 8 bytes that reference the header + header.WriteHeader( pvSaveMem ); + } + else + { + unsigned char *buf = new unsigned char[decompSize]; + + if( Compression::getCompression()->Decompress(buf, &decompSize, (unsigned char *)pvSaveMem+8, fileSize-8 ) == S_OK) + { + + // Only ReAlloc if we need to (we might already have enough) and align to 512 byte boundaries + DWORD currentHeapSize = pagesCommitted * CSF_PAGE_SIZE; + + DWORD desiredSize = decompSize; + + if( desiredSize > currentHeapSize ) + { + unsigned int pagesRequired = ( desiredSize + (CSF_PAGE_SIZE - 1 ) ) / CSF_PAGE_SIZE; + void *pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE, COMMIT_ALLOCATION, PAGE_READWRITE); + if( pvRet == NULL ) + { + // Out of physical memory + __debugbreak(); + } + pagesCommitted = pagesRequired; + } + + memcpy(pvSaveMem, buf, decompSize); + } + else + { + // Corrupt save, although most of the terrain should actually be ok + app.DebugPrintf("Failed to decompress save data!\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + ZeroMemory( pvSaveMem, fileSize ); + // Clear the first 8 bytes that reference the header + header.WriteHeader( pvSaveMem ); + } + + delete[] buf; + } + } + + header.ReadHeader( pvSaveMem, plat ); + + } + else + { + // Clear the first 8 bytes that reference the header + header.WriteHeader( pvSaveMem ); + } +} + +ConsoleSaveFileSplit::~ConsoleSaveFileSplit() +{ + VirtualFree( pvHeap, MAX_PAGE_COUNT * CSF_PAGE_SIZE, MEM_DECOMMIT ); + pagesCommitted = 0; + // Make sure we don't have any thumbnail data still waiting round - we can't need it now we've destroyed the save file anyway +#if defined _XBOX + app.GetSaveThumbnail(NULL,NULL); +#elif defined __PS3__ + app.GetSaveThumbnail(NULL,NULL, NULL,NULL); +#endif + + for(AUTO_VAR(it,regionFiles.begin()); it != regionFiles.end(); it++ ) + { + delete it->second; + } + + StorageManager.ResetSubfiles(); + DeleteCriticalSection(&m_lock); +} + +// Add the file to our table of internal files if not already there +// Open our actual save file ready for reading/writing, and the set the file pointer to the start of this file +FileEntry *ConsoleSaveFileSplit::createFile( const ConsoleSavePath &fileName ) +{ + LockSaveAccess(); + + // Determine if the file is a region file that should be split off into its own file + unsigned int regionFileIndex; + bool isRegionFile = GetNumericIdentifierFromName(fileName.getName(), ®ionFileIndex); + if( isRegionFile ) + { + // First, for backwards compatibility, check if it is already in the main file - will just use that if so + if( !header.fileExists( fileName.getName() ) ) + { + // Find or create a new region file + FileEntry *file = GetRegionFileEntry(regionFileIndex); + ReleaseSaveAccess(); + return file; + } + } + + FileEntry *file = header.AddFile( fileName.getName() ); + ReleaseSaveAccess(); + + return file; +} + +void ConsoleSaveFileSplit::deleteFile( FileEntry *file ) +{ + if( file == NULL ) return; + + assert( file->isRegionFile() == false ); + + LockSaveAccess(); + + DWORD numberOfBytesRead = 0; + DWORD numberOfBytesWritten = 0; + + const int bufferSize = 4096; + int amountToRead = bufferSize; + byte buffer[bufferSize]; + DWORD bufferDataSize = 0; + + + char *readStartOffset = (char *)pvSaveMem + file->data.startOffset + file->getFileSize(); + + char *writeStartOffset = (char *)pvSaveMem + file->data.startOffset; + + char *endOfDataOffset = (char *)pvSaveMem + header.GetStartOfNextData(); + + while(true) + { + // Fill buffer from file + if( readStartOffset + bufferSize > endOfDataOffset ) + { + amountToRead = (int)(endOfDataOffset - readStartOffset); + } + else + { + amountToRead = bufferSize; + } + + if( amountToRead == 0 ) + break; + + memcpy( buffer, readStartOffset, amountToRead ); + numberOfBytesRead = amountToRead; + + bufferDataSize = amountToRead; + readStartOffset += numberOfBytesRead; + + // Write buffer to file + memcpy( (void *)writeStartOffset, buffer, bufferDataSize ); + numberOfBytesWritten = bufferDataSize; + + writeStartOffset += numberOfBytesWritten; + } + + header.RemoveFile( file ); + + finalizeWrite(); + + ReleaseSaveAccess(); +} + +void ConsoleSaveFileSplit::setFilePointer(FileEntry *file,LONG lDistanceToMove,PLONG lpDistanceToMoveHigh,DWORD dwMoveMethod) +{ + LockSaveAccess(); + + if( file->isRegionFile() ) + { + file->currentFilePointer = lDistanceToMove; + } + else + { + file->currentFilePointer = file->data.startOffset + lDistanceToMove; + } + + if( dwMoveMethod == FILE_END) + { + file->currentFilePointer += file->getFileSize(); + } + + ReleaseSaveAccess(); +} + +// If this file needs to grow, move the data after along +void ConsoleSaveFileSplit::PrepareForWrite( FileEntry *file, DWORD nNumberOfBytesToWrite ) +{ + int bytesToGrowBy = ( (file->currentFilePointer - file->data.startOffset) + nNumberOfBytesToWrite) - file->getFileSize(); + if( bytesToGrowBy <= 0 ) + return; + + // 4J Stu - Not forcing a minimum size, it is up to the caller to write data in sensible amounts + // This lets us keep some of the smaller files small + //if( bytesToGrowBy < 1024 ) + // bytesToGrowBy = 1024; + + // Move all the data beyond us + PIXBeginNamedEvent(0,"Growing file by %d bytes", bytesToGrowBy); + MoveDataBeyond(file, bytesToGrowBy); + PIXEndNamedEvent(); + + // Update our length + if( file->data.length < 0 ) + file->data.length = 0; + file->data.length += bytesToGrowBy; + + // Write the header with the updated data + finalizeWrite(); +} + +BOOL ConsoleSaveFileSplit::writeFile(FileEntry *file,LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten) +{ + assert( pvSaveMem != NULL ); + if( pvSaveMem == NULL ) + { + return 0; + } + + LockSaveAccess(); + + if( file->isRegionFile() ) + { + unsigned int sizeRequired = file->currentFilePointer + nNumberOfBytesToWrite; + RegionFileReference *fileRef = regionFiles[file->data.regionIndex]; + if( sizeRequired > file->getFileSize() ) + { + fileRef->data = (unsigned char *)realloc(fileRef->data, sizeRequired); + file->data.length = sizeRequired; + } + + memcpy( fileRef->data + file->currentFilePointer, lpBuffer, nNumberOfBytesToWrite ); + +// app.DebugPrintf(">>>>>>>>>>>>>> writing a region file's data 0x%.8x, 0x%x offset %d of %d bytes (writing %d bytes)\n",file->data.regionIndex,fileRef->data,file->currentFilePointer, file->getFileSize(), nNumberOfBytesToWrite); + + file->currentFilePointer += nNumberOfBytesToWrite; + file->updateLastModifiedTime(); + fileRef->dirty = true; + } + else + { + PrepareForWrite( file, nNumberOfBytesToWrite ); + + char *writeStartOffset = (char *)pvSaveMem + file->currentFilePointer; + //printf("Write: pvSaveMem = %0xd, currentFilePointer = %d, writeStartOffset = %0xd\n", pvSaveMem, file->currentFilePointer, writeStartOffset); + + memcpy( (void *)writeStartOffset, lpBuffer, nNumberOfBytesToWrite ); + *lpNumberOfBytesWritten = nNumberOfBytesToWrite; + + if(file->data.length < 0) + file->data.length = 0; + + file->currentFilePointer += *lpNumberOfBytesWritten; + + //wprintf(L"Wrote %d bytes to %s, new file pointer is %I64d\n", *lpNumberOfBytesWritten, file->data.filename, file->currentFilePointer); + + file->updateLastModifiedTime(); + } + + ReleaseSaveAccess(); + + return 1; +} + +BOOL ConsoleSaveFileSplit::zeroFile(FileEntry *file, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten) +{ + assert( pvSaveMem != NULL ); + if( pvSaveMem == NULL ) + { + return 0; + } + + LockSaveAccess(); + + if( file->isRegionFile() ) + { + unsigned int sizeRequired = file->currentFilePointer + nNumberOfBytesToWrite; + RegionFileReference *fileRef = regionFiles[file->data.regionIndex]; + if( sizeRequired > file->getFileSize() ) + { + fileRef->data = (unsigned char *)realloc(fileRef->data, sizeRequired); + file->data.length = sizeRequired; + } + + memset( fileRef->data + file->currentFilePointer, 0, nNumberOfBytesToWrite ); + +// app.DebugPrintf(">>>>>>>>>>>>>> writing a region file's data 0x%.8x, 0x%x offset %d of %d bytes (writing %d bytes)\n",file->data.regionIndex,fileRef->data,file->currentFilePointer, file->getFileSize(), nNumberOfBytesToWrite); + + file->currentFilePointer += nNumberOfBytesToWrite; + file->updateLastModifiedTime(); + fileRef->dirty = true; + } + else + { + PrepareForWrite( file, nNumberOfBytesToWrite ); + + char *writeStartOffset = (char *)pvSaveMem + file->currentFilePointer; + //printf("Write: pvSaveMem = %0xd, currentFilePointer = %d, writeStartOffset = %0xd\n", pvSaveMem, file->currentFilePointer, writeStartOffset); + + memset( (void *)writeStartOffset, 0, nNumberOfBytesToWrite ); + *lpNumberOfBytesWritten = nNumberOfBytesToWrite; + + if(file->data.length < 0) + file->data.length = 0; + + file->currentFilePointer += *lpNumberOfBytesWritten; + + //wprintf(L"Wrote %d bytes to %s, new file pointer is %I64d\n", *lpNumberOfBytesWritten, file->data.filename, file->currentFilePointer); + + file->updateLastModifiedTime(); + } + + ReleaseSaveAccess(); + + return 1; +} + +BOOL ConsoleSaveFileSplit::readFile( FileEntry *file, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead) +{ + DWORD actualBytesToRead; + assert( pvSaveMem != NULL ); + if( pvSaveMem == NULL ) + { + return 0; + } + + LockSaveAccess(); + + if( file->isRegionFile() ) + { + actualBytesToRead = nNumberOfBytesToRead; + if( file->currentFilePointer + nNumberOfBytesToRead > file->data.length ) + { + actualBytesToRead = file->data.length - file->currentFilePointer; + } + RegionFileReference *fileRef = regionFiles[file->data.regionIndex]; + memcpy( lpBuffer, fileRef->data + file->currentFilePointer, actualBytesToRead ); + *lpNumberOfBytesRead = actualBytesToRead; + + file->currentFilePointer += actualBytesToRead; + } + else + { + char *readStartOffset = (char *)pvSaveMem + file->currentFilePointer; + //printf("Read: pvSaveMem = %0xd, currentFilePointer = %d, readStartOffset = %0xd\n", pvSaveMem, file->currentFilePointer, readStartOffset); + + assert( nNumberOfBytesToRead <= file->getFileSize() ); + + actualBytesToRead = nNumberOfBytesToRead; + if( file->currentFilePointer + nNumberOfBytesToRead > file->data.startOffset + file->data.length ) + { + actualBytesToRead = (file->data.startOffset + file->data.length) - file->currentFilePointer; + } + + memcpy( lpBuffer, readStartOffset, actualBytesToRead ); + *lpNumberOfBytesRead = actualBytesToRead; + + file->currentFilePointer += *lpNumberOfBytesRead; + + //wprintf(L"Read %d bytes from %s, new file pointer is %I64d\n", *lpNumberOfBytesRead, file->data.filename, file->currentFilePointer); + } + + + + ReleaseSaveAccess(); + + return 1; +} + +BOOL ConsoleSaveFileSplit::closeHandle( FileEntry *file ) +{ + LockSaveAccess(); + finalizeWrite(); + ReleaseSaveAccess(); + + return TRUE; +} + +// In this method, attempt to write any dirty region files, subject to maintaining a maximum write output rate. Writing is prioritised by time since the region was last written. +void ConsoleSaveFileSplit::tick() +{ + int64_t currentTime = System::currentTimeMillis(); + + // Don't do anything if the save system is up to something... + if( StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle ) + { + return; + } + + // ...or we shouldn't be saving... + if( StorageManager.GetSaveDisabled() ) + { + return; + } + + // ... or we haven't passed the required time since last assessing what to do + if( ( currentTime - m_lastTickTime ) < WRITE_TICK_RATE_MS ) + { + return; + } + + LockSaveAccess(); + + m_lastTickTime = currentTime; + + // Get total amount of data written over the time period we are interested in averaging over. Remove any older data. + unsigned int bytesWritten = 0; + for( AUTO_VAR(it, writeHistory.begin()); it != writeHistory.end(); ) + { + if( ( currentTime - it->writeTime ) > ( WRITE_BANDWIDTH_MEASUREMENT_PERIOD_SECONDS * 1000 ) ) + { + it = writeHistory.erase(it); + } + else + { + bytesWritten += it->writeSize; + it++; + } + } + + // Compile a vector of dirty regions. + vector dirtyRegions; + for( AUTO_VAR(it, regionFiles.begin()); it != regionFiles.end(); it++ ) + { + DirtyRegionFile dirtyRegion; + + if( it->second->dirty ) + { + dirtyRegion.fileRef = it->second->fileEntry->getRegionFileIndex(); + dirtyRegion.lastWritten = it->second->lastWritten; + dirtyRegions.push_back( dirtyRegion ); + } + } + + // Sort into ascending order, by lastWritten time. First elements will therefore be the ones least recently saved + std::sort( dirtyRegions.begin(), dirtyRegions.end() ); + + bool writeRequired = false; + unsigned int bytesInTimePeriod = bytesWritten; + unsigned int bytesAddedThisTick = 0; + for( int i = 0; i < dirtyRegions.size(); i++ ) + { + RegionFileReference *regionRef = regionFiles[dirtyRegions[i].fileRef]; + unsigned int compressedSize = regionRef->GetCompressedSize(); + bytesInTimePeriod += compressedSize; + bytesAddedThisTick += compressedSize; + + // Always consider at least one item for writing, even if it breaks the rule on the maximum number of bytes we would like to send per tick + if( ( i > 0 ) && ( bytesAddedThisTick > WRITE_MAX_WRITE_PER_TICK ) ) + { + break; + } + + // Could we add this without breaking our bytes per second cap? + if ( ( bytesInTimePeriod / WRITE_BANDWIDTH_MEASUREMENT_PERIOD_SECONDS ) > WRITE_BANDWIDTH_BYTESPERSECOND ) + { + break; + } + + // Can add for writing + WriteHistory writeEvent; + writeEvent.writeSize = compressedSize; + writeEvent.writeTime = System::currentTimeMillis(); + writeHistory.push_back(writeEvent); + + regionRef->Compress(); +// app.DebugPrintf("Tick: Writing region 0x%.8x, compressed as %d bytes\n",regionRef->fileEntry->getRegionFileIndex(), regionRef->dataCompressedSize); + StorageManager.UpdateSubfile(regionRef->index, regionRef->dataCompressed, regionRef->dataCompressedSize); + regionRef->dirty = false; + regionRef->lastWritten = System::currentTimeMillis(); + + writeRequired = true; + } +#ifndef _CONTENT_PACKAGE + { + unsigned int totalDirty = 0; + unsigned int totalDirtyBytes = 0; + __int64 oldestDirty = currentTime; + for( AUTO_VAR(it, regionFiles.begin()); it != regionFiles.end(); it++ ) + { + if( it->second->dirty ) + { + if( it->second->lastWritten < oldestDirty ) + { + oldestDirty = it->second->lastWritten; + } + totalDirty++; + totalDirtyBytes += it->second->fileEntry->getFileSize(); + } + } +#ifdef _DURANGO + PIXReportCounter(L"Dirty regions", (float)totalDirty); + PIXReportCounter(L"Dirty MB", (float)totalDirtyBytes / ( 1024 * 1024) ); + PIXReportCounter(L"Dirty oldest age", ((float) currentTime - oldestDirty ) ); + PIXReportCounter(L"Region writing bandwidth",((float)bytesInTimePeriod/ WRITE_BANDWIDTH_MEASUREMENT_PERIOD_SECONDS) / ( 1024 * 1024)); +#endif + } +#endif + + if( writeRequired ) + { + StorageManager.SaveSubfiles(SaveRegionFilesCallback, this); + } + + ReleaseSaveAccess(); +} + +void ConsoleSaveFileSplit::finalizeWrite() +{ + LockSaveAccess(); + header.WriteHeader( pvSaveMem ); + ReleaseSaveAccess(); +} + +void ConsoleSaveFileSplit::MoveDataBeyond(FileEntry *file, DWORD nNumberOfBytesToWrite) +{ + DWORD numberOfBytesRead = 0; + DWORD numberOfBytesWritten = 0; + + const DWORD bufferSize = 4096; + DWORD amountToRead = bufferSize; + //assert( nNumberOfBytesToWrite <= bufferSize ); + static byte buffer1[bufferSize]; + static byte buffer2[bufferSize]; + DWORD buffer1Size = 0; + DWORD buffer2Size = 0; + + // Only ReAlloc if we need to (we might already have enough) and align to 512 byte boundaries + DWORD currentHeapSize = pagesCommitted * CSF_PAGE_SIZE; + + DWORD desiredSize = header.GetFileSize() + nNumberOfBytesToWrite; + + if( desiredSize > currentHeapSize ) + { + unsigned int pagesRequired = ( desiredSize + (CSF_PAGE_SIZE - 1 ) ) / CSF_PAGE_SIZE; + void *pvRet = VirtualAlloc(pvHeap, pagesRequired * CSF_PAGE_SIZE, COMMIT_ALLOCATION, PAGE_READWRITE); + if( pvRet == NULL ) + { + // Out of physical memory + __debugbreak(); + } + pagesCommitted = pagesRequired; + } + + // This is the start of where we want the space to be, and the start of the data that we need to move + char *spaceStartOffset = (char *)pvSaveMem + file->data.startOffset + file->getFileSize(); + + // This is the end of where we want the space to be + char *spaceEndOffset = spaceStartOffset + nNumberOfBytesToWrite; + + // This is the current end of the data that we want to move + char *beginEndOfDataOffset = (char *)pvSaveMem + header.GetStartOfNextData(); + + // This is where the end of the data is going to be + char *finishEndOfDataOffset = beginEndOfDataOffset + nNumberOfBytesToWrite; + + // This is where we are going to read from (with the amount we want to read subtracted before we read) + char *readStartOffset = beginEndOfDataOffset; + + // This is where we can safely write to (with the amount we want write subtracted before we write) + char *writeStartOffset = finishEndOfDataOffset; + + //printf("\n******* MOVEDATABEYOND *******\n"); + //printf("Space start: %d, space end: %d\n", spaceStartOffset - (char *)pvSaveMem, spaceEndOffset - (char *)pvSaveMem); + //printf("Current end of data: %d, new end of data: %d\n", beginEndOfDataOffset - (char *)pvSaveMem, finishEndOfDataOffset - (char *)pvSaveMem); + + // Optimisation for things that are being moved in whole region file sector (4K chunks). We could generalise this a bit more but seems safest at the moment to identify this particular type + // of move and code explicitly for this situation + if( ( nNumberOfBytesToWrite & 4095 ) == 0 ) + { + if( nNumberOfBytesToWrite > 0 ) + { + // Get addresses for start & end of the region we are copying from as uintptr_t, for easier maths + uintptr_t uiFromStart = (uintptr_t)spaceStartOffset; + uintptr_t uiFromEnd = (uintptr_t)beginEndOfDataOffset; + + // Round both of these values to get 4096 byte chunks that we will need to at least partially move + uintptr_t uiFromStartChunk = uiFromStart & ~((uintptr_t)4095); + uintptr_t uiFromEndChunk = (uiFromEnd - 1 ) & ~((uintptr_t)4095); + + // Loop through all the affected source 4096 chunks, going backwards so we don't overwrite anything we'll need in the future + for( uintptr_t uiCurrentChunk = uiFromEndChunk; uiCurrentChunk >= uiFromStartChunk; uiCurrentChunk -= 4096 ) + { + // Establish chunk we'll need to copy + uintptr_t uiCopyStart = uiCurrentChunk; + uintptr_t uiCopyEnd = uiCurrentChunk + 4096; + // Clamp chunk to the bounds of the full region we are trying to copy + if( uiCopyStart < uiFromStart ) + { + // Needs to be clampged against the start of our region + uiCopyStart = uiFromStart; + } + if ( uiCopyEnd > uiFromEnd ) + { + // Needs to be clamped to the end of our region + uiCopyEnd = uiFromEnd; + } + XMemCpy( (void *)(uiCopyStart + nNumberOfBytesToWrite), ( void *)uiCopyStart, uiCopyEnd - uiCopyStart ); + } + } + } + else + { + while(true) + { + // Copy buffer 1 to buffer 2 + memcpy( buffer2, buffer1, buffer1Size); + buffer2Size = buffer1Size; + + // Fill buffer 1 from file + if( (readStartOffset - bufferSize) < spaceStartOffset ) + { + amountToRead = (DWORD)(readStartOffset - spaceStartOffset); + } + else + { + amountToRead = bufferSize; + } + + // Push the read point back by the amount of bytes that we are going to read + readStartOffset -= amountToRead; + + //printf("About to read %u from %d\n", amountToRead, readStartOffset - (char *)pvSaveMem ); + + memcpy( buffer1, readStartOffset, amountToRead ); + numberOfBytesRead = amountToRead; + + buffer1Size = amountToRead; + + // Move back the write pointer by the amount of bytes we are going to write + writeStartOffset -= buffer2Size; + + // Write buffer 2 to file + if( (writeStartOffset + buffer2Size) <= finishEndOfDataOffset) + { + //printf("About to write %u to %d\n", buffer2Size, writeStartOffset - (char *)pvSaveMem ); + memcpy( (void *)writeStartOffset, buffer2, buffer2Size ); + numberOfBytesWritten = buffer2Size; + } + else + { + assert((writeStartOffset + buffer2Size) <= finishEndOfDataOffset); + numberOfBytesWritten = 0; + } + + if( numberOfBytesRead == 0 ) + { + //printf("\n************** MOVE COMPLETED *************** \n\n"); + assert( writeStartOffset == spaceEndOffset ); + break; + } + } + } + + header.AdjustStartOffsets( file, nNumberOfBytesToWrite ); +} + +// Attempt to convert a filename into a numeric identifier, which we use for region files. File names supported are of the form: +// +// Filename Encoded as +// +// r.x.z.mcr 00 00 xx zz +// DIM-1r.x.z.mcr 00 01 xx zz +// DIM1/r.x.z.mcr 00 02 xx zz + +bool ConsoleSaveFileSplit::GetNumericIdentifierFromName(const wstring &fileName, unsigned int *idOut) +{ + // Determine whether it is one of our region file names if the file extension is ".mbr" + if( fileName.length() < 4 ) return false; + wstring extension = fileName.substr(fileName.length()-4,4); + if( extension != wstring(L".mcr") ) return false; + + unsigned int id = 0; + int x, z; + + const wchar_t *cstr = fileName.c_str(); + const wchar_t *body = cstr + 2; + + // If this filename starts with a "r" then assume it is of the format "r.x.z.mcr" - don't do anything as default value we've set are correct + if( cstr[0] != L'r' ) + { + // Must be prefixed by "DIM-1r." or "DIM1/r." + body = cstr + 7; + // Differentiate between these 2 options + if( cstr[3] == L'-' ) + { + // "DIM-1r." + id = 0x00010000; + } + else + { + // "DIM/1r." + id = 0x00020000; + } + } + // Get x/z coords + swscanf_s(body, L"%d.%d.mcr", &x, &z ); + + // Pack full id + id |= ( ( x << 8 ) & 0x0000ff00 ); + id |= ( z & 0x000000ff ); + + *idOut = id; + + return true; +} + +// Convert a numeric file identifier (for region files) back into a normal filename. See comment above. + +wstring ConsoleSaveFileSplit::GetNameFromNumericIdentifier(unsigned int idIn) +{ + wstring prefix; + + switch(idIn & 0x00ff0000 ) + { + case 0: + prefix = L""; + break; + case 1: + prefix = L"DIM-1"; + break; + case 2: + prefix = L"DIM1/"; + break; + } + signed char regionX = ( idIn >> 8 ) & 255; + signed char regionZ = idIn & 255; + wstring region = ( prefix + wstring(L"r.") + _toString(regionX) + L"." + _toString(regionZ) + L".mcr" ); + + return region; +} + +// Compress any dirty region files, and tell the storage manager about them so that it will process them when we ask it to save sub files +void ConsoleSaveFileSplit::processSubfilesForWrite() +{ +#if 0 + // 4J Stu - There are debug reasons where we want to force a save of all regions + StorageManager.ResetSubfiles(); + for(AUTO_VAR(it,regionFiles.begin()); it != regionFiles.end(); it++ ) + { + RegionFileReference* region = it->second; + int index = StorageManager.AddSubfile(region->fileEntry->data.regionIndex); + //if( region->dirty ) + { + region->Compress(); + StorageManager.UpdateSubfile(index, region->dataCompressed, region->dataCompressedSize); + region->dirty = false; + region->lastWritten = System::currentTimeMillis(); + } + } +#else + for(AUTO_VAR(it,regionFiles.begin()); it != regionFiles.end(); it++ ) + { + RegionFileReference* region = it->second; + if( region->dirty ) + { + region->Compress(); + StorageManager.UpdateSubfile(region->index, region->dataCompressed, region->dataCompressedSize); + region->dirty = false; + region->lastWritten = System::currentTimeMillis(); + } + } +#endif +} + +// Clean up any memory allocated for compressed data when we have finished writing +void ConsoleSaveFileSplit::processSubfilesAfterWrite() +{ + // This is called from the StorageManager.Tick() which should always be on the main thread + for(AUTO_VAR(it,regionFiles.begin()); it != regionFiles.end(); it++ ) + { + RegionFileReference* region = it->second; + region->ReleaseCompressed(); + } +} + +bool ConsoleSaveFileSplit::doesFileExist(ConsoleSavePath file) +{ + LockSaveAccess(); + bool exists = header.fileExists( file.getName() ); + ReleaseSaveAccess(); + + return exists; +} + +void ConsoleSaveFileSplit::Flush(bool autosave, bool updateThumbnail) +{ + LockSaveAccess(); + +#ifdef _XBOX_ONE + MinecraftServer *server = MinecraftServer::getInstance(); +#endif + + // The storage manage might potentially be busy doing a sub-file write initiated from the tick. Wait until this is totally processed. + while( StorageManager.GetSaveState() != C4JStorage::ESaveGame_Idle ) + { +#ifdef _XBOX_ONE + if (server && server->IsSuspending()) + { + // If the server is mid-suspend we need to tick the storage manager ourselves + StorageManager.Tick(); + } +#endif + + app.DebugPrintf("Flush wait\n"); + Sleep(10); + } + + finalizeWrite(); + + m_autosave = autosave; + if(!m_autosave) processSubfilesForWrite(); + + // Get the frequency of the timer + LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime; + float fElapsedTime = 0.0f; + QueryPerformanceFrequency( &qwTicksPerSec ); + float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart; + + unsigned int fileSize = header.GetFileSize(); + + // Assume that the compression will make it smaller so initially attempt to allocate the current file size + // We add 4 bytes to the start so that we can signal compressed data + // And another 4 bytes to store the decompressed data size + unsigned int compLength = fileSize+8; + + // 4J Stu - Added TU-1 interim + + // Attempt to allocate the required memory + // We do not own this, it belongs to the StorageManager + byte *compData = (byte *)StorageManager.AllocateSaveData( compLength ); + + // If we failed to allocate then compData will be NULL + // Pre-calculate the compressed data size so that we can attempt to allocate a smaller buffer + if(compData == NULL) + { + // Length should be 0 here so that the compression call knows that we want to know the length back + compLength = 0; + + // Pre-calculate the buffer size required for the compressed data + PIXBeginNamedEvent(0,"Pre-calc save compression"); + // Save the start time + QueryPerformanceCounter( &qwTime ); + Compression::getCompression()->Compress(NULL,&compLength,pvSaveMem,fileSize); + QueryPerformanceCounter( &qwNewTime ); + + qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart; + fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart)); + + app.DebugPrintf("Check buffer size: Elapsed time %f\n", fElapsedTime); + PIXEndNamedEvent(); + + // We add 4 bytes to the start so that we can signal compressed data + // And another 4 bytes to store the decompressed data size + compLength = compLength+8; + + // Attempt to allocate the required memory + compData = (byte *)StorageManager.AllocateSaveData( compLength ); + } + + if(compData != NULL) + { + // Re-compress all save data before we save it to disk + PIXBeginNamedEvent(0,"Actual save compression"); + // Save the start time + QueryPerformanceCounter( &qwTime ); + Compression::getCompression()->Compress(compData+8,&compLength,pvSaveMem,fileSize); + QueryPerformanceCounter( &qwNewTime ); + + qwDeltaTime.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart; + fElapsedTime = fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart)); + + app.DebugPrintf("Compress: Elapsed time %f\n", fElapsedTime); + PIXEndNamedEvent(); + + ZeroMemory(compData,8); + int saveVer = 0; + memcpy( compData, &saveVer, sizeof(int) ); + memcpy( compData+4, &fileSize, sizeof(int) ); + + app.DebugPrintf("Save data compressed from %d to %d\n", fileSize, compLength); + + if(updateThumbnail) + { + PBYTE pbThumbnailData=NULL; + DWORD dwThumbnailDataSize=0; + + PBYTE pbDataSaveImage=NULL; + DWORD dwDataSizeSaveImage=0; + +#if ( defined _XBOX || defined _DURANGO ) + app.GetSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize); +#elif ( defined __PS3__ || defined __ORBIS__ ) + app.GetSaveThumbnail(&pbThumbnailData,&dwThumbnailDataSize,&pbDataSaveImage,&dwDataSizeSaveImage); +#endif + + BYTE bTextMetadata[88]; + ZeroMemory(bTextMetadata,88); + + __int64 seed = 0; + bool hasSeed = false; + if(MinecraftServer::getInstance()!= NULL && MinecraftServer::getInstance()->levels[0]!=NULL) + { + seed = MinecraftServer::getInstance()->levels[0]->getLevelData()->getSeed(); + hasSeed = true; + } + + int iTextMetadataBytes = app.CreateImageTextData(bTextMetadata, seed, hasSeed, app.GetGameHostOption(eGameHostOption_All), Minecraft::GetInstance()->getCurrentTexturePackId()); + + // set the icon and save image + StorageManager.SetSaveImages(pbThumbnailData,dwThumbnailDataSize,pbDataSaveImage,dwDataSizeSaveImage,bTextMetadata,iTextMetadataBytes); + app.DebugPrintf("Save thumbnail size %d\n",dwThumbnailDataSize); + + } + + INT saveOrCheckpointId = 0; + bool validSave = StorageManager.GetSaveUniqueNumber(&saveOrCheckpointId); + TelemetryManager->RecordLevelSaveOrCheckpoint(ProfileManager.GetPrimaryPad(), saveOrCheckpointId, compLength+8); + + // save the data + StorageManager.SaveSaveData( &ConsoleSaveFileSplit::SaveSaveDataCallback, this ); +#ifndef _CONTENT_PACKAGE + if( app.DebugSettingsOn()) + { + if(app.GetWriteSavesToFolderEnabled() ) + { + DebugFlushToFile(compData, compLength+8); + } + } +#endif + ReleaseSaveAccess(); + } +} + +int ConsoleSaveFileSplit::SaveSaveDataCallback(LPVOID lpParam,bool bRes) +{ + ConsoleSaveFileSplit *pClass=(ConsoleSaveFileSplit *)lpParam; + + // Don't save sub files on autosave (their always being saved anyway) + if (!pClass->m_autosave) + { + // This is called from the StorageManager.Tick() which should always be on the main thread + StorageManager.SaveSubfiles(SaveRegionFilesCallback, pClass); + } + return 0; +} + +int ConsoleSaveFileSplit::SaveRegionFilesCallback(LPVOID lpParam,bool bRes) +{ + ConsoleSaveFileSplit *pClass=(ConsoleSaveFileSplit *)lpParam; + + // This is called from the StorageManager.Tick() which should always be on the main thread + pClass->processSubfilesAfterWrite(); + + return 0; +} + +#ifndef _CONTENT_PACKAGE +void ConsoleSaveFileSplit::DebugFlushToFile(void *compressedData /*= NULL*/, unsigned int compressedDataSize /*= 0*/) +{ + LockSaveAccess(); + + finalizeWrite(); + + unsigned int fileSize = header.GetFileSize(); + + DWORD numberOfBytesWritten = 0; + + File targetFileDir(L"Saves"); + + if(!targetFileDir.exists()) + targetFileDir.mkdir(); + + wchar_t *fileName = new wchar_t[XCONTENT_MAX_FILENAME_LENGTH+1]; + + SYSTEMTIME t; + GetSystemTime( &t ); + + //14 chars for the digits + //11 chars for the separators + suffix + //25 chars total + wstring cutFileName = m_fileName; + if(m_fileName.length() > XCONTENT_MAX_FILENAME_LENGTH - 25) + { + cutFileName = m_fileName.substr(0, XCONTENT_MAX_FILENAME_LENGTH - 25); + } + swprintf(fileName, XCONTENT_MAX_FILENAME_LENGTH+1, L"\\v%04d-%ls%02d.%02d.%02d.%02d.%02d.mcs",VER_PRODUCTBUILD,cutFileName.c_str(), t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); + +#ifdef _UNICODE + wstring wtemp = targetFileDir.getPath() + wstring(fileName); + LPCWSTR lpFileName = wtemp.c_str(); +#else + LPCSTR lpFileName = wstringtofilename( targetFileDir.getPath() + wstring(fileName) ); +#endif + + HANDLE hSaveFile = CreateFile( lpFileName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_FLAG_RANDOM_ACCESS, NULL); + + if(compressedData != NULL && compressedDataSize > 0) + { + WriteFile(hSaveFile,compressedData,compressedDataSize,&numberOfBytesWritten,NULL); + assert(numberOfBytesWritten == compressedDataSize); + } + else + { + WriteFile(hSaveFile,pvSaveMem,fileSize,&numberOfBytesWritten,NULL); + assert(numberOfBytesWritten == fileSize); + } + CloseHandle( hSaveFile ); + + delete[] fileName; + + ReleaseSaveAccess(); +} +#endif + +unsigned int ConsoleSaveFileSplit::getSizeOnDisk() +{ + return header.GetFileSize(); +} + +wstring ConsoleSaveFileSplit::getFilename() +{ + return m_fileName; +} + +vector *ConsoleSaveFileSplit::getFilesWithPrefix(const wstring &prefix) +{ + return header.getFilesWithPrefix( prefix ); +} + +vector *ConsoleSaveFileSplit::getRegionFilesByDimension(unsigned int dimensionIndex) +{ + vector *files = NULL; + + for( AUTO_VAR(it,regionFiles.begin()); it != regionFiles.end(); ++it ) + { + unsigned int entryDimension = ( (it->first) >> 16) & 0xFF; + + if(entryDimension == dimensionIndex) + { + if( files == NULL ) + { + files = new vector(); + } + + files->push_back(it->second->fileEntry); + } + } + + return files; +} + +#if defined(__PS3__) || defined(__ORBIS__) +wstring ConsoleSaveFileSplit::getPlayerDataFilenameForLoad(const PlayerUID& pUID) +{ + return header.getPlayerDataFilenameForLoad( pUID ); +} +wstring ConsoleSaveFileSplit::getPlayerDataFilenameForSave(const PlayerUID& pUID) +{ + return header.getPlayerDataFilenameForSave( pUID ); +} +vector *ConsoleSaveFileSplit::getValidPlayerDatFiles() +{ + return header.getValidPlayerDatFiles(); +} +#endif + +int ConsoleSaveFileSplit::getSaveVersion() +{ + return header.getSaveVersion(); +} + +int ConsoleSaveFileSplit::getOriginalSaveVersion() +{ + return header.getOriginalSaveVersion(); +} + +void ConsoleSaveFileSplit::LockSaveAccess() +{ + EnterCriticalSection(&m_lock); +} + +void ConsoleSaveFileSplit::ReleaseSaveAccess() +{ + LeaveCriticalSection(&m_lock); +} + +ESavePlatform ConsoleSaveFileSplit::getSavePlatform() +{ + return header.getSavePlatform(); +} + +bool ConsoleSaveFileSplit::isSaveEndianDifferent() +{ + return header.isSaveEndianDifferent(); +} + +void ConsoleSaveFileSplit::setLocalPlatform() +{ + header.setLocalPlatform(); +} + +void ConsoleSaveFileSplit::setPlatform(ESavePlatform plat) +{ + header.setPlatform(plat); +} + +ByteOrder ConsoleSaveFileSplit::getSaveEndian() +{ + return header.getSaveEndian(); +} + +ByteOrder ConsoleSaveFileSplit::getLocalEndian() +{ + return header.getLocalEndian(); +} + +void ConsoleSaveFileSplit::setEndian(ByteOrder endian) +{ + header.setEndian(endian); +} + +void ConsoleSaveFileSplit::ConvertRegionFile(File sourceFile) +{ + DWORD numberOfBytesWritten = 0; + DWORD numberOfBytesRead = 0; + + RegionFile sourceRegionFile(this, &sourceFile); + + for(unsigned int x = 0; x < 32; ++x) + { + for(unsigned int z = 0; z < 32; ++z) + { + DataInputStream *dis = sourceRegionFile.getChunkDataInputStream(x,z); + + if(dis) + { + byteArray inData(1024*1024); + int read = dis->read(inData); + dis->close(); + dis->deleteChildStream(); + delete dis; + + DataOutputStream *dos = sourceRegionFile.getChunkDataOutputStream(x,z); + dos->write(inData, 0, read); + + + dos->close(); + dos->deleteChildStream(); + delete dos; + delete inData.data; + + } + + } + } + sourceRegionFile.writeAllOffsets(); // saves all the endian swapped offsets back out to the file (not all of these are written in the above processing). + +} + +void ConsoleSaveFileSplit::ConvertToLocalPlatform() +{ + if(getSavePlatform() == SAVE_FILE_PLATFORM_LOCAL) + { + // already in the correct format + return; + } + // convert each of the region files to the local platform + vector *allFilesInSave = getFilesWithPrefix(wstring(L"")); + for(AUTO_VAR(it, allFilesInSave->begin()); it < allFilesInSave->end(); ++it) + { + FileEntry *fe = *it; + wstring fName( fe->data.filename ); + wstring suffix(L".mcr"); + if( fName.compare(fName.length() - suffix.length(), suffix.length(), suffix) == 0 ) + { + app.DebugPrintf("Processing a region file: %ls\n",fName.c_str()); + ConvertRegionFile(File(fe->data.filename) ); + } + else + { + app.DebugPrintf("%ls is not a region file, ignoring\n", fName.c_str()); + } + } + + setLocalPlatform(); // set the platform of this save to the local platform, now that it's been coverted +} diff --git a/Minecraft.World/ConsoleSaveFileSplit.h b/Minecraft.World/ConsoleSaveFileSplit.h new file mode 100644 index 00000000..0b2017a2 --- /dev/null +++ b/Minecraft.World/ConsoleSaveFileSplit.h @@ -0,0 +1,143 @@ +#pragma once + +#include "FileHeader.h" +#include "ConsoleSavePath.h" +#include "ConsoleSaveFile.h" + +class ProgressRenderer; + +class ConsoleSaveFileSplit : public ConsoleSaveFile +{ +private: + FileHeader header; + + static const int WRITE_BANDWIDTH_BYTESPERSECOND = 1048576; // Average bytes per second we will cap to when writing region files during the tick() method + static const int WRITE_BANDWIDTH_MEASUREMENT_PERIOD_SECONDS = 10; // Time period over which the bytes per second average is calculated + static const int WRITE_TICK_RATE_MS = 500; // Time between attempts to work out which regions we should write during the tick + static const int WRITE_MAX_WRITE_PER_TICK = WRITE_BANDWIDTH_BYTESPERSECOND; // Maximum number of bytes we can add in a single tick + + class WriteHistory + { + public: + int64_t writeTime; + unsigned int writeSize; + } ; + + class DirtyRegionFile + { + public: + int64_t lastWritten; + unsigned int fileRef; + bool operator<(const DirtyRegionFile& rhs) { return lastWritten < rhs.lastWritten; } + }; + + class RegionFileReference + { + public: + RegionFileReference(int index, unsigned int regionIndex, unsigned int length = 0, unsigned char *data = NULL); + ~RegionFileReference(); + void Compress(); // Compress from data to dataCompressed + void Decompress(); // Decompress from dataCompressed -> data + unsigned int GetCompressedSize(); // Gets byte size for what this region will compress to + void ReleaseCompressed(); // Release dataCompressed + FileEntry *fileEntry; + unsigned char *data; + unsigned char *dataCompressed; + unsigned int dataCompressedSize; + int index; + bool dirty; + int64_t lastWritten; + }; + unordered_map regionFiles; + vector writeHistory; + int64_t m_lastTickTime; + + FileEntry *GetRegionFileEntry(unsigned int regionIndex); + + wstring m_fileName; + bool m_autosave; + +// HANDLE hHeap; + static void *pvHeap; + static unsigned int pagesCommitted; +#ifdef _LARGE_WORLDS + static const unsigned int CSF_PAGE_SIZE = 64 * 1024; + static const unsigned int MAX_PAGE_COUNT = 32 * 1024; // 2GB virtual allocation +#else + static const unsigned int CSF_PAGE_SIZE = 64 * 1024; + static const unsigned int MAX_PAGE_COUNT = 1024; +#endif + LPVOID pvSaveMem; + + CRITICAL_SECTION m_lock; + + void PrepareForWrite( FileEntry *file, DWORD nNumberOfBytesToWrite ); + void MoveDataBeyond(FileEntry *file, DWORD nNumberOfBytesToWrite); + + bool GetNumericIdentifierFromName(const wstring &fileName, unsigned int *idOut); + wstring GetNameFromNumericIdentifier(unsigned int idIn); + void processSubfilesForWrite(); + void processSubfilesAfterWrite(); +public: + static int SaveSaveDataCallback(LPVOID lpParam,bool bRes); + static int SaveRegionFilesCallback(LPVOID lpParam,bool bRes); + +private: + void _init(const wstring &fileName, LPVOID pvSaveData, DWORD fileSize, ESavePlatform plat); + +public: + ConsoleSaveFileSplit(const wstring &fileName, LPVOID pvSaveData = NULL, DWORD fileSize = 0, bool forceCleanSave = false, ESavePlatform plat = SAVE_FILE_PLATFORM_LOCAL); + ConsoleSaveFileSplit(ConsoleSaveFile *sourceSave, bool alreadySmallRegions = true, ProgressListener *progress = NULL); + virtual ~ConsoleSaveFileSplit(); + + // 4J Stu - Initial implementation is intended to have a similar interface to the standard Xbox file access functions + + virtual FileEntry *createFile( const ConsoleSavePath &fileName ); + virtual void deleteFile( FileEntry *file ); + + virtual void setFilePointer(FileEntry *file,LONG lDistanceToMove,PLONG lpDistanceToMoveHigh,DWORD dwMoveMethod); + virtual BOOL writeFile( FileEntry *file, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten ); + virtual BOOL zeroFile(FileEntry *file, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten); + virtual BOOL readFile( FileEntry *file, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead ); + virtual BOOL closeHandle( FileEntry *file ); + + virtual void finalizeWrite(); + virtual void tick(); + + virtual bool doesFileExist(ConsoleSavePath file); + + virtual void Flush(bool autosave, bool updateThumbnail = true); + +#ifndef _CONTENT_PACKAGE + virtual void DebugFlushToFile(void *compressedData = NULL, unsigned int compressedDataSize = 0); +#endif + virtual unsigned int getSizeOnDisk(); + + virtual wstring getFilename(); + + virtual vector *getFilesWithPrefix(const wstring &prefix); + virtual vector *getRegionFilesByDimension(unsigned int dimensionIndex); + +#if defined(__PS3__) || defined(__ORBIS__) + virtual wstring getPlayerDataFilenameForLoad(const PlayerUID& pUID); + virtual wstring getPlayerDataFilenameForSave(const PlayerUID& pUID); + virtual vector *getValidPlayerDatFiles(); +#endif //__PS3__ + + virtual int getSaveVersion(); + virtual int getOriginalSaveVersion(); + + virtual void LockSaveAccess(); + virtual void ReleaseSaveAccess(); + + virtual ESavePlatform getSavePlatform(); + virtual bool isSaveEndianDifferent(); + virtual void setLocalPlatform(); + virtual void setPlatform(ESavePlatform plat); + virtual ByteOrder getSaveEndian(); + virtual ByteOrder getLocalEndian(); + virtual void setEndian(ByteOrder endian); + + virtual void ConvertRegionFile(File sourceFile); + virtual void ConvertToLocalPlatform(); +}; \ No newline at end of file diff --git a/Minecraft.World/ConsoleSavePath.h b/Minecraft.World/ConsoleSavePath.h new file mode 100644 index 00000000..321b8903 --- /dev/null +++ b/Minecraft.World/ConsoleSavePath.h @@ -0,0 +1,15 @@ +#pragma once +using namespace std; + +class ConsoleSavePath +{ +private: + wstring path; + +public: + ConsoleSavePath( const wstring &newPath ) { path = newPath; } + + wstring getName() const { return path; } + + wstring operator+( wstring &b ) { return path + b; } +}; \ No newline at end of file diff --git a/Minecraft.World/Container.cpp b/Minecraft.World/Container.cpp new file mode 100644 index 00000000..03dc3580 --- /dev/null +++ b/Minecraft.World/Container.cpp @@ -0,0 +1,2 @@ +#include "stdafx.h" +#include "Container.h" diff --git a/Minecraft.World/Container.h b/Minecraft.World/Container.h new file mode 100644 index 00000000..7aa63728 --- /dev/null +++ b/Minecraft.World/Container.h @@ -0,0 +1,23 @@ +#pragma once +using namespace std; + +class ItemInstance; +class Player; + +class Container +{ +public: + static const int LARGE_MAX_STACK_SIZE = 64; + + virtual unsigned int getContainerSize() = 0; + virtual shared_ptr getItem(unsigned int slot) = 0; + virtual shared_ptr removeItem(unsigned int slot, int count) = 0; + virtual shared_ptr removeItemNoUpdate(int slot) = 0; + virtual void setItem(unsigned int slot, shared_ptr item) = 0; + virtual int getName() = 0; + virtual int getMaxStackSize() = 0; + virtual void setChanged() = 0; + virtual bool stillValid(shared_ptr player) = 0; + virtual void startOpen() = 0; + virtual void stopOpen() = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/ContainerAckPacket.cpp b/Minecraft.World/ContainerAckPacket.cpp new file mode 100644 index 00000000..b416bfa6 --- /dev/null +++ b/Minecraft.World/ContainerAckPacket.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "ContainerAckPacket.h" + + + +ContainerAckPacket::ContainerAckPacket() +{ + containerId = 0; + uid = 0; + accepted = 0; +} + +ContainerAckPacket::ContainerAckPacket(int containerId, short uid, bool accepted) +{ + this->containerId = containerId; + this->uid = uid; + this->accepted = accepted; +} + +void ContainerAckPacket::handle(PacketListener *listener) +{ + listener->handleContainerAck(shared_from_this()); +} + +void ContainerAckPacket::read(DataInputStream *dis) //throws IOException +{ + containerId = dis->readByte(); + uid = dis->readShort(); + accepted = dis->readByte() != 0; +} + +void ContainerAckPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeByte(containerId); + dos->writeShort(uid); + dos->writeByte(accepted ? 1 : 0); +} + +int ContainerAckPacket::getEstimatedSize() +{ + return 4; +} diff --git a/Minecraft.World/ContainerAckPacket.h b/Minecraft.World/ContainerAckPacket.h new file mode 100644 index 00000000..00f3592e --- /dev/null +++ b/Minecraft.World/ContainerAckPacket.h @@ -0,0 +1,30 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +#include "stdafx.h" +#include +#include "PacketListener.h" + +class ContainerAckPacket : public Packet, public enable_shared_from_this +{ +public: + int containerId; + short uid; + bool accepted; + + ContainerAckPacket(); + ContainerAckPacket(int containerId, short uid, bool accepted); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ContainerAckPacket()); } + virtual int getId() { return 106; } +}; + + diff --git a/Minecraft.World/ContainerButtonClickPacket.cpp b/Minecraft.World/ContainerButtonClickPacket.cpp new file mode 100644 index 00000000..7aa1aec1 --- /dev/null +++ b/Minecraft.World/ContainerButtonClickPacket.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "PacketListener.h" +#include "ContainerButtonClickPacket.h" + + + +ContainerButtonClickPacket::ContainerButtonClickPacket() +{ +} + +ContainerButtonClickPacket::ContainerButtonClickPacket(int containerId, int buttonId) +{ + this->containerId = containerId; + this->buttonId = buttonId; +} + +void ContainerButtonClickPacket::handle(PacketListener *listener) +{ + listener->handleContainerButtonClick(shared_from_this()); +} + +void ContainerButtonClickPacket::read(DataInputStream *dis) +{ + containerId = dis->readByte(); + buttonId = dis->readByte(); +} + +void ContainerButtonClickPacket::write(DataOutputStream *dos) +{ + dos->writeByte(containerId); + dos->writeByte(buttonId); +} + +int ContainerButtonClickPacket::getEstimatedSize() +{ + return 2; +} \ No newline at end of file diff --git a/Minecraft.World/ContainerButtonClickPacket.h b/Minecraft.World/ContainerButtonClickPacket.h new file mode 100644 index 00000000..522c5f86 --- /dev/null +++ b/Minecraft.World/ContainerButtonClickPacket.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Packet.h" + +class ContainerButtonClickPacket : public Packet, public enable_shared_from_this +{ +public: + int containerId; + int buttonId; + + ContainerButtonClickPacket(); + ContainerButtonClickPacket(int containerId, int buttonId); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ContainerButtonClickPacket()); } + virtual int getId() { return 108; } +}; \ No newline at end of file diff --git a/Minecraft.World/ContainerClickPacket.cpp b/Minecraft.World/ContainerClickPacket.cpp new file mode 100644 index 00000000..5b3ca24f --- /dev/null +++ b/Minecraft.World/ContainerClickPacket.cpp @@ -0,0 +1,65 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.item.h" +#include "PacketListener.h" +#include "ContainerClickPacket.h" + + + +ContainerClickPacket::~ContainerClickPacket() +{ +} + +ContainerClickPacket::ContainerClickPacket() +{ + containerId = 0; + slotNum = 0; + buttonNum = 0; + uid = 0; + item = nullptr; + quickKey = false; +} + +ContainerClickPacket::ContainerClickPacket(int containerId, int slotNum, int buttonNum, bool quickKey, shared_ptr item, short uid) +{ + this->containerId = containerId; + this->slotNum = slotNum; + this->buttonNum = buttonNum; + this->uid = uid; + this->quickKey = quickKey; + // 4J - make a copy of the relevant bits of this item, as we want our packets to have full ownership of any data they reference + this->item = item ? item->copy() : nullptr; +} + +void ContainerClickPacket::handle(PacketListener *listener) +{ + listener->handleContainerClick(shared_from_this()); +} + +void ContainerClickPacket::read(DataInputStream *dis) //throws IOException +{ + containerId = dis->readByte(); + slotNum = dis->readShort(); + buttonNum = dis->readByte(); + uid = dis->readShort(); + quickKey = dis->readBoolean(); + + item = readItem(dis); +} + +void ContainerClickPacket::write(DataOutputStream *dos) // throws IOException +{ + dos->writeByte(containerId); + dos->writeShort(slotNum); + dos->writeByte(buttonNum); + dos->writeShort(uid); + dos->writeBoolean(quickKey); + + writeItem(item, dos); +} + +int ContainerClickPacket::getEstimatedSize() +{ + return 4 + 4 + 2 + 1; +} diff --git a/Minecraft.World/ContainerClickPacket.h b/Minecraft.World/ContainerClickPacket.h new file mode 100644 index 00000000..8bea7da5 --- /dev/null +++ b/Minecraft.World/ContainerClickPacket.h @@ -0,0 +1,30 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class ContainerClickPacket : public Packet, public enable_shared_from_this +{ +public: + int containerId; + int slotNum; + int buttonNum; + short uid; + shared_ptr item; + bool quickKey; + + ContainerClickPacket(); + ~ContainerClickPacket(); + ContainerClickPacket(int containerId, int slotNum, int buttonNum, bool quickKey, shared_ptr item, short uid); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ContainerClickPacket()); } + virtual int getId() { return 102; } +}; + + diff --git a/Minecraft.World/ContainerClosePacket.cpp b/Minecraft.World/ContainerClosePacket.cpp new file mode 100644 index 00000000..964359f9 --- /dev/null +++ b/Minecraft.World/ContainerClosePacket.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "ContainerClosePacket.h" + + + +ContainerClosePacket::ContainerClosePacket() +{ + containerId = 0; +} + +ContainerClosePacket::ContainerClosePacket(int containerId) +{ + this->containerId = containerId; +} + +void ContainerClosePacket::handle(PacketListener *listener) +{ + listener->handleContainerClose(shared_from_this()); +} + +void ContainerClosePacket::read(DataInputStream *dis) //throws IOException +{ + containerId = dis->readByte(); +} + +void ContainerClosePacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeByte(containerId); +} + +int ContainerClosePacket::getEstimatedSize() +{ + return 1; +} diff --git a/Minecraft.World/ContainerClosePacket.h b/Minecraft.World/ContainerClosePacket.h new file mode 100644 index 00000000..bf032a97 --- /dev/null +++ b/Minecraft.World/ContainerClosePacket.h @@ -0,0 +1,24 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class ContainerClosePacket : public Packet, public enable_shared_from_this +{ +public: + int containerId; + + ContainerClosePacket(); + ContainerClosePacket(int containerId); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ContainerClosePacket()); } + virtual int getId() { return 101; } +}; + + diff --git a/Minecraft.World/ContainerMenu.cpp b/Minecraft.World/ContainerMenu.cpp new file mode 100644 index 00000000..57190024 --- /dev/null +++ b/Minecraft.World/ContainerMenu.cpp @@ -0,0 +1,120 @@ +#include "stdafx.h" +#include "Container.h" +#include "net.minecraft.world.item.h" +#include "Slot.h" +#include "GenericStats.h" +#include "..\Minecraft.Client\StatsCounter.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\LocalPlayer.h" +#include "ContainerMenu.h" + +ContainerMenu::ContainerMenu(shared_ptr inventory, shared_ptr container) : AbstractContainerMenu() +{ + this->container = container; + this->containerRows = container->getContainerSize() / 9; + container->startOpen(); + + int yo = (containerRows - 4) * 18; + + for (int y = 0; y < containerRows; y++) + { + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(container, x + y * 9, 8 + x * 18, 18 + y * 18)); + } + } + + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x + y * 9 + 9, 8 + x * 18, 103 + y * 18 + yo)); + } + } + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x, 8 + x * 18, 161 + yo)); + } +} + +bool ContainerMenu::stillValid(shared_ptr player) +{ + return container->stillValid(player); +} + +shared_ptr ContainerMenu::quickMoveStack(shared_ptr player, int slotIndex) +{ + shared_ptr clicked = nullptr; + Slot *slot = slots->at(slotIndex); + if (slot != NULL && slot->hasItem()) + { + shared_ptr stack = slot->getItem(); + clicked = stack->copy(); + + if (slotIndex < containerRows * 9) + { + if(!moveItemStackTo(stack, containerRows * 9, (int)slots->size(), true)) + { + // 4J Stu - Brought forward from 1.2 + return nullptr; + } + } + else + { + if(!moveItemStackTo(stack, 0, containerRows * 9, false)) + { + // 4J Stu - Brought forward from 1.2 + return nullptr; + } + } + if (stack->count == 0) + { + slot->set(nullptr); + } + else + { + slot->setChanged(); + } + } + return clicked; +} + +void ContainerMenu::removed(shared_ptr player) +{ + AbstractContainerMenu::removed(player); + container->stopOpen(); +} + +shared_ptr ContainerMenu::clicked(int slotIndex, int buttonNum, int clickType, shared_ptr player) +{ + shared_ptr out = AbstractContainerMenu::clicked(slotIndex, buttonNum, clickType, player); + +#ifdef _EXTENDED_ACHIEVEMENTS + shared_ptr localPlayer = dynamic_pointer_cast(player); + + if (localPlayer != NULL) // 4J-JEV: For "Chestful o'Cobblestone" achievement. + { + int cobblecount = 0; + for (int i = 0; i < container->getContainerSize(); i++) + { + shared_ptr item = container->getItem(i); + if ( (item != nullptr) && (item->id == Tile::stoneBrick_Id) ) + { + cobblecount += item->GetCount(); + } + } + + // 4J-JEV: This check performed on XboxOne servers, for other platforms check here. +#ifndef _DURANGO + StatsCounter *sc = Minecraft::GetInstance()->stats[localPlayer->GetXboxPad()]; + int minedCount = sc->getTotalValue(GenericStats::blocksMined(Tile::rock_Id)) + sc->getTotalValue(GenericStats::blocksMined(Tile::stoneBrick_Id)); + if (cobblecount >= 1728 && minedCount >= 1728 ) +#endif + { + localPlayer->awardStat(GenericStats::chestfulOfCobblestone(),GenericStats::param_chestfulOfCobblestone(cobblecount)); + } + } +#endif + + return out; +} diff --git a/Minecraft.World/ContainerMenu.h b/Minecraft.World/ContainerMenu.h new file mode 100644 index 00000000..e6da0782 --- /dev/null +++ b/Minecraft.World/ContainerMenu.h @@ -0,0 +1,22 @@ +#pragma once + +#include "AbstractContainerMenu.h" + +class Container; + +class ContainerMenu : public AbstractContainerMenu +{ +private: + shared_ptr container; + int containerRows; + +public: + ContainerMenu(shared_ptr inventory, shared_ptr container); + + virtual bool stillValid(shared_ptr player); + virtual shared_ptr quickMoveStack(shared_ptr player, int slotIndex); + void removed(shared_ptr player); + + // 4J ADDED, + virtual shared_ptr clicked(int slotIndex, int buttonNum, int clickType, shared_ptr player); +}; diff --git a/Minecraft.World/ContainerOpenPacket.cpp b/Minecraft.World/ContainerOpenPacket.cpp new file mode 100644 index 00000000..7cb530cf --- /dev/null +++ b/Minecraft.World/ContainerOpenPacket.cpp @@ -0,0 +1,48 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "ContainerOpenPacket.h" + +ContainerOpenPacket::ContainerOpenPacket() +{ + containerId = 0; + type = 0; + title = 0; + size = 0; +} + +ContainerOpenPacket::ContainerOpenPacket(int containerId, int type, int title, int size) +{ + this->containerId = containerId; + this->type = type; + this->title = title; + this->size = size; +} + +void ContainerOpenPacket::handle(PacketListener *listener) +{ + listener->handleContainerOpen(shared_from_this()); +} + + +void ContainerOpenPacket::read(DataInputStream *dis) //throws IOException +{ + containerId = dis->readByte() & 0xff; + type = dis->readByte() & 0xff; + title = dis->readShort(); + size = dis->readByte() & 0xff; +} + +void ContainerOpenPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeByte(containerId & 0xff); + dos->writeByte(type & 0xff); + dos->writeShort(title & 0xffff); + dos->writeByte(size & 0xff); +} + +int ContainerOpenPacket::getEstimatedSize() +{ + return 5; +} diff --git a/Minecraft.World/ContainerOpenPacket.h b/Minecraft.World/ContainerOpenPacket.h new file mode 100644 index 00000000..dbc65b9a --- /dev/null +++ b/Minecraft.World/ContainerOpenPacket.h @@ -0,0 +1,37 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class ContainerOpenPacket : public Packet, public enable_shared_from_this +{ +public: + static const int CONTAINER = 0; + static const int WORKBENCH = 1; + static const int FURNACE = 2; + static const int TRAP = 3; + static const int ENCHANTMENT = 4; + static const int BREWING_STAND = 5; + static const int TRADER_NPC = 6; + static const int BEACON = 7; + static const int REPAIR_TABLE = 8; + + int containerId; + int type; + int title; // 4J Stu - Changed from string + int size; + + ContainerOpenPacket(); + ContainerOpenPacket(int containerId, int type, int title, int size); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ContainerOpenPacket()); } + virtual int getId() { return 100; } +}; + + diff --git a/Minecraft.World/ContainerSetContentPacket.cpp b/Minecraft.World/ContainerSetContentPacket.cpp new file mode 100644 index 00000000..13cfebd7 --- /dev/null +++ b/Minecraft.World/ContainerSetContentPacket.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.item.h" +#include "PacketListener.h" +#include "ContainerSetContentPacket.h" + + + +ContainerSetContentPacket::~ContainerSetContentPacket() +{ + delete[] items.data; +} + +ContainerSetContentPacket::ContainerSetContentPacket() +{ + containerId = 0; +} + +ContainerSetContentPacket::ContainerSetContentPacket(int containerId, vector > *newItems) +{ + this->containerId = containerId; + items = ItemInstanceArray((int)newItems->size()); + for (unsigned int i = 0; i < items.length; i++) + { + shared_ptr item = newItems->at(i); + items[i] = item == NULL ? nullptr : item->copy(); + } +} + +void ContainerSetContentPacket::read(DataInputStream *dis) //throws IOException +{ + containerId = dis->readByte(); + int count = dis->readShort(); + items = ItemInstanceArray(count); + for (int i = 0; i < count; i++) + { + items[i] = readItem(dis); + } +} + +void ContainerSetContentPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeByte(containerId); + dos->writeShort(items.length); + for (unsigned int i = 0; i < items.length; i++) + { + writeItem(items[i], dos); + } +} + +void ContainerSetContentPacket::handle(PacketListener *listener) +{ + listener->handleContainerContent(shared_from_this()); +} + +int ContainerSetContentPacket::getEstimatedSize() +{ + return 3 + items.length * 5; +} diff --git a/Minecraft.World/ContainerSetContentPacket.h b/Minecraft.World/ContainerSetContentPacket.h new file mode 100644 index 00000000..10315f58 --- /dev/null +++ b/Minecraft.World/ContainerSetContentPacket.h @@ -0,0 +1,26 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class ContainerSetContentPacket : public Packet, public enable_shared_from_this +{ +public: + int containerId; + ItemInstanceArray items; + + ContainerSetContentPacket(); + ~ContainerSetContentPacket(); + ContainerSetContentPacket(int containerId, vector > *newItems); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ContainerSetContentPacket()); } + virtual int getId() { return 104; } +}; + + diff --git a/Minecraft.World/ContainerSetDataPacket.cpp b/Minecraft.World/ContainerSetDataPacket.cpp new file mode 100644 index 00000000..756d8f58 --- /dev/null +++ b/Minecraft.World/ContainerSetDataPacket.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "ContainerSetDataPacket.h" + + + +ContainerSetDataPacket::ContainerSetDataPacket() +{ + containerId = 0; + id = -1; + value = 0; +} + +ContainerSetDataPacket::ContainerSetDataPacket(int containerId, int id, int value) +{ + this->containerId = containerId; + this->id = id; + this->value = value; +} + +void ContainerSetDataPacket::handle(PacketListener *listener) +{ + listener->handleContainerSetData(shared_from_this()); +} + +void ContainerSetDataPacket::read(DataInputStream *dis) //throws IOException +{ + containerId = dis->readByte(); + id = dis->readShort(); + value = dis->readShort(); +} + +void ContainerSetDataPacket::write(DataOutputStream *dos) // throws IOException +{ + dos->writeByte(containerId); + dos->writeShort(id); + dos->writeShort(value); +} + +int ContainerSetDataPacket::getEstimatedSize() +{ + return 1 + 4; +} diff --git a/Minecraft.World/ContainerSetDataPacket.h b/Minecraft.World/ContainerSetDataPacket.h new file mode 100644 index 00000000..ce075e00 --- /dev/null +++ b/Minecraft.World/ContainerSetDataPacket.h @@ -0,0 +1,24 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class ContainerSetDataPacket : public Packet, public enable_shared_from_this +{ +public: + int containerId; + int id; + int value; + + ContainerSetDataPacket(); + ContainerSetDataPacket(int containerId, int id, int value); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ContainerSetDataPacket()); } + virtual int getId() { return 105; } +}; \ No newline at end of file diff --git a/Minecraft.World/ContainerSetSlotPacket.cpp b/Minecraft.World/ContainerSetSlotPacket.cpp new file mode 100644 index 00000000..42892f3c --- /dev/null +++ b/Minecraft.World/ContainerSetSlotPacket.cpp @@ -0,0 +1,53 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.item.h" +#include "PacketListener.h" +#include "ContainerSetSlotPacket.h" + + + +const int ContainerSetSlotPacket::CONTAINER = 0; +const int ContainerSetSlotPacket::WORKBENCH = 1; +const int ContainerSetSlotPacket::FURNACE = 2; + +ContainerSetSlotPacket::ContainerSetSlotPacket() +{ + containerId = 0; + slot = 0; + item = nullptr; +} + +ContainerSetSlotPacket::ContainerSetSlotPacket(int containerId, int slot, shared_ptr item) +{ + this->containerId = containerId; + this->slot = slot; + this->item = item == NULL ? item : item->copy(); +} + +void ContainerSetSlotPacket::handle(PacketListener *listener) +{ + listener->handleContainerSetSlot(shared_from_this()); +} + +void ContainerSetSlotPacket::read(DataInputStream *dis) //throws IOException +{ + // 4J Stu - TU-1 hotfix + // Fix for #13142 - Holding down the A button on the furnace ingredient slot causes the UI to display incorrect item counts + BYTE byteId = dis->readByte(); + containerId = *(char *)&byteId; + slot = dis->readShort(); + item = readItem(dis); +} + +void ContainerSetSlotPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeByte(containerId); + dos->writeShort(slot); + writeItem(item, dos); +} + +int ContainerSetSlotPacket::getEstimatedSize() +{ + return 3 + 5; +} diff --git a/Minecraft.World/ContainerSetSlotPacket.h b/Minecraft.World/ContainerSetSlotPacket.h new file mode 100644 index 00000000..61269df0 --- /dev/null +++ b/Minecraft.World/ContainerSetSlotPacket.h @@ -0,0 +1,30 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class ContainerSetSlotPacket : public Packet, public enable_shared_from_this +{ +public: + static const int CONTAINER; + static const int WORKBENCH; + static const int FURNACE; + + int containerId; + int slot; + shared_ptr item; + + ContainerSetSlotPacket(); + ContainerSetSlotPacket(int containerId, int slot, shared_ptr item); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ContainerSetSlotPacket()); } + virtual int getId() { return 103; } +}; + + diff --git a/Minecraft.World/Control.h b/Minecraft.World/Control.h new file mode 100644 index 00000000..1c12a98f --- /dev/null +++ b/Minecraft.World/Control.h @@ -0,0 +1,9 @@ +#pragma once + +class Control +{ +public: + static const int MoveControlFlag = 1; + static const int LookControlFlag = 2; + static const int JumpControlFlag = 4; +}; \ No newline at end of file diff --git a/Minecraft.World/ControlledByPlayerGoal.cpp b/Minecraft.World/ControlledByPlayerGoal.cpp new file mode 100644 index 00000000..1e035494 --- /dev/null +++ b/Minecraft.World/ControlledByPlayerGoal.cpp @@ -0,0 +1,152 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.pathfinder.h" +#include "ControlledByPlayerGoal.h" + +ControlledByPlayerGoal::ControlledByPlayerGoal(Mob *mob, float maxSpeed, float walkSpeed) +{ + this->mob = mob; + this->maxSpeed = maxSpeed; + this->walkSpeed = walkSpeed; + speed = 0; + boosting = false; + boostTime = 0; + boostTimeTotal = 0; + setRequiredControlFlags(Control::MoveControlFlag | Control::JumpControlFlag | Control::LookControlFlag); +} + +void ControlledByPlayerGoal::start() +{ + speed = 0; + + // 4J Stu - Need to initialise this otherwise the pig will never move if you jump on before another goal has made it move and set the speed + if(mob->getSpeed() < walkSpeed) mob->setSpeed(walkSpeed); +} + +void ControlledByPlayerGoal::stop() +{ + boosting = false; + speed = 0; +} + +bool ControlledByPlayerGoal::canUse() +{ + shared_ptr player = dynamic_pointer_cast( mob->rider.lock() ); + return mob->isAlive() && player && (boosting || mob->canBeControlledByRider()); +} + +void ControlledByPlayerGoal::tick() +{ + shared_ptr player = dynamic_pointer_cast(mob->rider.lock()); + PathfinderMob *pig = (PathfinderMob *)mob; + + float yrd = Mth::wrapDegrees(player->yRot - mob->yRot) * 0.5f; + if (yrd > 5) yrd = 5; + if (yrd < -5) yrd = -5; + + mob->yRot = Mth::wrapDegrees(mob->yRot + yrd); + if (speed < maxSpeed) speed += (maxSpeed - speed) * 0.01f; + if (speed > maxSpeed) speed = maxSpeed; + + int x = Mth::floor(mob->x); + int y = Mth::floor(mob->y); + int z = Mth::floor(mob->z); + float moveSpeed = speed; + if (boosting) + { + if (boostTime++ > boostTimeTotal) + { + boosting = false; + } + moveSpeed += moveSpeed * 1.15f * Mth::sin((float) boostTime / boostTimeTotal * PI); + } + + float friction = 0.91f; + if (mob->onGround) + { + friction = 0.6f * 0.91f; + int t = mob->level->getTile(x,y,z); + if (t > 0) + { + friction = Tile::tiles[t]->friction * 0.91f; + } + } + float friction2 = (0.6f * 0.6f * 0.91f * 0.91f * 0.6f * 0.91f) / (friction * friction * friction); + float sin = Mth::sin(pig->yRot * PI / 180); + float cos = Mth::cos(pig->yRot * PI / 180); + float aproxSpeed = pig->getSpeed() * friction2; + float dist = max((int)moveSpeed, 1); + dist = aproxSpeed / dist; + float normMoveSpeed = moveSpeed * dist; + float xa = -(normMoveSpeed * sin); + float za = normMoveSpeed * cos; + + if (Mth::abs(xa) > Mth::abs(za)) + { + if (xa < 0) xa -= mob->bbWidth / 2.0f; + if (xa > 0) xa += mob->bbWidth / 2.0f; + za = 0; + } + else + { + xa = 0; + if (za < 0) za -= mob->bbWidth / 2.0f; + if (za > 0) za += mob->bbWidth / 2.0f; + } + + int xt = Mth::floor(mob->x + xa); + int zt = Mth::floor(mob->z + za); + + Node *size = new Node(Mth::floor(mob->bbWidth + 1), Mth::floor(mob->bbHeight + player->bbHeight + 1), Mth::floor(mob->bbWidth + 1)); + + if (x != xt || z != zt) + { + if (PathFinder::isFree(mob, xt, y, zt, size, false, false, true) == PathFinder::TYPE_BLOCKED + && PathFinder::isFree(mob, x, y + 1, z, size, false, false, true) == PathFinder::TYPE_OPEN + && PathFinder::isFree(mob, xt, y + 1, zt, size, false, false, true) == PathFinder::TYPE_OPEN) + { + pig->getJumpControl()->jump(); + } + } + + if (!player->abilities.instabuild && speed >= maxSpeed * 0.5f && mob->getRandom()->nextFloat() < 0.006f && !boosting) + { + shared_ptr carriedItem = player->getCarriedItem(); + + if (carriedItem != NULL && carriedItem->id == Item::carrotOnAStick_Id) + { + carriedItem->hurt(1, player); + + if (carriedItem->count == 0) + { + shared_ptr replacement = shared_ptr(new ItemInstance(Item::fishingRod)); + replacement->setTag(carriedItem->tag); + player->inventory->items[player->inventory->selected] = replacement; + } + } + } + + mob->travel(0, moveSpeed); +} + +bool ControlledByPlayerGoal::isBoosting() +{ + return boosting; +} + +void ControlledByPlayerGoal::boost() +{ + boosting = true; + boostTime = 0; + boostTimeTotal = mob->getRandom()->nextInt(MAX_BOOST_TIME + MIN_BOOST_TIME + 1) + MIN_BOOST_TIME; +} + +bool ControlledByPlayerGoal::canBoost() +{ + return !isBoosting() && speed > maxSpeed * 0.3f; +} \ No newline at end of file diff --git a/Minecraft.World/ControlledByPlayerGoal.h b/Minecraft.World/ControlledByPlayerGoal.h new file mode 100644 index 00000000..f49eaaf1 --- /dev/null +++ b/Minecraft.World/ControlledByPlayerGoal.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Goal.h" +#include "..\Minecraft.World\SharedConstants.h" + +class Mob; + +class ControlledByPlayerGoal : public Goal +{ +private: + static const int MIN_BOOST_TIME = SharedConstants::TICKS_PER_SECOND * 7; + static const int MAX_BOOST_TIME = SharedConstants::TICKS_PER_SECOND * 35; + + Mob *mob; // Owner of this goal + float maxSpeed; + float walkSpeed; + float speed; + bool boosting; + int boostTime; + int boostTimeTotal; + +public: + ControlledByPlayerGoal(Mob *mob, float maxSpeed, float walkSpeed); // 4J Added walkSpeed param + + void start(); + void stop(); + bool canUse(); + void tick(); + bool isBoosting(); + void boost(); + bool canBoost(); +}; \ No newline at end of file diff --git a/Minecraft.World/Coord.h b/Minecraft.World/Coord.h new file mode 100644 index 00000000..9fb8ad72 --- /dev/null +++ b/Minecraft.World/Coord.h @@ -0,0 +1,12 @@ +#pragma once + +class Coord +{ +public: + const int x, y, z; + +public: + Coord(int x, int y, int z) : x( x ), y( y ), z( z ) + { + }; +}; \ No newline at end of file diff --git a/Minecraft.World/CoralTile.cpp b/Minecraft.World/CoralTile.cpp new file mode 100644 index 00000000..a60fc3cd --- /dev/null +++ b/Minecraft.World/CoralTile.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" +#include "CoralTile.h" + +CoralTile::CoralTile(int id) : HalfTransparentTile(id, L"coral", Material::coral, true) +{ + float r = 1 / 16.0f; + setShape(0 - r, 0 - r, 0 - r, 1 + r, 1 + r, 1 + r); +} + +int CoralTile::getColor(LevelSource *level, int x, int y, int z) +{ + return (x * x * 3187961 + x * 987243 + y * y * 43297126 + y * 987121 + z * z * 927469861 + z * 1861) & 0xffffff; +} + +int CoralTile::getColor(LevelSource *level, int x, int y, int z, int data) +{ + return (x * x * 3187961 + x * 987243 + y * y * 43297126 + y * 987121 + z * z * 927469861 + z * 1861) & 0xffffff; +} diff --git a/Minecraft.World/CoralTile.h b/Minecraft.World/CoralTile.h new file mode 100644 index 00000000..efb5837b --- /dev/null +++ b/Minecraft.World/CoralTile.h @@ -0,0 +1,11 @@ +#pragma once +#include "HalfTransparentTile.h" +#include "Material.h" + +class CoralTile : public HalfTransparentTile +{ +public: + CoralTile(int id); + virtual int getColor(LevelSource *level, int x, int y, int z); + virtual int getColor(LevelSource *level, int x, int y, int z, int data); // 4J added +}; \ No newline at end of file diff --git a/Minecraft.World/Cow.cpp b/Minecraft.World/Cow.cpp new file mode 100644 index 00000000..c259183e --- /dev/null +++ b/Minecraft.World/Cow.cpp @@ -0,0 +1,130 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.stats.h" +#include "Cow.h" +#include "..\Minecraft.Client\Textures.h" +#include "MobCategory.h" + + + +Cow::Cow(Level *level) : Animal( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_COW; // 4J was L"/mob/cow.png"; + this->setSize(0.9f, 1.3f); + + getNavigation()->setAvoidWater(true); + goalSelector.addGoal(0, new FloatGoal(this)); + goalSelector.addGoal(1, new PanicGoal(this, 0.38f)); + goalSelector.addGoal(2, new BreedGoal(this, 0.2f)); + goalSelector.addGoal(3, new TemptGoal(this, 0.25f, Item::wheat_Id, false)); + goalSelector.addGoal(4, new FollowParentGoal(this, 0.25f)); + goalSelector.addGoal(5, new RandomStrollGoal(this, 0.2f)); + goalSelector.addGoal(6, new LookAtPlayerGoal(this, typeid(Player), 6)); + goalSelector.addGoal(7, new RandomLookAroundGoal(this)); +} + +bool Cow::useNewAi() +{ + return true; +} + +int Cow::getMaxHealth() +{ + return 10; +} + +int Cow::getAmbientSound() +{ + return eSoundType_MOB_COW_AMBIENT; +} + +int Cow::getHurtSound() +{ + return eSoundType_MOB_COW_HURT; +} + +int Cow::getDeathSound() +{ + return eSoundType_MOB_COW_HURT; +} + +float Cow::getSoundVolume() +{ + return 0.4f; +} + +int Cow::getDeathLoot() +{ + return Item::leather->id; +} + +void Cow::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + // drop some leather + int count = random->nextInt(3) + random->nextInt(1 + playerBonusLevel); + for (int i = 0; i < count; i++) + { + spawnAtLocation(Item::leather_Id, 1); + } + // and some meat + count = random->nextInt(3) + 1 + random->nextInt(1 + playerBonusLevel); + for (int i = 0; i < count; i++) + { + if (isOnFire()) + { + spawnAtLocation(Item::beef_cooked_Id, 1); + } + else + { + spawnAtLocation(Item::beef_raw_Id, 1); + } + } +} + +bool Cow::interact(shared_ptr player) +{ + shared_ptr item = player->inventory->getSelected(); + if (item != NULL && item->id == Item::bucket_empty->id) + { + player->awardStat(GenericStats::cowsMilked(),GenericStats::param_cowsMilked()); + + if (--item->count <= 0) + { + player->inventory->setItem(player->inventory->selected, shared_ptr( new ItemInstance(Item::milk) ) ); + } + else if (!player->inventory->add(shared_ptr( new ItemInstance(Item::milk) ))) + { + player->drop(shared_ptr( new ItemInstance(Item::milk) )); + } + + return true; + } + return Animal::interact(player); +} + +shared_ptr Cow::getBreedOffspring(shared_ptr target) +{ + // 4J - added limit to number of animals that can be bred + if( level->canCreateMore( GetType(), Level::eSpawnType_Breed) ) + { + return shared_ptr( new Cow(level) ); + } + else + { + return nullptr; + } +} diff --git a/Minecraft.World/Cow.h b/Minecraft.World/Cow.h new file mode 100644 index 00000000..a1a91aba --- /dev/null +++ b/Minecraft.World/Cow.h @@ -0,0 +1,32 @@ +#pragma once + +using namespace std; + +#include "Animal.h" + +class Player; +class Level; + +class Cow : public Animal +{ +public: + eINSTANCEOF GetType() { return eTYPE_COW; } + static Entity *create(Level *level) { return new Cow(level); } + +public: + Cow(Level *level); + virtual bool useNewAi(); + virtual int getMaxHealth(); + +protected: + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual float getSoundVolume(); + virtual int getDeathLoot(); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + +public: + virtual bool interact(shared_ptr player); + virtual shared_ptr getBreedOffspring(shared_ptr target); +}; diff --git a/Minecraft.World/CraftItemPacket.cpp b/Minecraft.World/CraftItemPacket.cpp new file mode 100644 index 00000000..7c6df6ad --- /dev/null +++ b/Minecraft.World/CraftItemPacket.cpp @@ -0,0 +1,46 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.item.h" +#include "PacketListener.h" +#include "CraftItemPacket.h" + + + +CraftItemPacket::~CraftItemPacket() +{ +} + +CraftItemPacket::CraftItemPacket() +{ + recipe = -1; + uid = 0; +} + +CraftItemPacket::CraftItemPacket(int recipe, short uid) +{ + this->recipe = recipe; + this->uid = uid; +} + +void CraftItemPacket::handle(PacketListener *listener) +{ + listener->handleCraftItem(shared_from_this()); +} + +void CraftItemPacket::read(DataInputStream *dis) //throws IOException +{ + uid = dis->readShort(); + recipe = dis->readInt(); +} + +void CraftItemPacket::write(DataOutputStream *dos) // throws IOException +{ + dos->writeShort(uid); + dos->writeInt(recipe); +} + +int CraftItemPacket::getEstimatedSize() +{ + return 2 + 4; +} diff --git a/Minecraft.World/CraftItemPacket.h b/Minecraft.World/CraftItemPacket.h new file mode 100644 index 00000000..9793d593 --- /dev/null +++ b/Minecraft.World/CraftItemPacket.h @@ -0,0 +1,27 @@ +#pragma once + +// 4J ADDED THIS PACKET + +using namespace std; + +#include "Packet.h" + +class CraftItemPacket : public Packet, public enable_shared_from_this +{ +public: + int recipe; + short uid; + + CraftItemPacket(); + ~CraftItemPacket(); + CraftItemPacket(int recipe, short uid); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new CraftItemPacket()); } + virtual int getId() { return 150; } +}; \ No newline at end of file diff --git a/Minecraft.World/CraftingContainer.cpp b/Minecraft.World/CraftingContainer.cpp new file mode 100644 index 00000000..fbcc5678 --- /dev/null +++ b/Minecraft.World/CraftingContainer.cpp @@ -0,0 +1,100 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "AbstractContainerMenu.h" +#include "CraftingContainer.h" + +CraftingContainer::~CraftingContainer() +{ + +} + +CraftingContainer::CraftingContainer(AbstractContainerMenu *menu, unsigned int w, unsigned int h) +{ + unsigned int size = w * h; + items = new ItemInstanceArray(size); + this->menu = menu; + this->width = w; +} + +unsigned int CraftingContainer::getContainerSize() +{ + return items->length; +} + +shared_ptr CraftingContainer::getItem(unsigned int slot) +{ + if (slot >= getContainerSize()) + { + return nullptr; + } + return (*items)[slot]; +} + +shared_ptr CraftingContainer::getItem(unsigned int x, unsigned int y) +{ + if (x < 0 || x >= width) + { + return nullptr; + } + unsigned int pos = x + y * width; + return getItem(pos); +} + +int CraftingContainer::getName() +{ + return 0; +} + +shared_ptr CraftingContainer::removeItemNoUpdate(int slot) +{ + if ((*items)[slot] != NULL) + { + shared_ptr item = (*items)[slot]; + (*items)[slot] = nullptr; + return item; + } + return nullptr; +} + +shared_ptr CraftingContainer::removeItem(unsigned int slot, int count) +{ + if ((*items)[slot] != NULL) + { + if ((*items)[slot]->count <= count) + { + shared_ptr item = (*items)[slot]; + (*items)[slot] = nullptr; + menu->slotsChanged(); // 4J - used to take pointer to this, but wasn't using it so removed + return item; + } + else + { + shared_ptr i = (*items)[slot]->remove(count); + if ((*items)[slot]->count == 0) (*items)[slot] = nullptr; + menu->slotsChanged(); // 4J - used to take pointer to this, but wasn't using it so removed + return i; + } + } + return nullptr; +} + +void CraftingContainer::setItem(unsigned int slot, shared_ptr item) +{ + (*items)[slot] = item; + if(menu) menu->slotsChanged(); +} + +int CraftingContainer::getMaxStackSize() +{ + return Container::LARGE_MAX_STACK_SIZE; +} + +void CraftingContainer::setChanged() +{ +} + +bool CraftingContainer::stillValid(shared_ptr player) +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/CraftingContainer.h b/Minecraft.World/CraftingContainer.h new file mode 100644 index 00000000..863249a2 --- /dev/null +++ b/Minecraft.World/CraftingContainer.h @@ -0,0 +1,32 @@ +#pragma once +using namespace std; +#include "Container.h" + +class AbstractContainerMenu; + +class CraftingContainer : public Container +{ +private: + ItemInstanceArray *items; + unsigned int width; + AbstractContainerMenu *menu; + +public: + CraftingContainer(AbstractContainerMenu *menu, unsigned int w, unsigned int h); + ~CraftingContainer(); + + virtual unsigned int getContainerSize(); + virtual shared_ptr getItem(unsigned int slot); + shared_ptr getItem(unsigned int x, unsigned int y); + virtual int getName(); + virtual shared_ptr removeItemNoUpdate(int slot); + virtual shared_ptr removeItem(unsigned int slot, int count); + virtual void setItem(unsigned int slot, shared_ptr item); + virtual int getMaxStackSize(); + virtual void setChanged(); + bool stillValid(shared_ptr player); + + void startOpen() { } // TODO Auto-generated method stub + void stopOpen() { } // TODO Auto-generated method stub + +}; \ No newline at end of file diff --git a/Minecraft.World/CraftingMenu.cpp b/Minecraft.World/CraftingMenu.cpp new file mode 100644 index 00000000..eeb2a6e3 --- /dev/null +++ b/Minecraft.World/CraftingMenu.cpp @@ -0,0 +1,138 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.crafting.h" +#include "CraftingContainer.h" +#include "ResultContainer.h" +#include "ResultSlot.h" +#include "CraftingMenu.h" + +const int CraftingMenu::RESULT_SLOT = 0; +const int CraftingMenu::CRAFT_SLOT_START = 1; +const int CraftingMenu::CRAFT_SLOT_END = CraftingMenu::CRAFT_SLOT_START + 9; +const int CraftingMenu::INV_SLOT_START = CraftingMenu::CRAFT_SLOT_END; +const int CraftingMenu::INV_SLOT_END = CraftingMenu::INV_SLOT_START + 9 * 3; +const int CraftingMenu::USE_ROW_SLOT_START = CraftingMenu::INV_SLOT_END; +const int CraftingMenu::USE_ROW_SLOT_END = CraftingMenu::USE_ROW_SLOT_START + 9; + +CraftingMenu::CraftingMenu(shared_ptr inventory, Level *level, int xt, int yt, int zt) : AbstractContainerMenu() +{ + craftSlots = shared_ptr( new CraftingContainer(this, 3, 3) ); + resultSlots = shared_ptr( new ResultContainer() ); + + this->level = level; + this->x = xt; + this->y = yt; + this->z = zt; + addSlot(new ResultSlot( inventory->player, craftSlots, resultSlots, 0, 120 + 4, 31 + 4)); + + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 3; x++) + { + addSlot(new Slot(craftSlots, x + y * 3, 30 + x * 18, 17 + y * 18)); + } + } + + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x + y * 9 + 9, 8 + x * 18, 84 + y * 18)); + } + } + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x, 8 + x * 18, 142)); + } + + slotsChanged(); // 4J - removed craftSlots parameter, see comment below +} + +void CraftingMenu::slotsChanged() // 4J used to take a shared_ptr but wasn't using it, so removed to simplify things +{ + resultSlots->setItem(0, Recipes::getInstance()->getItemFor(craftSlots, level)); +} + +void CraftingMenu::removed(shared_ptr player) +{ + AbstractContainerMenu::removed(player); + if (level->isClientSide) return; + + for (int i = 0; i < 9; i++) + { + shared_ptr item = craftSlots->removeItemNoUpdate(i); + if (item != NULL) + { + player->drop(item); + } + } +} + +bool CraftingMenu::stillValid(shared_ptr player) +{ + if (level->getTile(x, y, z) != Tile::workBench_Id) return false; + if (player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) > 8 * 8) return false; + return true; +} + +shared_ptr CraftingMenu::quickMoveStack(shared_ptr player, int slotIndex) +{ + shared_ptr clicked = nullptr; + Slot *slot = slots->at(slotIndex); + if (slot != NULL && slot->hasItem()) + { + shared_ptr stack = slot->getItem(); + clicked = stack->copy(); + + if (slotIndex == RESULT_SLOT) + { + if(!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, true)) + { + return nullptr; + } + slot->onQuickCraft(stack, clicked); + } + else if (slotIndex >= INV_SLOT_START && slotIndex < INV_SLOT_END) + { + if(!moveItemStackTo(stack, USE_ROW_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + } + else if (slotIndex >= USE_ROW_SLOT_START && slotIndex < USE_ROW_SLOT_END) + { + if(!moveItemStackTo(stack, INV_SLOT_START, INV_SLOT_END, false)) + { + return nullptr; + } + } + else + { + if(!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + } + if (stack->count == 0) + { + slot->set(nullptr); + } + else + { + slot->setChanged(); + } + if (stack->count == clicked->count) + { + // nothing moved + return nullptr; + } + else + { + slot->onTake(player, stack); + } + } + return clicked; +} \ No newline at end of file diff --git a/Minecraft.World/CraftingMenu.h b/Minecraft.World/CraftingMenu.h new file mode 100644 index 00000000..0452eccc --- /dev/null +++ b/Minecraft.World/CraftingMenu.h @@ -0,0 +1,35 @@ +#pragma once + +#include "AbstractContainerMenu.h" + +class CraftingContainer; +class Container; + +class CraftingMenu : public AbstractContainerMenu +{ + // 4J Stu Made these public for UI menus, perhaps should make friend class? +public: + static const int RESULT_SLOT; + static const int CRAFT_SLOT_START; + static const int CRAFT_SLOT_END; + static const int INV_SLOT_START; + static const int INV_SLOT_END; + static const int USE_ROW_SLOT_START; + static const int USE_ROW_SLOT_END; + +public: + shared_ptr craftSlots; + shared_ptr resultSlots; + +private: + Level *level; + int x, y, z; + +public: + CraftingMenu(shared_ptr inventory, Level *level, int xt, int yt, int zt); + + virtual void slotsChanged();// 4J used to take a shared_ptr but wasn't using it, so removed to simplify things + virtual void removed(shared_ptr player); + virtual bool stillValid(shared_ptr player); + virtual shared_ptr quickMoveStack(shared_ptr player, int slotIndex); +}; \ No newline at end of file diff --git a/Minecraft.World/Creature.cpp b/Minecraft.World/Creature.cpp new file mode 100644 index 00000000..b3403bc4 --- /dev/null +++ b/Minecraft.World/Creature.cpp @@ -0,0 +1,3 @@ +#include "stdafx.h" +#include "Creature.h" + diff --git a/Minecraft.World/Creature.h b/Minecraft.World/Creature.h new file mode 100644 index 00000000..98f47af6 --- /dev/null +++ b/Minecraft.World/Creature.h @@ -0,0 +1,10 @@ +#pragma once +#include "Entity.h" + +class Level; + +class Creature +{ +public: + Creature() {} +}; diff --git a/Minecraft.World/Creeper.cpp b/Minecraft.World/Creeper.cpp new file mode 100644 index 00000000..3f6c66a7 --- /dev/null +++ b/Minecraft.World/Creeper.cpp @@ -0,0 +1,169 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.ai.goal.target.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.damagesource.h" +#include "GeneralStat.h" +#include "Skeleton.h" +#include "Creeper.h" +#include "Arrow.h" +#include "..\Minecraft.Client\Textures.h" +#include "SoundTypes.h" + + + +void Creeper::_init() +{ + swell = 0; + oldSwell = 0; +} + +Creeper::Creeper(Level *level) : Monster( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + _init(); + + this->textureIdx = TN_MOB_CREEPER; // 4J was L"/mob/creeper.png"; + + goalSelector.addGoal(1, new FloatGoal(this)); + goalSelector.addGoal(2, new SwellGoal(this)); + goalSelector.addGoal(3, new AvoidPlayerGoal(this, typeid(Ozelot), 6, 0.25f, 0.30f)); + goalSelector.addGoal(4, new MeleeAttackGoal(this, 0.25f, false)); + goalSelector.addGoal(5, new RandomStrollGoal(this, 0.20f)); + goalSelector.addGoal(6, new LookAtPlayerGoal(this, typeid(Player), 8)); + goalSelector.addGoal(6, new RandomLookAroundGoal(this)); + + targetSelector.addGoal(1, new NearestAttackableTargetGoal(this, typeid(Player), 16, 0, true)); + targetSelector.addGoal(2, new HurtByTargetGoal(this, false)); +} + +bool Creeper::useNewAi() +{ + return true; +} + +int Creeper::getMaxHealth() +{ + return 20; +} + +void Creeper::defineSynchedData() +{ + Monster::defineSynchedData(); + + entityData->define(DATA_SWELL_DIR, (byte) -1); + entityData->define(DATA_IS_POWERED, (byte) 0); +} + +void Creeper::addAdditonalSaveData(CompoundTag *entityTag) +{ + Monster::addAdditonalSaveData(entityTag); + if (entityData->getByte(DATA_IS_POWERED) == 1) entityTag->putBoolean(L"powered", true); +} + +void Creeper::readAdditionalSaveData(CompoundTag *tag) +{ + Monster::readAdditionalSaveData(tag); + entityData->set(DATA_IS_POWERED, (byte) (tag->getBoolean(L"powered") ? 1 : 0)); +} + +void Creeper::tick() +{ + oldSwell = swell; + if (isAlive()) + { + int swellDir = getSwellDir(); + if (swellDir > 0 && swell == 0) + { + level->playSound(shared_from_this(), eSoundType_RANDOM_FUSE, 1, 0.5f); + } + swell += swellDir; + if (swell < 0) swell = 0; + if (swell >= MAX_SWELL) + { + swell = MAX_SWELL; + if (!level->isClientSide) + { + bool destroyBlocks = true; //level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); + if (isPowered()) level->explode(shared_from_this(), x, y, z, 6, destroyBlocks); + else level->explode(shared_from_this(), x, y, z, 3,destroyBlocks); + remove(); + } + } + } + Monster::tick(); +} + +int Creeper::getHurtSound() +{ + return eSoundType_MOB_CREEPER_HURT; +} + +int Creeper::getDeathSound() +{ + return eSoundType_MOB_CREEPER_DEATH; +} + +void Creeper::die(DamageSource *source) +{ + Monster::die(source); + + if ( dynamic_pointer_cast(source->getEntity()) != NULL ) + { + spawnAtLocation(Item::record_01_Id + random->nextInt(12), 1); + } + + shared_ptr player = dynamic_pointer_cast(source->getEntity()); + if ( (dynamic_pointer_cast(source->getDirectEntity()) != NULL) && (player != NULL) ) + { + player->awardStat(GenericStats::archer(), GenericStats::param_archer()); + } +} + +bool Creeper::doHurtTarget(shared_ptr target) +{ + return true; +} + +bool Creeper::isPowered() +{ + return entityData->getByte(DATA_IS_POWERED) == 1; +} + +float Creeper::getSwelling(float a) +{ + return (oldSwell + (swell - oldSwell) * a) / (MAX_SWELL - 2); +} + +int Creeper::getDeathLoot() +{ + return Item::sulphur->id; +} + +int Creeper::getSwellDir() +{ + return (int) (char) entityData->getByte(DATA_SWELL_DIR); +} + +void Creeper::setSwellDir(int dir) +{ + entityData->set(DATA_SWELL_DIR, (byte) dir); +} + +void Creeper::thunderHit(const LightningBolt *lightningBolt) +{ + Monster::thunderHit(lightningBolt); + entityData->set(DATA_IS_POWERED, (byte) 1); +} diff --git a/Minecraft.World/Creeper.h b/Minecraft.World/Creeper.h new file mode 100644 index 00000000..a5da0e10 --- /dev/null +++ b/Minecraft.World/Creeper.h @@ -0,0 +1,59 @@ +#pragma once +using namespace std; + +#include "Monster.h" + +class Level; +class DamageSource; + +class Creeper : public Monster +{ +public: + eINSTANCEOF GetType() { return eTYPE_CREEPER; } + static Entity *create(Level *level) { return new Creeper(level); } + +private: + static const int DATA_SWELL_DIR = 16; + static const int DATA_IS_POWERED = 17; + + int swell; + int oldSwell; + + static const int MAX_SWELL = 30; + + void _init(); + +public: + Creeper(Level *level); + + virtual bool useNewAi(); + virtual int getMaxHealth(); + +protected: + virtual void defineSynchedData(); + +public: + virtual void addAdditonalSaveData(CompoundTag *entityTag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +protected: + virtual void tick(); + +protected: + virtual int getHurtSound(); + virtual int getDeathSound(); + +public: + virtual void die(DamageSource *source); + virtual bool doHurtTarget(shared_ptr target); + virtual bool isPowered(); + float getSwelling(float a); + +protected: + int getDeathLoot(); + +public: + int getSwellDir(); + void setSwellDir(int dir); + void thunderHit(const LightningBolt *lightningBolt) ; +}; diff --git a/Minecraft.World/CropTile.cpp b/Minecraft.World/CropTile.cpp new file mode 100644 index 00000000..0c19d889 --- /dev/null +++ b/Minecraft.World/CropTile.cpp @@ -0,0 +1,169 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.h" +#include "CropTile.h" + +CropTile::CropTile(int id) : Bush(id) +{ + setTicking(true); + updateDefaultShape(); + icons = NULL; + + setDestroyTime(0.0f); + setSoundType(SOUND_GRASS); + setNotCollectStatistics(); + sendTileData(); +} + +// 4J Added override +void CropTile::updateDefaultShape() +{ + float ss = 0.5f; + this->setShape(0.5f - ss, 0, 0.5f - ss, 0.5f + ss, 0.25f, 0.5f + ss); +} + +bool CropTile::mayPlaceOn(int tile) +{ + return tile == Tile::farmland_Id; +} + +void CropTile::tick(Level *level, int x, int y, int z, Random *random) +{ + Bush::tick(level, x, y, z, random); + if (level->getRawBrightness(x, y + 1, z) >= Level::MAX_BRIGHTNESS - 6) + { + + int age = level->getData(x, y, z); + if (age < 7) + { + float growthSpeed = getGrowthSpeed(level, x, y, z); + + if (random->nextInt((int) (25 / growthSpeed) + 1) == 0) + { + age++; + level->setData(x, y, z, age); + } + } + } +} + +void CropTile::growCropsToMax(Level *level, int x, int y, int z) +{ + level->setData(x, y, z, 7); +} + +float CropTile::getGrowthSpeed(Level *level, int x, int y, int z) +{ + float speed = 1; + + int n = level->getTile(x, y, z - 1); + int s = level->getTile(x, y, z + 1); + int w = level->getTile(x - 1, y, z); + int e = level->getTile(x + 1, y, z); + + int d0 = level->getTile(x - 1, y, z - 1); + int d1 = level->getTile(x + 1, y, z - 1); + int d2 = level->getTile(x + 1, y, z + 1); + int d3 = level->getTile(x - 1, y, z + 1); + + bool horizontal = w == this->id || e == this->id; + bool vertical = n == this->id || s == this->id; + bool diagonal = d0 == this->id || d1 == this->id || d2 == this->id || d3 == this->id; + + for (int xx = x - 1; xx <= x + 1; xx++) + for (int zz = z - 1; zz <= z + 1; zz++) + { + int t = level->getTile(xx, y - 1, zz); + + float tileSpeed = 0; + if (t == Tile::farmland_Id) + { + tileSpeed = 1; + if (level->getData(xx, y - 1, zz) > 0) tileSpeed = 3; + } + + if (xx != x || zz != z) tileSpeed /= 4; + + speed += tileSpeed; + } + + if (diagonal || (horizontal && vertical)) speed /= 2; + + return speed; + +} + +Icon *CropTile::getTexture(int face, int data) +{ + if (data < 0 || data > 7) data = 7; + return icons[data]; +} + +int CropTile::getRenderShape() +{ + return Tile::SHAPE_ROWS; +} + +int CropTile::getBaseSeedId() +{ + return Item::seeds_wheat_Id; +} + +int CropTile::getBasePlantId() +{ + return Item::wheat_Id; +} + +/** + * Using this method instead of destroy() to determine if seeds should be + * dropped + */ +void CropTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus) +{ + Bush::spawnResources(level, x, y, z, data, odds, 0); + + if (level->isClientSide) + { + return; + } + if (data >= 7) { + int count = 3 + playerBonus; + for (int i = 0; i < count; i++) + { + if (level->random->nextInt(5 * 3) > data) continue; + popResource(level, x, y, z, shared_ptr(new ItemInstance(getBaseSeedId(), 1, 0))); + } + } +} + +int CropTile::getResource(int data, Random *random, int playerBonusLevel) +{ + if (data == 7) + { + return getBasePlantId(); + } + + return getBaseSeedId(); +} + +int CropTile::getResourceCount(Random *random) +{ + return 1; +} + +int CropTile::cloneTileId(Level *level, int x, int y, int z) +{ + return getBaseSeedId(); +} + +void CropTile::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[8]; + + for (int i = 0; i < 8; i++) + { + icons[i] = iconRegister->registerIcon(L"crops_" + _toString(i)); + } +} \ No newline at end of file diff --git a/Minecraft.World/CropTile.h b/Minecraft.World/CropTile.h new file mode 100644 index 00000000..998c5367 --- /dev/null +++ b/Minecraft.World/CropTile.h @@ -0,0 +1,44 @@ +#pragma once +#include "Bush.h" +#include "Material.h" + +class Random; +class Level; +class ChunkRebuildData; + +class CropTile : public Bush +{ + friend class Tile; + friend class ChunkRebuildData; +private: + Icon **icons; + +protected: + CropTile(int id); + virtual bool mayPlaceOn(int tile); +public: + // 4J Added override + virtual void updateDefaultShape(); + virtual void tick(Level *level, int x, int y, int z, Random *random); + void growCropsToMax(Level *level, int x, int y, int z); +private: + float getGrowthSpeed(Level *level, int x, int y, int z); +public: + virtual Icon *getTexture(int face, int data); + virtual int getRenderShape(); +protected: + virtual int getBaseSeedId(); + virtual int getBasePlantId(); + +public: + /** + * Using this method instead of destroy() to determine if seeds should be + * dropped + */ + virtual void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int getResourceCount(Random *random); + virtual int cloneTileId(Level *level, int x, int y, int z); + //@Override + virtual void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/CustomLevelSource.cpp b/Minecraft.World/CustomLevelSource.cpp new file mode 100644 index 00000000..216e63b9 --- /dev/null +++ b/Minecraft.World/CustomLevelSource.cpp @@ -0,0 +1,640 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.levelgen.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.level.levelgen.structure.h" +#include "net.minecraft.world.level.levelgen.synth.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.storage.h" +#include "CustomLevelSource.h" + +const double CustomLevelSource::SNOW_SCALE = 0.3; +const double CustomLevelSource::SNOW_CUTOFF = 0.5; + +CustomLevelSource::CustomLevelSource(Level *level, __int64 seed, bool generateStructures) : generateStructures( generateStructures ) +{ +#ifdef _OVERRIDE_HEIGHTMAP + m_XZSize = level->getLevelData()->getXZSize(); + + m_heightmapOverride = byteArray( (m_XZSize*16) * (m_XZSize*16) ); + +#ifdef _UNICODE + wstring path = L"GAME:\\GameRules\\heightmap.bin"; + +#else +#ifdef _WINDOWS64 + string path = "GameRules\\heightmap.bin"; +#else + string path = "GAME:\\GameRules\\heightmap.bin"; +#endif +#endif + HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if( file == INVALID_HANDLE_VALUE ) + { + app.FatalLoadError(); + DWORD error = GetLastError(); + assert(false); + } + else + { + +#ifdef _DURANGO + __debugbreak(); // TODO + DWORD bytesRead,dwFileSize = 0; +#else + DWORD bytesRead,dwFileSize = GetFileSize(file,NULL); +#endif + if(dwFileSize > m_heightmapOverride.length) + { + app.DebugPrintf("Heightmap binary is too large!!\n"); + __debugbreak(); + } + BOOL bSuccess = ReadFile(file,m_heightmapOverride.data,dwFileSize,&bytesRead,NULL); + + if(bSuccess==FALSE) + { + app.FatalLoadError(); + } + CloseHandle(file); + } + + m_waterheightOverride = byteArray( (m_XZSize*16) * (m_XZSize*16) ); + +#ifdef _UNICODE + wstring waterHeightPath = L"GAME:\\GameRules\\waterheight.bin"; + +#else +#ifdef _WINDOWS64 + string waterHeightPath = "GameRules\\waterheight.bin"; +#else + string waterHeightPath = "GAME:\\GameRules\\waterheight.bin"; +#endif +#endif + file = CreateFile(waterHeightPath.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if( file == INVALID_HANDLE_VALUE ) + { + DWORD error = GetLastError(); + //assert(false); + memset(m_waterheightOverride.data, level->seaLevel, m_waterheightOverride.length); + } + else + { + +#ifdef _DURANGO + __debugbreak(); // TODO + DWORD bytesRead,dwFileSize = 0; +#else + DWORD bytesRead,dwFileSize = GetFileSize(file,NULL); +#endif + if(dwFileSize > m_waterheightOverride.length) + { + app.DebugPrintf("waterheight binary is too large!!\n"); + __debugbreak(); + } + BOOL bSuccess = ReadFile(file,m_waterheightOverride.data,dwFileSize,&bytesRead,NULL); + + if(bSuccess==FALSE) + { + app.FatalLoadError(); + } + CloseHandle(file); + } + + caveFeature = new LargeCaveFeature(); + strongholdFeature = new StrongholdFeature(); + villageFeature = new VillageFeature(0,m_XZSize); + mineShaftFeature = new MineShaftFeature(); + canyonFeature = new CanyonFeature(); + + this->level = level; + + random = new Random(seed); + pprandom = new Random(seed); // 4J - added, so that we can have a separate random for doing post-processing in parallel with creation + perlinNoise3 = new PerlinNoise(random, 4); +#endif +} + +CustomLevelSource::~CustomLevelSource() +{ +#ifdef _OVERRIDE_HEIGHTMAP + delete caveFeature; + delete strongholdFeature; + delete villageFeature; + delete mineShaftFeature; + delete canyonFeature; + + this->level = level; + + delete random; + delete perlinNoise3; +#endif +} + +void CustomLevelSource::prepareHeights(int xOffs, int zOffs, byteArray blocks) +{ +#ifdef _OVERRIDE_HEIGHTMAP + int xChunks = 16 / CHUNK_WIDTH; + int yChunks = Level::maxBuildHeight / CHUNK_HEIGHT; + int waterHeight = level->seaLevel; + + int xSize = xChunks + 1; + int ySize = Level::maxBuildHeight / CHUNK_HEIGHT + 1; + int zSize = xChunks + 1; + + int xMapStart = xOffs + m_XZSize/2; + int zMapStart = zOffs + m_XZSize/2; + for (int xc = 0; xc < xChunks; xc++) + { + for (int zc = 0; zc < xChunks; zc++) + { + for (int yc = 0; yc < yChunks; yc++) + { + for (int y = 0; y < CHUNK_HEIGHT; y++) + { + for (int x = 0; x < CHUNK_WIDTH; x++) + { + for (int z = 0; z < CHUNK_WIDTH; z++) + { + int mapIndex = (zMapStart * 16 + z + ( zc * CHUNK_WIDTH )) * (m_XZSize * 16) + (xMapStart * 16 + x + ( xc * CHUNK_WIDTH )); + int mapHeight = m_heightmapOverride[mapIndex]; + waterHeight = m_waterheightOverride[mapIndex]; + //app.DebugPrintf("MapHeight = %d, y = %d\n", mapHeight, yc * CHUNK_HEIGHT + y); + /////////////////////////////////////////////////////////////////// + // 4J - add this chunk of code to make land "fall-off" at the edges of + // a finite world - size of that world is currently hard-coded in here + const int worldSize = m_XZSize * 16; + const int falloffStart = 32; // chunks away from edge were we start doing fall-off + const float falloffMax = 128.0f; // max value we need to get to falloff by the edge of the map + + int xxx = ( ( xOffs * 16 ) + x + ( xc * CHUNK_WIDTH ) ); + int zzz = ( ( zOffs * 16 ) + z + ( zc * CHUNK_WIDTH ) ); + + // Get distance to edges of world in x + int xxx0 = xxx + ( worldSize / 2 ); + if( xxx0 < 0 ) xxx0 = 0; + int xxx1 = ( ( worldSize / 2 ) - 1 ) - xxx; + if( xxx1 < 0 ) xxx1 = 0; + + // Get distance to edges of world in z + int zzz0 = zzz + ( worldSize / 2 ); + if( zzz0 < 0 ) zzz0 = 0; + int zzz1 = ( ( worldSize / 2 ) - 1 ) - zzz; + if( zzz1 < 0 ) zzz1 = 0; + + // Get min distance to any edge + int emin = xxx0; + if (xxx1 < emin ) emin = xxx1; + if (zzz0 < emin ) emin = zzz0; + if (zzz1 < emin ) emin = zzz1; + + float comp = 0.0f; + + // Calculate how much we want the world to fall away, if we're in the defined region to do so + if( emin < falloffStart ) + { + int falloff = falloffStart - emin; + comp = ((float)falloff / (float)falloffStart ) * falloffMax; + } + // 4J - end of extra code + /////////////////////////////////////////////////////////////////// + int tileId = 0; + // 4J - this comparison used to just be with 0.0f but is now varied by block above + if (yc * CHUNK_HEIGHT + y < mapHeight) + { + tileId = (byte) Tile::rock_Id; + } + else if (yc * CHUNK_HEIGHT + y < waterHeight) + { + tileId = (byte) Tile::calmWater_Id; + } + + // 4J - more extra code to make sure that the column at the edge of the world is just water & rock, to match the infinite sea that + // continues on after the edge of the world. + + if( emin == 0 ) + { + // This matches code in MultiPlayerChunkCache that makes the geometry which continues at the edge of the world + if( yc * CHUNK_HEIGHT + y <= ( level->getSeaLevel() - 10 ) ) tileId = Tile::rock_Id; + else if( yc * CHUNK_HEIGHT + y < level->getSeaLevel() ) tileId = Tile::calmWater_Id; + } + + int indexY = (yc * CHUNK_HEIGHT + y); + int offsAdjustment = 0; + if(indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + offsAdjustment = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + int offs = ( (x + xc * CHUNK_WIDTH) << Level::genDepthBitsPlusFour | (z + zc * CHUNK_WIDTH) << Level::genDepthBits | indexY) + offsAdjustment; + blocks[offs] = tileId; + } + } + } + } + } + } +#endif +} + + +void CustomLevelSource::buildSurfaces(int xOffs, int zOffs, byteArray blocks, BiomeArray biomes) +{ +#ifdef _OVERRIDE_HEIGHTMAP + int waterHeight = level->seaLevel; + int xMapStart = xOffs + m_XZSize/2; + int zMapStart = zOffs + m_XZSize/2; + + double s = 1 / 32.0; + + doubleArray depthBuffer(16*16); // 4J - used to be declared with class level scope but moved here for thread safety + + depthBuffer = perlinNoise3->getRegion(depthBuffer, xOffs * 16, zOffs * 16, 0, 16, 16, 1, s * 2, s * 2, s * 2); + + for (int x = 0; x < 16; x++) + { + for (int z = 0; z < 16; z++) + { + int mapIndex = (zMapStart * 16 + z) * (m_XZSize * 16) + (xMapStart * 16 + x); + waterHeight = m_waterheightOverride[mapIndex]; + + Biome *b = biomes[z + x * 16]; + float temp = b->getTemperature(); + int runDepth = (int) (depthBuffer[x + z * 16] / 3 + 3 + random->nextDouble() * 0.25); + + int run = -1; + + byte top = b->topMaterial; + byte material = b->material; + + LevelGenerationOptions *lgo = app.getLevelGenerationOptions(); + if(lgo != NULL) + { + lgo->getBiomeOverride(b->id,material,top); + } + + for (int y = Level::maxBuildHeight - 1; y >= 0; y--) + { + + int indexY = y; + int offsAdjustment = 0; + if(indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + offsAdjustment = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + int offs = (x << Level::genDepthBitsPlusFour | z << Level::genDepthBits | indexY) + offsAdjustment; + + if (y <= 1 + random->nextInt(2)) // 4J - changed to make the bedrock not have bits you can get stuck in + // if (y <= 0 + random->nextInt(5)) + { + blocks[offs] = (byte) Tile::unbreakable_Id; + } + else + { + int old = blocks[offs]; + + if (old == 0) + { + run = -1; + } + else if (old == Tile::rock_Id) + { + if (run == -1) + { + if (runDepth <= 0) + { + top = 0; + material = (byte) Tile::rock_Id; + } + else if (y >= waterHeight - 4 && y <= waterHeight + 1) + { + top = b->topMaterial; + material = b->material; + if(lgo != NULL) + { + lgo->getBiomeOverride(b->id,material,top); + } + } + + if (y < waterHeight && top == 0) + { + if (temp < 0.15f) top = (byte) Tile::ice_Id; + else top = (byte) Tile::calmWater_Id; + } + + run = runDepth; + if (y >= waterHeight - 1) blocks[offs] = top; + else blocks[offs] = material; + } + else if (run > 0) + { + run--; + blocks[offs] = material; + + // place a few sandstone blocks beneath sand + // runs + if (run == 0 && material == Tile::sand_Id) + { + run = random->nextInt(4); + material = (byte) Tile::sandStone_Id; + } + } + } + } + } + } + } + + delete [] depthBuffer.data; +#endif +} + +LevelChunk *CustomLevelSource::create(int x, int z) +{ +#ifdef _OVERRIDE_HEIGHTMAP + return getChunk(x,z); +#else + return NULL; +#endif +} + +LevelChunk *CustomLevelSource::getChunk(int xOffs, int zOffs) +{ +#ifdef _OVERRIDE_HEIGHTMAP + random->setSeed(xOffs * 341873128712l + zOffs * 132897987541l); + + // 4J - now allocating this with a physical alloc & bypassing general memory management so that it will get cleanly freed + int blocksSize = Level::maxBuildHeight * 16 * 16; + byte *tileData = (byte *)XPhysicalAlloc(blocksSize, MAXULONG_PTR, 4096, PAGE_READWRITE); + XMemSet128(tileData,0,blocksSize); + byteArray blocks = byteArray(tileData,blocksSize); + // byteArray blocks = byteArray(16 * level->depth * 16); + + // LevelChunk *levelChunk = new LevelChunk(level, blocks, xOffs, zOffs); // 4J - moved to below + + prepareHeights(xOffs, zOffs, blocks); + + // 4J - Some changes made here to how biomes, temperatures and downfalls are passed around for thread safety + BiomeArray biomes; + level->getBiomeSource()->getBiomeBlock(biomes, xOffs * 16, zOffs * 16, 16, 16, true); + + buildSurfaces(xOffs, zOffs, blocks, biomes); + + delete [] biomes.data; + + caveFeature->apply(this, level, xOffs, zOffs, blocks); + // 4J Stu Design Change - 1.8 gen goes stronghold, mineshaft, village, canyon + // this changed in 1.2 to canyon, mineshaft, village, stronghold + // This change makes sense as it stops canyons running through other structures + canyonFeature->apply(this, level, xOffs, zOffs, blocks); + if (generateStructures) + { + mineShaftFeature->apply(this, level, xOffs, zOffs, blocks); + villageFeature->apply(this, level, xOffs, zOffs, blocks); + strongholdFeature->apply(this, level, xOffs, zOffs, blocks); + } + // canyonFeature.apply(this, level, xOffs, zOffs, blocks); + // townFeature.apply(this, level, xOffs, zOffs, blocks); + // addCaves(xOffs, zOffs, blocks); + // addTowns(xOffs, zOffs, blocks); + + // levelChunk->recalcHeightmap(); // 4J - removed & moved into its own method + + // 4J - this now creates compressed block data from the blocks array passed in, so moved it until after the blocks are actually finalised. We also + // now need to free the passed in blocks as the LevelChunk doesn't use the passed in allocation anymore. + LevelChunk *levelChunk = new LevelChunk(level, blocks, xOffs, zOffs); + XPhysicalFree(tileData); + + return levelChunk; +#else + return NULL; +#endif +} + +// 4J - removed & moved into its own method from getChunk, so we can call recalcHeightmap after the chunk is added into the cache. Without +// doing this, then loads of the lightgaps() calls will fail to add any lights, because adding a light checks if the cache has this chunk in. +// lightgaps also does light 1 block into the neighbouring chunks, and maybe that is somehow enough to get lighting to propagate round the world, +// but this just doesn't seem right - this isn't a new fault in the 360 version, have checked that java does the same. +void CustomLevelSource::lightChunk(LevelChunk *lc) +{ +#ifdef _OVERRIDE_HEIGHTMAP + lc->recalcHeightmap(); +#endif +} + +bool CustomLevelSource::hasChunk(int x, int y) +{ + return true; +} + +void CustomLevelSource::calcWaterDepths(ChunkSource *parent, int xt, int zt) +{ +#ifdef _OVERRIDE_HEIGHTMAP + int xo = xt * 16; + int zo = zt * 16; + for (int x = 0; x < 16; x++) + { + int y = level->getSeaLevel(); + for (int z = 0; z < 16; z++) + { + int xp = xo + x + 7; + int zp = zo + z + 7; + int h = level->getHeightmap(xp, zp); + if (h <= 0) + { + if (level->getHeightmap(xp - 1, zp) > 0 || level->getHeightmap(xp + 1, zp) > 0 || level->getHeightmap(xp, zp - 1) > 0 || level->getHeightmap(xp, zp + 1) > 0) + { + bool hadWater = false; + if (hadWater || (level->getTile(xp - 1, y, zp) == Tile::calmWater_Id && level->getData(xp - 1, y, zp) < 7)) hadWater = true; + if (hadWater || (level->getTile(xp + 1, y, zp) == Tile::calmWater_Id && level->getData(xp + 1, y, zp) < 7)) hadWater = true; + if (hadWater || (level->getTile(xp, y, zp - 1) == Tile::calmWater_Id && level->getData(xp, y, zp - 1) < 7)) hadWater = true; + if (hadWater || (level->getTile(xp, y, zp + 1) == Tile::calmWater_Id && level->getData(xp, y, zp + 1) < 7)) hadWater = true; + if (hadWater) + { + for (int x2 = -5; x2 <= 5; x2++) + { + for (int z2 = -5; z2 <= 5; z2++) + { + int d = (x2 > 0 ? x2 : -x2) + (z2 > 0 ? z2 : -z2); + + if (d <= 5) + { + d = 6 - d; + if (level->getTile(xp + x2, y, zp + z2) == Tile::calmWater_Id) + { + int od = level->getData(xp + x2, y, zp + z2); + if (od < 7 && od < d) + { + level->setData(xp + x2, y, zp + z2, d); + } + } + } + } + } + if (hadWater) + { + level->setTileAndDataNoUpdate(xp, y, zp, Tile::calmWater_Id, 7); + for (int y2 = 0; y2 < y; y2++) + { + level->setTileAndDataNoUpdate(xp, y2, zp, Tile::calmWater_Id, 8); + } + } + } + } + } + } + } +#endif +} + +// 4J - changed this to used pprandom rather than random, so that we can run it concurrently with getChunk +void CustomLevelSource::postProcess(ChunkSource *parent, int xt, int zt) +{ +#ifdef _OVERRIDE_HEIGHTMAP + HeavyTile::instaFall = true; + int xo = xt * 16; + int zo = zt * 16; + + Biome *biome = level->getBiome(xo + 16, zo + 16); + + if (CustomLevelSource::FLOATING_ISLANDS) + { + calcWaterDepths(parent, xt, zt); + } + + pprandom->setSeed(level->getSeed()); + __int64 xScale = pprandom->nextLong() / 2 * 2 + 1; + __int64 zScale = pprandom->nextLong() / 2 * 2 + 1; + pprandom->setSeed(((xt * xScale) + (zt * zScale)) ^ level->getSeed()); + + bool hasVillage = false; + + PIXBeginNamedEvent(0,"Structure postprocessing"); + if (generateStructures) + { + mineShaftFeature->postProcess(level, pprandom, xt, zt); + hasVillage = villageFeature->postProcess(level, pprandom, xt, zt); + strongholdFeature->postProcess(level, pprandom, xt, zt); + } + PIXEndNamedEvent(); + +#if 0 + PIXBeginNamedEvent(0,"Lakes"); + if (!hasVillage && pprandom->nextInt(4) == 0) + { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(Level::maxBuildHeight); + int z = zo + pprandom->nextInt(16) + 8; + + LakeFeature *calmWater = new LakeFeature(Tile::calmWater_Id); + calmWater->place(level, pprandom, x, y, z); + delete calmWater; + } + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Lava"); + if (!hasVillage && pprandom->nextInt(8) == 0) + { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(pprandom->nextInt(Level::maxBuildHeight - 8) + 8); + int z = zo + pprandom->nextInt(16) + 8; + if (y < level->seaLevel || pprandom->nextInt(10) == 0) + { + LakeFeature *calmLava = new LakeFeature(Tile::calmLava_Id); + calmLava->place(level, pprandom, x, y, z); + delete calmLava; + } + } + PIXEndNamedEvent(); +#endif + + PIXBeginNamedEvent(0,"Monster rooms"); + for (int i = 0; i < 8; i++) { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(Level::maxBuildHeight); + int z = zo + pprandom->nextInt(16) + 8; + MonsterRoomFeature *mrf = new MonsterRoomFeature(); + if (mrf->place(level, pprandom, x, y, z)) + { + } + delete mrf; + } + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Biome decorate"); + biome->decorate(level, pprandom, xo, zo); + PIXEndNamedEvent(); + + app.processSchematics(parent->getChunk(xt,zt)); + + MobSpawner::postProcessSpawnMobs(level, biome, xo + 8, zo + 8, 16, 16, pprandom); + + // 4J - brought forward from 1.2.3 to get snow back in taiga biomes + xo += 8; + zo += 8; + for (int x = 0; x < 16; x++) + { + for (int z = 0; z < 16; z++) + { + int y = level->getTopRainBlock(xo + x, zo + z); + + if (level->shouldFreezeIgnoreNeighbors(x + xo, y - 1, z + zo)) + { + level->setTileNoUpdate(x + xo, y - 1, z + zo, Tile::ice_Id); // 4J - changed from setTile, otherwise we end up creating a *lot* of dynamic water tiles as these ice tiles are set + } + if (level->shouldSnow(x + xo, y, z + zo)) + { + level->setTile(x + xo, y, z + zo, Tile::topSnow_Id); + } + } + } + + HeavyTile::instaFall = false; +#endif +} + +bool CustomLevelSource::save(bool force, ProgressListener *progressListener) +{ + return true; +} + +bool CustomLevelSource::tick() +{ + return false; +} + +bool CustomLevelSource::shouldSave() +{ + return true; +} + +wstring CustomLevelSource::gatherStats() +{ + return L"CustomLevelSource"; +} + +vector *CustomLevelSource::getMobsAt(MobCategory *mobCategory, int x, int y, int z) +{ +#ifdef _OVERRIDE_HEIGHTMAP + Biome *biome = level->getBiome(x, z); + if (biome == NULL) + { + return NULL; + } + return biome->getMobs(mobCategory); +#else + return NULL; +#endif +} + +TilePos *CustomLevelSource::findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z) +{ +#ifdef _OVERRIDE_HEIGHTMAP + if (LargeFeature::STRONGHOLD == featureName && strongholdFeature != NULL) + { + return strongholdFeature->getNearestGeneratedFeature(level, x, y, z); + } +#endif + return NULL; +} diff --git a/Minecraft.World/CustomLevelSource.h b/Minecraft.World/CustomLevelSource.h new file mode 100644 index 00000000..af01a478 --- /dev/null +++ b/Minecraft.World/CustomLevelSource.h @@ -0,0 +1,79 @@ +#pragma once +using namespace std; + +#include "ChunkSource.h" + +#ifndef _CONTENT_PACKAGE +#define _OVERRIDE_HEIGHTMAP +#endif + +class ProgressListener; +class LargeFeature; +class StrongholdFeature; +class VillageFeature; +class MineShaftFeature; + +class CustomLevelSource : public ChunkSource +{ +public: + static const double SNOW_CUTOFF; + static const double SNOW_SCALE; + static const bool FLOATING_ISLANDS = false; + static const int CHUNK_HEIGHT = 8; + static const int CHUNK_WIDTH = 4; + +private: + +#ifdef _OVERRIDE_HEIGHTMAP + Random *random; + Random *pprandom; // 4J - added + PerlinNoise *perlinNoise3; + LargeFeature *caveFeature; + StrongholdFeature *strongholdFeature; + VillageFeature *villageFeature; + MineShaftFeature *mineShaftFeature; + LargeFeature *canyonFeature; + Level *level; +#endif + + byteArray m_heightmapOverride; + byteArray m_waterheightOverride; + +private: + + const bool generateStructures; + +public: + CustomLevelSource(Level *level, __int64 seed, bool generateStructures); + ~CustomLevelSource(); + +public: + void prepareHeights(int xOffs, int zOffs, byteArray blocks); + +public: + void buildSurfaces(int xOffs, int zOffs, byteArray blocks, BiomeArray biomes); + +private: + virtual LevelChunk *create(int x, int z); + +public: + virtual LevelChunk *getChunk(int xOffs, int zOffs); + virtual void lightChunk(LevelChunk *lc); // 4J added + +public: + virtual bool hasChunk(int x, int y); + +private: + void calcWaterDepths(ChunkSource *parent, int xt, int zt); + +public: + virtual void postProcess(ChunkSource *parent, int xt, int zt); + virtual bool save(bool force, ProgressListener *progressListener); + virtual bool tick(); + virtual bool shouldSave(); + virtual wstring gatherStats(); + +public: + virtual vector *getMobsAt(MobCategory *mobCategory, int x, int y, int z); + virtual TilePos *findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z); +}; diff --git a/Minecraft.World/CustomPayloadPacket.cpp b/Minecraft.World/CustomPayloadPacket.cpp new file mode 100644 index 00000000..29e2828e --- /dev/null +++ b/Minecraft.World/CustomPayloadPacket.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "BasicTypeContainers.h" +#include "CustomPayloadPacket.h" + +// Mojang-defined custom packets +const wstring CustomPayloadPacket::CUSTOM_BOOK_PACKET = L"MC|BEdit"; +const wstring CustomPayloadPacket::CUSTOM_BOOK_SIGN_PACKET = L"MC|BSign"; +const wstring CustomPayloadPacket::TEXTURE_PACK_PACKET = L"MC|TPack"; +const wstring CustomPayloadPacket::TRADER_LIST_PACKET = L"MC|TrList"; +const wstring CustomPayloadPacket::TRADER_SELECTION_PACKET = L"MC|TrSel"; +const wstring CustomPayloadPacket::SET_ADVENTURE_COMMAND_PACKET = L"MC|AdvCdm"; +const wstring CustomPayloadPacket::SET_BEACON_PACKET = L"MC|Beacon"; +const wstring CustomPayloadPacket::SET_ITEM_NAME_PACKET = L"MC|ItemName"; + +CustomPayloadPacket::CustomPayloadPacket() +{ +} + +CustomPayloadPacket::CustomPayloadPacket(const wstring &identifier, byteArray data) +{ + this->identifier = identifier; + this->data = data; + + if (data.data != NULL) + { + length = data.length; + + if (length > Short::MAX_VALUE) + { + app.DebugPrintf("Payload may not be larger than 32K\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + //throw new IllegalArgumentException("Payload may not be larger than 32k"); + } + } +} + +void CustomPayloadPacket::read(DataInputStream *dis) +{ + identifier = readUtf(dis, 20); + length = dis->readShort(); + + if (length > 0 && length < Short::MAX_VALUE) + { + if(data.data != NULL) + { + delete [] data.data; + } + data = byteArray(length); + dis->readFully(data); + } +} + +void CustomPayloadPacket::write(DataOutputStream *dos) +{ + writeUtf(identifier, dos); + dos->writeShort((short) length); + if (data.data != NULL) + { + dos->write(data); + } +} + +void CustomPayloadPacket::handle(PacketListener *listener) +{ + listener->handleCustomPayload( shared_from_this() ); +} + +int CustomPayloadPacket::getEstimatedSize() +{ + return 2 + identifier.length() * 2 + 2 + length; +} \ No newline at end of file diff --git a/Minecraft.World/CustomPayloadPacket.h b/Minecraft.World/CustomPayloadPacket.h new file mode 100644 index 00000000..95c5e708 --- /dev/null +++ b/Minecraft.World/CustomPayloadPacket.h @@ -0,0 +1,35 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class CustomPayloadPacket : public Packet, public enable_shared_from_this +{ +public: + + // Mojang-defined custom packets + static const wstring CUSTOM_BOOK_PACKET; + static const wstring CUSTOM_BOOK_SIGN_PACKET; + static const wstring TEXTURE_PACK_PACKET; + static const wstring TRADER_LIST_PACKET; + static const wstring TRADER_SELECTION_PACKET; + static const wstring SET_ADVENTURE_COMMAND_PACKET; + static const wstring SET_BEACON_PACKET; + static const wstring SET_ITEM_NAME_PACKET; + + wstring identifier; + int length; + byteArray data; + + CustomPayloadPacket(); + CustomPayloadPacket(const wstring &identifier, byteArray data); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new CustomPayloadPacket()); } + virtual int getId() { return 250; } +}; \ No newline at end of file diff --git a/Minecraft.World/DamageEnchantment.cpp b/Minecraft.World/DamageEnchantment.cpp new file mode 100644 index 00000000..686abc62 --- /dev/null +++ b/Minecraft.World/DamageEnchantment.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.item.h" +#include "DamageEnchantment.h" + +const int DamageEnchantment::names[] = {IDS_ENCHANTMENT_DAMAGE_ALL, IDS_ENCHANTMENT_DAMAGE_UNDEAD, IDS_ENCHANTMENT_DAMAGE_ARTHROPODS}; +const int DamageEnchantment::minCost[] = {1, 5, 5}; +const int DamageEnchantment::levelCost[] = {11, 8, 8}; +const int DamageEnchantment::levelCostSpan[] = {20, 20, 20}; + +DamageEnchantment::DamageEnchantment(int id, int frequency, int type) : Enchantment(id, frequency, EnchantmentCategory::weapon), type(type) +{ +} + +int DamageEnchantment::getMinCost(int level) +{ + return minCost[type] + (level - 1) * levelCost[type]; +} + +int DamageEnchantment::getMaxCost(int level) +{ + return getMinCost(level) + levelCostSpan[type]; +} + +int DamageEnchantment::getMaxLevel() +{ + return 5; +} + +int DamageEnchantment::getDamageBonus(int level, shared_ptr target) +{ + if (type == ALL) + { + return Mth::floor(level * 2.75f); + } + if (type == UNDEAD && target->getMobType() == UNDEAD) + { + return Mth::floor(level * 4.5f); + } + if (type == ARTHROPODS && target->getMobType() == ARTHROPOD) + { + return Mth::floor(level * 4.5f); + } + return 0; +} + +int DamageEnchantment::getDescriptionId() +{ + return names[type]; +} + +bool DamageEnchantment::isCompatibleWith(Enchantment *other) const +{ + return dynamic_cast(other) == NULL; +} + +bool DamageEnchantment::canEnchant(shared_ptr item) +{ + HatchetItem *hatchet = dynamic_cast(item->getItem()); + if (hatchet) return true; + return Enchantment::canEnchant(item); +} \ No newline at end of file diff --git a/Minecraft.World/DamageEnchantment.h b/Minecraft.World/DamageEnchantment.h new file mode 100644 index 00000000..00f9b047 --- /dev/null +++ b/Minecraft.World/DamageEnchantment.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Enchantment.h" + +class DamageEnchantment : public Enchantment +{ +public: + static const int ALL = 0; + static const int UNDEAD = 1; + static const int ARTHROPODS = 2; + +private: + static const int names[]; + static const int minCost[]; + static const int levelCost[]; + static const int levelCostSpan[]; + +public: + const int type; + + DamageEnchantment(int id, int frequency, int type); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); + virtual int getDamageBonus(int level, shared_ptr target); + virtual int getDescriptionId(); + virtual bool isCompatibleWith(Enchantment *other) const; + virtual bool canEnchant(shared_ptr item); +}; \ No newline at end of file diff --git a/Minecraft.World/DamageSource.cpp b/Minecraft.World/DamageSource.cpp new file mode 100644 index 00000000..29ea727e --- /dev/null +++ b/Minecraft.World/DamageSource.cpp @@ -0,0 +1,180 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.food.h" +#include "net.minecraft.network.packet.h" + +DamageSource *DamageSource::inFire = (new DamageSource(ChatPacket::e_ChatDeathInFire))->setIsFire(); +DamageSource *DamageSource::onFire = (new DamageSource(ChatPacket::e_ChatDeathOnFire))->bypassArmor()->setIsFire(); +DamageSource *DamageSource::lava = (new DamageSource(ChatPacket::e_ChatDeathLava))->setIsFire(); +DamageSource *DamageSource::inWall = (new DamageSource(ChatPacket::e_ChatDeathInWall))->bypassArmor(); +DamageSource *DamageSource::drown = (new DamageSource(ChatPacket::e_ChatDeathDrown))->bypassArmor(); +DamageSource *DamageSource::starve = (new DamageSource(ChatPacket::e_ChatDeathStarve))->bypassArmor(); +DamageSource *DamageSource::cactus = new DamageSource(ChatPacket::e_ChatDeathCactus); +DamageSource *DamageSource::fall = (new DamageSource(ChatPacket::e_ChatDeathFall))->bypassArmor(); +DamageSource *DamageSource::outOfWorld = (new DamageSource(ChatPacket::e_ChatDeathOutOfWorld))->bypassArmor()->bypassInvul(); +DamageSource *DamageSource::genericSource = (new DamageSource(ChatPacket::e_ChatDeathGeneric))->bypassArmor(); +DamageSource *DamageSource::explosion = (new DamageSource(ChatPacket::e_ChatDeathExplosion))->setScalesWithDifficulty(); +DamageSource *DamageSource::controlledExplosion = (new DamageSource(ChatPacket::e_ChatDeathExplosion)); +DamageSource *DamageSource::magic = (new DamageSource(ChatPacket::e_ChatDeathMagic))->bypassArmor()->setMagic(); +DamageSource *DamageSource::dragonbreath = (new DamageSource(ChatPacket::e_ChatDeathDragonBreath))->bypassArmor(); +DamageSource *DamageSource::wither = (new DamageSource(ChatPacket::e_ChatDeathWither))->bypassArmor(); +DamageSource *DamageSource::anvil = (new DamageSource(ChatPacket::e_ChatDeathAnvil)); +DamageSource *DamageSource::fallingBlock = (new DamageSource(ChatPacket::e_ChatDeathFallingBlock)); + +DamageSource *DamageSource::mobAttack(shared_ptr mob) +{ + return new EntityDamageSource(ChatPacket::e_ChatDeathMob, mob); +} + +DamageSource *DamageSource::playerAttack(shared_ptr player) +{ + return new EntityDamageSource(ChatPacket::e_ChatDeathPlayer, player); +} + +DamageSource *DamageSource::arrow(shared_ptr arrow, shared_ptr owner) +{ + return (new IndirectEntityDamageSource(ChatPacket::e_ChatDeathArrow, arrow, owner))->setProjectile(); +} + +DamageSource *DamageSource::fireball(shared_ptr fireball, shared_ptr owner) +{ + if (owner == NULL) + { + return (new IndirectEntityDamageSource(ChatPacket::e_ChatDeathOnFire, fireball, fireball))->setIsFire()->setProjectile(); + } + return (new IndirectEntityDamageSource(ChatPacket::e_ChatDeathFireball, fireball, owner))->setIsFire()->setProjectile(); +} + +DamageSource *DamageSource::thrown(shared_ptr entity, shared_ptr owner) +{ + return (new IndirectEntityDamageSource(ChatPacket::e_ChatDeathThrown, entity, owner))->setProjectile(); +} + +DamageSource *DamageSource::indirectMagic(shared_ptr entity, shared_ptr owner) +{ + return (new IndirectEntityDamageSource(ChatPacket::e_ChatDeathIndirectMagic, entity, owner) )->bypassArmor()->setMagic();; +} + +DamageSource *DamageSource::thorns(shared_ptr source) +{ + return (new EntityDamageSource(ChatPacket::e_ChatDeathThorns, source))->setMagic(); +} + +bool DamageSource::isProjectile() +{ + return _isProjectile; +} + +DamageSource *DamageSource::setProjectile() +{ + this->_isProjectile = true; + return this; +} + +bool DamageSource::isBypassArmor() +{ + return _bypassArmor; +} + +float DamageSource::getFoodExhaustion() +{ + return exhaustion; +} + +bool DamageSource::isBypassInvul() +{ + return _bypassInvul; +} + + +//DamageSource::DamageSource(const wstring &msgId) +DamageSource::DamageSource(ChatPacket::EChatPacketMessage msgId) +{ + // 4J added initialisors + _bypassArmor = false; + _bypassInvul = false; + // food exhastion caused by being damaged by this source + exhaustion = FoodConstants::EXHAUSTION_ATTACK; + isFireSource = false; + _isProjectile = false; + _isMagic = false; + + //this->msgId = msgId; + m_msgId = msgId; +} + +shared_ptr DamageSource::getDirectEntity() +{ + return getEntity(); +} + +shared_ptr DamageSource::getEntity() +{ + return shared_ptr(); +} + +DamageSource *DamageSource::bypassArmor() +{ + _bypassArmor = true; + // these kinds of damages don't cause the player to grow more hungry + exhaustion = 0; + return this; +} + +DamageSource *DamageSource::bypassInvul() +{ + _bypassInvul = true; + return this; +} + +DamageSource *DamageSource::setIsFire() +{ + isFireSource = true; + return this; +} + +DamageSource *DamageSource::setScalesWithDifficulty() +{ + _scalesWithDifficulty = true; + return this; +} + +bool DamageSource::scalesWithDifficulty() +{ + return _scalesWithDifficulty; +} + +bool DamageSource::isMagic() +{ + return _isMagic; +} + +DamageSource *DamageSource::setMagic() +{ + _isMagic = true; + return this; +} + +//wstring DamageSource::getLocalizedDeathMessage(shared_ptr player) +//{ +// return L"death." + msgId + player->name; +// //return I18n.get(L"death." + msgId, player.name); +//} + +shared_ptr DamageSource::getDeathMessagePacket(shared_ptr player) +{ + return shared_ptr( new ChatPacket(player->name, m_msgId ) ); +} + +bool DamageSource::isFire() +{ + return isFireSource; +} + +ChatPacket::EChatPacketMessage DamageSource::getMsgId() +{ + return m_msgId; +} \ No newline at end of file diff --git a/Minecraft.World/DamageSource.h b/Minecraft.World/DamageSource.h new file mode 100644 index 00000000..2e1d249d --- /dev/null +++ b/Minecraft.World/DamageSource.h @@ -0,0 +1,90 @@ +#pragma once +using namespace std; + +class Mob; +class Entity; +class Arrow; +class Fireball; +class Player; + +#include "ChatPacket.h" + +class DamageSource +{ +public: + static DamageSource *inFire; + static DamageSource *onFire; + static DamageSource *lava; + static DamageSource *inWall; + static DamageSource *drown; + static DamageSource *starve; + static DamageSource *cactus; + static DamageSource *fall; + static DamageSource *outOfWorld; + static DamageSource *genericSource; + static DamageSource *explosion; + static DamageSource *controlledExplosion; + static DamageSource *magic; + static DamageSource *dragonbreath; + static DamageSource *wither; + static DamageSource *anvil; + static DamageSource *fallingBlock; + + static DamageSource *mobAttack(shared_ptr mob); + static DamageSource *playerAttack(shared_ptr player); + static DamageSource *arrow(shared_ptr arrow, shared_ptr owner); + static DamageSource *fireball(shared_ptr fireball, shared_ptr owner); + static DamageSource *thrown(shared_ptr entity, shared_ptr owner); + static DamageSource *indirectMagic(shared_ptr entity, shared_ptr owner); + static DamageSource *thorns(shared_ptr source); + +private: + bool _bypassArmor; + bool _bypassInvul; + // food exhastion caused by being damaged by this source + float exhaustion; + bool isFireSource; + bool _isProjectile; + bool _scalesWithDifficulty; + bool _isMagic; + +public: + bool isProjectile(); + DamageSource *setProjectile(); + + bool isBypassArmor(); + float getFoodExhaustion(); + bool isBypassInvul(); + + //wstring msgId; + ChatPacket::EChatPacketMessage m_msgId; // 4J Made int so we can localise + +protected: + //DamageSource(const wstring &msgId); + DamageSource(ChatPacket::EChatPacketMessage msgId); + +public: + virtual ~DamageSource() {} + + virtual shared_ptr getDirectEntity(); + virtual shared_ptr getEntity(); + +protected: + DamageSource *bypassArmor(); + DamageSource *bypassInvul(); + DamageSource *setIsFire(); + DamageSource *setScalesWithDifficulty(); + +public: + virtual bool scalesWithDifficulty(); + + bool isMagic(); + DamageSource *setMagic(); + + // 4J Stu - Made return a packet + //virtual wstring getLocalizedDeathMessage(shared_ptr player); + virtual shared_ptr getDeathMessagePacket(shared_ptr player); + + bool isFire(); + ChatPacket::EChatPacketMessage getMsgId(); // 4J Stu - Used to return String +}; \ No newline at end of file diff --git a/Minecraft.World/DataInput.h b/Minecraft.World/DataInput.h new file mode 100644 index 00000000..258b2717 --- /dev/null +++ b/Minecraft.World/DataInput.h @@ -0,0 +1,22 @@ +#pragma once + +class DataInput +{ +public: + virtual int read() = 0; + virtual int read(byteArray b) = 0; + virtual int read(byteArray b, unsigned int offset, unsigned int length) = 0; + virtual bool readBoolean() = 0; + virtual byte readByte() = 0; + virtual unsigned char readUnsignedByte() = 0; + virtual bool readFully(byteArray a) = 0; + virtual double readDouble() = 0; + virtual float readFloat() = 0; + virtual int readInt() = 0; + virtual __int64 readLong() = 0; + virtual short readShort() = 0; + virtual wchar_t readChar() = 0; + virtual wstring readUTF() = 0; + virtual PlayerUID readPlayerUID() = 0; // 4J Added + virtual int skipBytes(int n) = 0; +}; diff --git a/Minecraft.World/DataInputStream.cpp b/Minecraft.World/DataInputStream.cpp new file mode 100644 index 00000000..2291bf48 --- /dev/null +++ b/Minecraft.World/DataInputStream.cpp @@ -0,0 +1,546 @@ +#include "stdafx.h" +#include "BasicTypeContainers.h" + +#include "DataInputStream.h" + +//Creates a DataInputStream that uses the specified underlying InputStream. +//Parameters: +//in - the specified input stream +DataInputStream::DataInputStream(InputStream *in) : stream( in ) +{ +} + +//Reads the next byte of data from this input stream. The value byte is returned as an int in the range 0 to 255. +//If no byte is available because the end of the stream has been reached, the value -1 is returned. +//This method blocks until input data is available, the end of the stream is detected, or an exception is thrown. +//This method simply performs in.read() and returns the result. +int DataInputStream::read() +{ + return stream->read(); +} + +//Reads some number of bytes from the contained input stream and stores them into the buffer array b. +//The number of bytes actually read is returned as an integer. This method blocks until input data is available, +//end of file is detected, or an exception is thrown. +//If b is null, a NullPointerException is thrown. If the length of b is zero, then no bytes are read and 0 is returned; +//otherwise, there is an attempt to read at least one byte. If no byte is available because the stream is at end of file, +//the value -1 is returned; otherwise, at least one byte is read and stored into b. +// +//The first byte read is stored into element b[0], the next one into b[1], and so on. The number of bytes read is, at most, +//equal to the length of b. Let k be the number of bytes actually read; these bytes will be stored in elements b[0] through b[k-1], +//leaving elements b[k] through b[b.length-1] unaffected. +// +//The read(b) method has the same effect as: +// +// read(b, 0, b.length) +// +//Overrides: +//read in class FilterInputStream +//Parameters: +//b - the buffer into which the data is read. +//Returns: +//the total number of bytes read into the buffer, or -1 if there is no more data because the end of the stream has been reached. +int DataInputStream::read(byteArray b) +{ + return read( b, 0, b.length ); +} + +//Reads up to len bytes of data from the contained input stream into an array of bytes. An attempt is made to read as many as len bytes, +//but a smaller number may be read, possibly zero. The number of bytes actually read is returned as an integer. +//This method blocks until input data is available, end of file is detected, or an exception is thrown. +// +//If len is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. +//If no byte is available because the stream is at end of file, the value -1 is returned; otherwise, at least one byte is read and stored into b. +// +//The first byte read is stored into element b[off], the next one into b[off+1], and so on. The number of bytes read is, +//at most, equal to len. Let k be the number of bytes actually read; these bytes will be stored in elements b[off] through b[off+k-1], +//leaving elements b[off+k] through b[off+len-1] unaffected. +// +//In every case, elements b[0] through b[off] and elements b[off+len] through b[b.length-1] are unaffected. +// +//Overrides: +//read in class FilterInputStream +//Parameters: +//b - the buffer into which the data is read. +//off - the start offset in the destination array b +//len - the maximum number of bytes read. +//Returns: +//the total number of bytes read into the buffer, or -1 if there is no more data because the end of the stream has been reached. +int DataInputStream::read(byteArray b, unsigned int offset, unsigned int length) +{ + return stream->read( b, offset, length ); +} + +//Closes this input stream and releases any system resources associated with the stream. This method simply performs in.close() +void DataInputStream::close() +{ + stream->close(); +} + +//Reads one input byte and returns true if that byte is nonzero, false if that byte is zero. This method is suitable for reading +//the byte written by the writeBoolean method of interface DataOutput. +//Returns: +//the boolean value read. +bool DataInputStream::readBoolean() +{ + return stream->read() != 0; +} + +//Reads and returns one input byte. The byte is treated as a signed value in the range -128 through 127, inclusive. +//This method is suitable for reading the byte written by the writeByte method of interface DataOutput. +//Returns: +//the 8-bit value read. +byte DataInputStream::readByte() +{ + return (byte) stream->read(); +} + +unsigned char DataInputStream::readUnsignedByte() +{ + return (unsigned char) stream->read(); +} + +//Reads two input bytes and returns a char value. Let a be the first byte read and b be the second byte. The value returned is: +//(char)((a << 8) | (b & 0xff)) +// +//This method is suitable for reading bytes written by the writeChar method of interface DataOutput. +//Returns: +//the char value read. +wchar_t DataInputStream::readChar() +{ + int a = stream->read(); + int b = stream->read(); + return (wchar_t)((a << 8) | (b & 0xff)); +} + +//Reads some bytes from an input stream and stores them into the buffer array b. The number of bytes read is equal to the length of b. +//This method blocks until one of the following conditions occurs: +// +//b.length bytes of input data are available, in which case a normal return is made. +//End of file is detected, in which case an EOFException is thrown. +//An I/O error occurs, in which case an IOException other than EOFException is thrown. +//If b is null, a NullPointerException is thrown. If b.length is zero, then no bytes are read. Otherwise, the first byte read is +//stored into element b[0], the next one into b[1], and so on. If an exception is thrown from this method, then it may be that some but +//not all bytes of b have been updated with data from the input stream. +// +//Parameters: +//b - the buffer into which the data is read. +bool DataInputStream::readFully(byteArray b) +{ + // TODO 4J Stu - I am not entirely sure if this matches the implementation of the Java library + // TODO 4J Stu - Need to handle exceptions here is we throw them in other InputStreams + for(unsigned int i = 0; i < b.length ;i++) + { + int byteRead = stream->read(); + if( byteRead == -1 ) + { + return false; + } + else + { + b[i] = byteRead; + } + } + return true; +} + +bool DataInputStream::readFully(charArray b) +{ + // TODO 4J Stu - I am not entirely sure if this matches the implementation of the Java library + // TODO 4J Stu - Need to handle exceptions here is we throw them in other InputStreams + for(unsigned int i = 0; i < b.length ;i++) + { + int byteRead = stream->read(); + if( byteRead == -1 ) + { + return false; + } + else + { + b[i] = byteRead; + } + } + return true; +} + +//Reads eight input bytes and returns a double value. It does this by first constructing a long value in exactly the manner +//of the readlong method, then converting this long value to a double in exactly the manner of the method Double.longBitsToDouble. +//This method is suitable for reading bytes written by the writeDouble method of interface DataOutput. +//Returns: +//the double value read. +double DataInputStream::readDouble() +{ + __int64 bits = readLong(); + + return Double::longBitsToDouble( bits ); +} + +//Reads four input bytes and returns a float value. It does this by first constructing an int value in exactly the manner +//of the readInt method, then converting this int value to a float in exactly the manner of the method Float.intBitsToFloat. +//This method is suitable for reading bytes written by the writeFloat method of interface DataOutput. +//Returns: +//the float value read. +float DataInputStream::readFloat() +{ + int bits = readInt(); + + return Float::intBitsToFloat( bits ); +} + +//Reads four input bytes and returns an int value. Let a-d be the first through fourth bytes read. The value returned is: +// +// (((a & 0xff) << 24) | ((b & 0xff) << 16) | +// ((c & 0xff) << 8) | (d & 0xff)) +// +//This method is suitable for reading bytes written by the writeInt method of interface DataOutput. +//Returns: +//the int value read. +int DataInputStream::readInt() +{ + int a = stream->read(); + int b = stream->read(); + int c = stream->read(); + int d = stream->read(); + int bits = (((a & 0xff) << 24) | ((b & 0xff) << 16) | + ((c & 0xff) << 8) | (d & 0xff)); + return bits; +} + +//Reads eight input bytes and returns a long value. Let a-h be the first through eighth bytes read. The value returned is: +// +// (((long)(a & 0xff) << 56) | +// ((long)(b & 0xff) << 48) | +// ((long)(c & 0xff) << 40) | +// ((long)(d & 0xff) << 32) | +// ((long)(e & 0xff) << 24) | +// ((long)(f & 0xff) << 16) | +// ((long)(g & 0xff) << 8) | +// ((long)(h & 0xff))) +// +//This method is suitable for reading bytes written by the writeLong method of interface DataOutput. +// +//Returns: +//the long value read. +__int64 DataInputStream::readLong() +{ + __int64 a = stream->read(); + __int64 b = stream->read(); + __int64 c = stream->read(); + __int64 d = stream->read(); + __int64 e = stream->read(); + __int64 f = stream->read(); + __int64 g = stream->read(); + __int64 h = stream->read(); + + __int64 bits = (((a & 0xff) << 56) | + ((b & 0xff) << 48) | + ((c & 0xff) << 40) | + ((d & 0xff) << 32) | + ((e & 0xff) << 24) | + ((f & 0xff) << 16) | + ((g & 0xff) << 8) | + ((h & 0xff))); + + return bits; +} + +//Reads two input bytes and returns a short value. Let a be the first byte read and b be the second byte. The value returned is: +//(short)((a << 8) | (b & 0xff)) +// +//This method is suitable for reading the bytes written by the writeShort method of interface DataOutput. +//Returns: +//the 16-bit value read. +short DataInputStream::readShort() +{ + int a = stream->read(); + int b = stream->read(); + return (short)((a << 8) | (b & 0xff)); +} + +//Reads in a string that has been encoded using a modified UTF-8 format. The general contract of readUTF is that it reads a representation +//of a Unicode character string encoded in modified UTF-8 format; this string of characters is then returned as a String. +//First, two bytes are read and used to construct an unsigned 16-bit integer in exactly the manner of the readUnsignedShort method . +//This integer value is called the UTF length and specifies the number of additional bytes to be read. These bytes are then converted +//to characters by considering them in groups. The length of each group is computed from the value of the first byte of the group. +//The byte following a group, if any, is the first byte of the next group. +// +//If the first byte of a group matches the bit pattern 0xxxxxxx (where x means "may be 0 or 1"), then the group consists of just that byte. +//The byte is zero-extended to form a character. +// +//If the first byte of a group matches the bit pattern 110xxxxx, then the group consists of that byte a and a second byte b. +//If there is no byte b (because byte a was the last of the bytes to be read), or if byte b does not match the bit pattern 10xxxxxx, +//then a UTFDataFormatException is thrown. Otherwise, the group is converted to the character: +// +//(char)(((a& 0x1F) << 6) | (b & 0x3F)) +// +//If the first byte of a group matches the bit pattern 1110xxxx, then the group consists of that byte a and two more bytes b and c. +//If there is no byte c (because byte a was one of the last two of the bytes to be read), or either byte b or byte c does not match the bit +//pattern 10xxxxxx, then a UTFDataFormatException is thrown. Otherwise, the group is converted to the character: +// +// (char)(((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F)) +// +//If the first byte of a group matches the pattern 1111xxxx or the pattern 10xxxxxx, then a UTFDataFormatException is thrown. +//If end of file is encountered at any time during this entire process, then an EOFException is thrown. +// +//After every group has been converted to a character by this process, the characters are gathered, in the same order in which their +//corresponding groups were read from the input stream, to form a String, which is returned. +// +//The writeUTF method of interface DataOutput may be used to write data that is suitable for reading by this method. +// +//Returns: +//a Unicode string. +wstring DataInputStream::readUTF() +{ + wstring outputString; + int a = stream->read(); + int b = stream->read(); + unsigned short UTFLength = (unsigned short) (((a & 0xff) << 8) | (b & 0xff)); + + //// 4J Stu - I decided while writing DataOutputStream that we didn't need to bother using the UTF8 format + //// used in the java libs, and just write in/out as wchar_t all the time + + /*for( unsigned short i = 0; i < UTFLength; i++) + { + wchar_t theChar = readChar(); + outputString.push_back(theChar); + }*/ + + + unsigned short currentByteIndex = 0; + while( currentByteIndex < UTFLength ) + { + int firstByte = stream->read(); + currentByteIndex++; + + if( firstByte == -1 ) + // TODO 4J Stu - EOFException + break; + + // Masking patterns: + // 10000000 = 0x80 // Match only highest bit + // 11000000 = 0xC0 // Match only highest two bits + // 11100000 = 0xE0 // Match only highest three bits + // 11110000 = 0xF0 // Match only highest four bits + + // Matching patterns: + // 10xxxxxx = 0x80 // ERROR, or second/third byte + // 1111xxxx = 0xF0 //ERROR + // 0xxxxxxx = 0x00 // One byte UTF + // 110xxxxx = 0xC0 // Two byte UTF + // 1110xxxx = 0xE0 // Three byte UTF + if( ( (firstByte & 0xC0 ) == 0x80 ) || ( (firstByte & 0xF0) == 0xF0) ) + { + // TODO 4J Stu - UTFDataFormatException + break; + } + else if( (firstByte & 0x80) == 0x00 ) + { + // One byte UTF + wchar_t readChar = (wchar_t)firstByte; + outputString.push_back( readChar ); + continue; + } + else if( (firstByte & 0xE0) == 0xC0 ) + { + // Two byte UTF + + // No more bytes to read + if( !(currentByteIndex < UTFLength) ) + { + // TODO 4J Stu - UTFDataFormatException + break; + } + + int secondByte = stream->read(); + currentByteIndex++; + + // No second byte + if( secondByte == -1 ) + { + // TODO 4J Stu - EOFException + break; + } + // Incorrect second byte pattern + else if( (secondByte & 0xC0 ) != 0x80 ) + { + // TODO 4J Stu - UTFDataFormatException + break; + } + + wchar_t readChar = (wchar_t)( ((firstByte& 0x1F) << 6) | (secondByte & 0x3F) ); + outputString.push_back( readChar ); + continue; + } + else if( (firstByte & 0xF0) == 0xE0 ) + { + // Three byte UTF + + // No more bytes to read + if( !(currentByteIndex < UTFLength) ) + { + // TODO 4J Stu - UTFDataFormatException + break; + } + + int secondByte = stream->read(); + currentByteIndex++; + + // No second byte + if( secondByte == -1 ) + { + // TODO 4J Stu - EOFException + break; + } + + // No more bytes to read + if( !(currentByteIndex < UTFLength) ) + { + // TODO 4J Stu - UTFDataFormatException + break; + } + + int thirdByte = stream->read(); + currentByteIndex++; + + // No third byte + if( thirdByte == -1 ) + { + // TODO 4J Stu - EOFException + break; + } + // Incorrect second or third byte pattern + else if( ( (secondByte & 0xC0 ) != 0x80 ) || ( (thirdByte & 0xC0 ) != 0x80 ) ) + { + // TODO 4J Stu - UTFDataFormatException + break; + } + + wchar_t readChar = (wchar_t)(((firstByte & 0x0F) << 12) | ((secondByte & 0x3F) << 6) | (thirdByte & 0x3F)); + outputString.push_back( readChar ); + continue; + } + } + + return outputString; +} + +int DataInputStream::readUTFChar() +{ + int returnValue = -1; + int firstByte = stream->read(); + + if( firstByte == -1 ) + // TODO 4J Stu - EOFException + return returnValue; + + // Masking patterns: + // 10000000 = 0x80 // Match only highest bit + // 11000000 = 0xC0 // Match only highest two bits + // 11100000 = 0xE0 // Match only highest three bits + // 11110000 = 0xF0 // Match only highest four bits + + // Matching patterns: + // 10xxxxxx = 0x80 // ERROR, or second/third byte + // 1111xxxx = 0xF0 //ERROR + // 0xxxxxxx = 0x00 // One byte UTF + // 110xxxxx = 0xC0 // Two byte UTF + // 1110xxxx = 0xE0 // Three byte UTF + if( ( (firstByte & 0xC0 ) == 0x80 ) || ( (firstByte & 0xF0) == 0xF0) ) + { + // TODO 4J Stu - UTFDataFormatException + return returnValue; + } + else if( (firstByte & 0x80) == 0x00 ) + { + // One byte UTF + returnValue = firstByte; + } + else if( (firstByte & 0xE0) == 0xC0 ) + { + // Two byte UTF + int secondByte = stream->read(); + + // No second byte + if( secondByte == -1 ) + { + // TODO 4J Stu - EOFException + return returnValue; + } + // Incorrect second byte pattern + else if( (secondByte & 0xC0 ) != 0x80 ) + { + // TODO 4J Stu - UTFDataFormatException + return returnValue; + } + + returnValue = ((firstByte& 0x1F) << 6) | (secondByte & 0x3F); + } + else if( (firstByte & 0xF0) == 0xE0 ) + { + // Three byte UTF + + int secondByte = stream->read(); + + // No second byte + if( secondByte == -1 ) + { + // TODO 4J Stu - EOFException + return returnValue; + } + + int thirdByte = stream->read(); + + // No third byte + if( thirdByte == -1 ) + { + // TODO 4J Stu - EOFException + return returnValue; + } + // Incorrect second or third byte pattern + else if( ( (secondByte & 0xC0 ) != 0x80 ) || ( (thirdByte & 0xC0 ) != 0x80 ) ) + { + // TODO 4J Stu - UTFDataFormatException + return returnValue; + } + + returnValue = (((firstByte & 0x0F) << 12) | ((secondByte & 0x3F) << 6) | (thirdByte & 0x3F)); + } + return returnValue; +} + +// 4J Added +PlayerUID DataInputStream::readPlayerUID() +{ + PlayerUID returnValue; +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + for(int idPos=0;idPosskip(n); +} + +int DataInputStream::skipBytes(int n) +{ + return skip(n); +} \ No newline at end of file diff --git a/Minecraft.World/DataInputStream.h b/Minecraft.World/DataInputStream.h new file mode 100644 index 00000000..f69d490a --- /dev/null +++ b/Minecraft.World/DataInputStream.h @@ -0,0 +1,35 @@ +#pragma once +// 4J Stu - Represents Java standard library class (although we miss out an intermediate inheritance class that we don't care about) + +#include "InputStream.h" +#include "DataInput.h" + +class DataInputStream : public InputStream, public DataInput +{ +private: + InputStream *stream; + +public: + DataInputStream(InputStream *in); + virtual int read(); + virtual int read(byteArray b); + virtual int read(byteArray b, unsigned int offset, unsigned int length); + virtual void close(); + virtual bool readBoolean(); + virtual byte readByte(); + virtual unsigned char readUnsignedByte(); + virtual wchar_t readChar(); + virtual bool readFully(byteArray b); + virtual bool readFully(charArray b); + virtual double readDouble(); + virtual float readFloat(); + virtual int readInt(); + virtual __int64 readLong(); + virtual short readShort(); + virtual wstring readUTF(); + void deleteChildStream(); + virtual int readUTFChar(); + virtual PlayerUID readPlayerUID(); // 4J Added + virtual __int64 skip(__int64 n); + virtual int skipBytes(int n); +}; \ No newline at end of file diff --git a/Minecraft.World/DataLayer.cpp b/Minecraft.World/DataLayer.cpp new file mode 100644 index 00000000..a5e7d088 --- /dev/null +++ b/Minecraft.World/DataLayer.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "DataLayer.h" + +DataLayer::DataLayer(int length, int depthBits) : depthBits(depthBits), depthBitsPlusFour(depthBits + 4) +{ + data = byteArray(length >> 1); +} + +DataLayer::DataLayer(byteArray data, int depthBits) : depthBits(depthBits), depthBitsPlusFour(depthBits + 4) +{ + this->data = data; +} + +DataLayer::~DataLayer() +{ + delete[] data.data; +} + +int DataLayer::get(int x, int y, int z) +{ + int pos = (x << depthBitsPlusFour | z << depthBits | y); + int slot = pos >> 1; + int part = pos & 1; + + if (part == 0) + { + return data[slot] & 0xf; + } else + { + return (data[slot] >> 4) & 0xf; + } +} + +void DataLayer::set(int x, int y, int z, int val) +{ + int pos = (x << depthBitsPlusFour | z << depthBits | y); + + int slot = pos >> 1; + int part = pos & 1; + + if (part == 0) + { + data[slot] = (byte) ((data[slot] & 0xf0) | (val & 0xf)); + } else + { + data[slot] = (byte) ((data[slot] & 0x0f) | ((val & 0xf) << 4)); + } +} + +bool DataLayer::isValid() +{ + return data.data != NULL; +} + +void DataLayer::setAll(int br) +{ + byte val = (byte) (br & (br << 4)); + for (unsigned int i = 0; i < data.length; i++) + { + data[i] = val; + } +} \ No newline at end of file diff --git a/Minecraft.World/DataLayer.h b/Minecraft.World/DataLayer.h new file mode 100644 index 00000000..798215e6 --- /dev/null +++ b/Minecraft.World/DataLayer.h @@ -0,0 +1,22 @@ +#pragma once + +class DataLayer +{ +public: + byteArray data; + +private: + const int depthBits; + const int depthBitsPlusFour; + +public: + DataLayer(int length, int depthBits); + DataLayer(byteArray data, int depthBits); + ~DataLayer(); + + int get(int x, int y, int z); + + void set(int x, int y, int z, int val); + bool isValid(); + void setAll(int br); +}; diff --git a/Minecraft.World/DataOutput.h b/Minecraft.World/DataOutput.h new file mode 100644 index 00000000..648be6df --- /dev/null +++ b/Minecraft.World/DataOutput.h @@ -0,0 +1,20 @@ +#pragma once + +class DataOutput +{ +public: + virtual void write(unsigned int b) = 0; + virtual void write(byteArray b) = 0; + virtual void write(byteArray b, unsigned int offset, unsigned int length) = 0; + virtual void writeByte(byte a) = 0; + virtual void writeDouble(double a) = 0; + virtual void writeFloat(float a) = 0; + virtual void writeInt(int a) = 0; + virtual void writeLong(__int64 a) = 0; + virtual void writeShort(short a) = 0; + virtual void writeBoolean(bool v) = 0; + virtual void writeChar(wchar_t v) = 0; + virtual void writeChars(const wstring& s) = 0; + virtual void writeUTF(const wstring& a) = 0; + virtual void writePlayerUID(PlayerUID player) = 0; // 4J Added +}; \ No newline at end of file diff --git a/Minecraft.World/DataOutputStream.cpp b/Minecraft.World/DataOutputStream.cpp new file mode 100644 index 00000000..8e277c23 --- /dev/null +++ b/Minecraft.World/DataOutputStream.cpp @@ -0,0 +1,268 @@ +#include "stdafx.h" +#include "BasicTypeContainers.h" + +#include "DataOutputStream.h" + +//Creates a new data output stream to write data to the specified underlying output stream. The counter written is set to zero. +//Parameters: +//out - the underlying output stream, to be saved for later use. +DataOutputStream::DataOutputStream( OutputStream *out ) : stream( out ), written( 0 ) +{ +} + +// 4J Stu - We cannot always delete the stream when we are destroyed, but we want to clear it up as there +// are occasions when we don't have a handle to the child stream elsewhere and want to delete it +void DataOutputStream::deleteChildStream() +{ + delete stream; +} + +//Writes the specified byte (the low eight bits of the argument b) to the underlying output stream. +//If no exception is thrown, the counter written is incremented by 1. +//Implements the write method of OutputStream. +//Parameters: +//b - the byte to be written. +void DataOutputStream::write(unsigned int b) +{ + stream->write( b ); + // TODO 4J Stu - Exception handling? + written++; +} + +void DataOutputStream::flush() +{ + stream->flush(); +} + +//Writes b.length bytes from the specified byte array to this output stream. +//The general contract for write(b) is that it should have exactly the same effect as the call write(b, 0, b.length). +//Parameters: +//b - the data. +void DataOutputStream::write(byteArray b) +{ + write(b, 0, b.length); +} + +//Writes len bytes from the specified byte array starting at offset off to the underlying output stream. +//If no exception is thrown, the counter written is incremented by len. +//Parameters: +//b - the data. +//off - the start offset in the data. +//len - the number of bytes to write. +void DataOutputStream::write(byteArray b, unsigned int offset, unsigned int length) +{ + stream->write(b, offset, length); + // TODO 4J Stu - Some form of error checking? + written += length; +} + +//Closes this output stream and releases any system resources associated with the stream. +//The close method of FilterOutputStream calls its flush method, and then calls the close method of its underlying output stream. +void DataOutputStream::close() +{ + stream->close(); +} + +//Writes out a byte to the underlying output stream as a 1-byte value. If no exception is thrown, the counter written is incremented by 1. +//Parameters: +//v - a byte value to be written. +void DataOutputStream::writeByte(byte a) +{ + stream->write( a ); +} + +//Converts the double argument to a long using the doubleToLongBits method in class Double, +//and then writes that long value to the underlying output stream as an 8-byte quantity, +//high byte first. If no exception is thrown, the counter written is incremented by 8. +//Parameters: +//v - a double value to be written. +void DataOutputStream::writeDouble(double a) +{ + __int64 bits = Double::doubleToLongBits( a ); + + writeLong( bits ); + // TODO 4J Stu - Error handling? + written += 8; +} + +//Converts the float argument to an int using the floatToIntBits method in class Float, +//and then writes that int value to the underlying output stream as a 4-byte quantity, high byte first. +//If no exception is thrown, the counter written is incremented by 4. +//Parameters: +//v - a float value to be written. +void DataOutputStream::writeFloat(float a) +{ + int bits = Float::floatToIntBits( a ); + + writeInt( bits ); + // TODO 4J Stu - Error handling? + written += 4; +} + +//Writes an int to the underlying output stream as four bytes, high byte first. If no exception is thrown, the counter written is incremented by 4. +//Parameters: +//v - an int to be written. +void DataOutputStream::writeInt(int a) +{ + stream->write( (a >> 24) & 0xff ); + stream->write( (a >> 16) & 0xff ); + stream->write( (a >> 8) & 0xff ); + stream->write( a & 0xff ); + // TODO 4J Stu - Error handling? + written += 4; +} + +//Writes a long to the underlying output stream as eight bytes, high byte first. +//In no exception is thrown, the counter written is incremented by 8. +//Parameters: +//v - a long to be written. +void DataOutputStream::writeLong(__int64 a) +{ + stream->write( (a >> 56) & 0xff ); + stream->write( (a >> 48) & 0xff ); + stream->write( (a >> 40) & 0xff ); + stream->write( (a >> 32) & 0xff ); + stream->write( (a >> 24) & 0xff ); + stream->write( (a >> 16) & 0xff ); + stream->write( (a >> 8) & 0xff ); + stream->write( a & 0xff ); + // TODO 4J Stu - Error handling? + written += 4; +} + +//Writes a short to the underlying output stream as two bytes, high byte first. +//If no exception is thrown, the counter written is incremented by 2. +//Parameters: +//v - a short to be written. +void DataOutputStream::writeShort(short a) +{ + stream->write( (a >> 8) & 0xff ); + stream->write( a & 0xff ); + // TODO 4J Stu - Error handling? + written += 2; +} + +//Writes a char to the underlying output stream as a 2-byte value, high byte first. +//If no exception is thrown, the counter written is incremented by 2. +//Parameters: +//v - a char value to be written. +void DataOutputStream::writeChar( wchar_t v ) +{ + stream->write( (v >> 8) & 0xff ); + stream->write( v & 0xff ); + // TODO 4J Stu - Error handling? + written += 2; +} + +//Writes a string to the underlying output stream as a sequence of characters. +//Each character is written to the data output stream as if by the writeChar method. +//If no exception is thrown, the counter written is incremented by twice the length of s. +//Parameters: +//s - a String value to be written. +void DataOutputStream::writeChars(const wstring& str) +{ + for( unsigned int i = 0; i < str.length(); i++) + { + writeChar( str.at( i ) ); + // TODO 4J Stu - Error handling? + } + // Incrementing handled by the writeChar function +} + +//Writes a boolean to the underlying output stream as a 1-byte value. +//The value true is written out as the value (byte)1; the value false is written out as the value (byte)0. +//If no exception is thrown, the counter written is incremented by 1. +//Parameters: +//v - a boolean value to be written. +void DataOutputStream::writeBoolean(bool b) +{ + stream->write( b ? (byte)1 : (byte)0 ); + // TODO 4J Stu - Error handling? + written += 1; +} + +//Writes a string to the underlying output stream using modified UTF-8 encoding in a machine-independent manner. +//First, two bytes are written to the output stream as if by the writeShort method giving the number of bytes to follow. +//This value is the number of bytes actually written out, not the length of the string. Following the length, +//each character of the string is output, in sequence, using the modified UTF-8 encoding for the character. +//If no exception is thrown, the counter written is incremented by the total number of bytes written to the output stream. +//This will be at least two plus the length of str, and at most two plus thrice the length of str. +//Parameters: +//str - a string to be written. +void DataOutputStream::writeUTF(const wstring& str) +{ + int strlen = (int)str.length(); + int utflen = 0; + int c, count = 0; + + /* use charAt instead of copying String to char array */ + for (int i = 0; i < strlen; i++) + { + c = str.at(i); + if ((c >= 0x0001) && (c <= 0x007F)) + { + utflen++; + } + else if (c > 0x07FF) + { + utflen += 3; + } + else + { + utflen += 2; + } + } + + //if (utflen > 65535) + // throw new UTFDataFormatException( + // "encoded string too long: " + utflen + " bytes"); + + byteArray bytearr(utflen+2); + + bytearr[count++] = (byte) ((utflen >> 8) & 0xFF); + bytearr[count++] = (byte) ((utflen >> 0) & 0xFF); + + int i=0; + for (i=0; i= 0x0001) && (c <= 0x007F))) break; + bytearr[count++] = (byte) c; + } + + for (;i < strlen; i++) + { + c = str.at(i); + if ((c >= 0x0001) && (c <= 0x007F)) + { + bytearr[count++] = (byte) c; + + } + else if (c > 0x07FF) + { + bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); + bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F)); + bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); + } + else + { + bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); + bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); + } + } + write(bytearr, 0, utflen+2); + delete[] bytearr.data; +} + +// 4J Added +void DataOutputStream::writePlayerUID(PlayerUID player) +{ +#if defined(__PS3__) || defined(__ORBIS__) || defined (__PSVITA__) + for(int idPos=0;idPostile = tile; +} + +bool DeadBushFeature::place(Level *level, Random *random, int x, int y, int z) +{ + int t = 0; + while (((t = level->getTile(x, y, z)) == 0 || t == Tile::leaves_Id) && y > 0) + y--; + + for (int i = 0; i < 4; i++) + { + int x2 = x + random->nextInt(8) - random->nextInt(8); + int y2 = y + random->nextInt(4) - random->nextInt(4); + int z2 = z + random->nextInt(8) - random->nextInt(8); + if (level->isEmptyTile(x2, y2, z2) ) + { + if (Tile::tiles[tile]->canSurvive(level, x2, y2, z2)) + { + level->setTileNoUpdate(x2, y2, z2, tile); + } + } + } + + return true; + +} \ No newline at end of file diff --git a/Minecraft.World/DeadBushFeature.h b/Minecraft.World/DeadBushFeature.h new file mode 100644 index 00000000..6b0a338f --- /dev/null +++ b/Minecraft.World/DeadBushFeature.h @@ -0,0 +1,15 @@ +#pragma once +#include "Feature.h" + +class Level; + +class DeadBushFeature : public Feature +{ +private: + int tile; + +public: + DeadBushFeature (int tile); + + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/DeadBushTile.cpp b/Minecraft.World/DeadBushTile.cpp new file mode 100644 index 00000000..33fdfe6c --- /dev/null +++ b/Minecraft.World/DeadBushTile.cpp @@ -0,0 +1,46 @@ +#include "stdafx.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "DeadBushTile.h" + +DeadBushTile::DeadBushTile(int id) : Bush(id,Material::replaceable_plant) +{ + updateDefaultShape(); +} + +// 4J Added override +void DeadBushTile::updateDefaultShape() +{ + float ss = 0.4f; + this->setShape(0.5f - ss, 0, 0.5f - ss, 0.5f + ss, 0.8f, 0.5f + ss); +} + +bool DeadBushTile::mayPlaceOn(int tile) +{ + return tile == Tile::sand_Id; +} + +int DeadBushTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return -1; +} + +void DeadBushTile::playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data) +{ + if (!level->isClientSide && player->getSelectedItem() != NULL && player->getSelectedItem()->id == Item::shears_Id) + { + player->awardStat( + GenericStats::blocksMined(id), + GenericStats::param_blocksMined(id,data,1) + ); + + // drop leaf block instead of sapling + popResource(level, x, y, z, shared_ptr(new ItemInstance(Tile::deadBush, 1, data))); + } + else + { + Bush::playerDestroy(level, player, x, y, z, data); + } +} diff --git a/Minecraft.World/DeadBushTile.h b/Minecraft.World/DeadBushTile.h new file mode 100644 index 00000000..2d74816e --- /dev/null +++ b/Minecraft.World/DeadBushTile.h @@ -0,0 +1,16 @@ +#pragma once +#include "Bush.h" + +class Random; + +class DeadBushTile : public Bush +{ + friend class Tile; +protected: + DeadBushTile(int id); + virtual bool mayPlaceOn(int tile); +public: + virtual void updateDefaultShape(); // 4J Added override + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual void playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data); +}; diff --git a/Minecraft.World/DebugOptionsPacket.cpp b/Minecraft.World/DebugOptionsPacket.cpp new file mode 100644 index 00000000..f287efcd --- /dev/null +++ b/Minecraft.World/DebugOptionsPacket.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.item.h" +#include "PacketListener.h" +#include "DebugOptionsPacket.h" + + + +DebugOptionsPacket::~DebugOptionsPacket() +{ +} + +DebugOptionsPacket::DebugOptionsPacket() +{ + m_uiVal = 0L; +} + +DebugOptionsPacket::DebugOptionsPacket(unsigned int uiVal) +{ + this->m_uiVal = uiVal; +} + +void DebugOptionsPacket::handle(PacketListener *listener) +{ + listener->handleDebugOptions(shared_from_this()); +} + +void DebugOptionsPacket::read(DataInputStream *dis) //throws IOException +{ + m_uiVal = (unsigned int)dis->readInt(); +} + +void DebugOptionsPacket::write(DataOutputStream *dos) // throws IOException +{ + dos->writeInt((int)m_uiVal); +} + +int DebugOptionsPacket::getEstimatedSize() +{ + return sizeof(int); +} diff --git a/Minecraft.World/DebugOptionsPacket.h b/Minecraft.World/DebugOptionsPacket.h new file mode 100644 index 00000000..9ac5ef2f --- /dev/null +++ b/Minecraft.World/DebugOptionsPacket.h @@ -0,0 +1,26 @@ +#pragma once + +// 4J ADDED THIS PACKET + +using namespace std; + +#include "Packet.h" + +class DebugOptionsPacket : public Packet, public enable_shared_from_this +{ +public: + unsigned int m_uiVal; + + DebugOptionsPacket(); + ~DebugOptionsPacket(); + DebugOptionsPacket(unsigned int uiVal); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new DebugOptionsPacket()); } + virtual int getId() { return 152; } +}; diff --git a/Minecraft.World/DecorationMaterial.h b/Minecraft.World/DecorationMaterial.h new file mode 100644 index 00000000..d3012e7e --- /dev/null +++ b/Minecraft.World/DecorationMaterial.h @@ -0,0 +1,12 @@ +#pragma once +#include "Material.h" + +class DecorationMaterial : public Material +{ +public: + DecorationMaterial(MaterialColor *color) : Material(color) { makeDestroyedByHand(); } + + virtual bool isSolid() { return false; } + virtual bool blocksLight() { return false; } + virtual bool blocksMotion() { return false; } +}; \ No newline at end of file diff --git a/Minecraft.World/DefaultGameModeCommand.cpp b/Minecraft.World/DefaultGameModeCommand.cpp new file mode 100644 index 00000000..529733f7 --- /dev/null +++ b/Minecraft.World/DefaultGameModeCommand.cpp @@ -0,0 +1,26 @@ +#include "stdafx.h" +#include "net.minecraft.commands.h" +#include "DefaultGameModeCommand.h" + +EGameCommand DefaultGameModeCommand::getId() +{ + return eGameCommand_DefaultGameMode; +} + +void DefaultGameModeCommand::execute(shared_ptr source, byteArray commandData) +{ + //if (args.length > 0) + //{ + // GameType newMode = getModeForString(source, args[0]); + // doSetGameType(newMode); + + // String modeName = I18n.get("gameMode." + newMode.getName()); + // logAdminAction(source, "commands.defaultgamemode.success", modeName); + + //} +} + +void DefaultGameModeCommand::doSetGameType(GameType *newGameType) +{ + //MinecraftServer::getInstance()->setDefaultGameMode(newGameType); +} \ No newline at end of file diff --git a/Minecraft.World/DefaultGameModeCommand.h b/Minecraft.World/DefaultGameModeCommand.h new file mode 100644 index 00000000..99d45a43 --- /dev/null +++ b/Minecraft.World/DefaultGameModeCommand.h @@ -0,0 +1,15 @@ +#pragma once + +#include "GameModeCommand.h" + +class GameType; + +class DefaultGameModeCommand : public GameModeCommand +{ +public: + virtual EGameCommand getId(); + virtual void execute(shared_ptr source, byteArray commandData); + +protected: + void doSetGameType(GameType *newGameType); +}; \ No newline at end of file diff --git a/Minecraft.World/DefendVillageTargetGoal.cpp b/Minecraft.World/DefendVillageTargetGoal.cpp new file mode 100644 index 00000000..ad6b3fd8 --- /dev/null +++ b/Minecraft.World/DefendVillageTargetGoal.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.village.h" +#include "net.minecraft.world.entity.animal.h" +#include "DefendVillageTargetGoal.h" + +DefendVillageTargetGoal::DefendVillageTargetGoal(VillagerGolem *golem) : TargetGoal(golem, 16, false, true) +{ + this->golem = golem; + setRequiredControlFlags(TargetGoal::TargetFlag); +} + +bool DefendVillageTargetGoal::canUse() +{ + shared_ptr village = golem->getVillage(); + if (village == NULL) return false; + potentialTarget = weak_ptr(village->getClosestAggressor(dynamic_pointer_cast(golem->shared_from_this()))); + return canAttack(potentialTarget.lock(), false); +} + +void DefendVillageTargetGoal::start() +{ + golem->setTarget(potentialTarget.lock()); + TargetGoal::start(); +} \ No newline at end of file diff --git a/Minecraft.World/DefendVillageTargetGoal.h b/Minecraft.World/DefendVillageTargetGoal.h new file mode 100644 index 00000000..d74d95b0 --- /dev/null +++ b/Minecraft.World/DefendVillageTargetGoal.h @@ -0,0 +1,18 @@ +#pragma once + +#include "TargetGoal.h" + +class VillagerGolem; + +class DefendVillageTargetGoal : public TargetGoal +{ +private: + VillagerGolem *golem; // Owner of this goal + weak_ptr potentialTarget; + +public: + DefendVillageTargetGoal(VillagerGolem *golem); + + bool canUse(); + void start(); +}; \ No newline at end of file diff --git a/Minecraft.World/Definitions.h b/Minecraft.World/Definitions.h new file mode 100644 index 00000000..d338019d --- /dev/null +++ b/Minecraft.World/Definitions.h @@ -0,0 +1,40 @@ +#pragma once +using namespace std; + +class AABB; +class Recipy; +class Object; + +typedef std::vector AABBList; +typedef std::vector RecipyList; +typedef std::vector ObjectList; + +#define MAX_PATH_SIZE 256 +#define PI (3.141592654f) +#define HALF_PI (1.570796327f) + +enum ByteOrder +{ + BIGENDIAN, + LITTLEENDIAN, + +#if defined(__PS3__) || defined(_XBOX) + LOCALSYTEM_ENDIAN = BIGENDIAN, +#else + LOCALSYTEM_ENDIAN = LITTLEENDIAN, +#endif +}; +enum EDefaultSkins +{ + eDefaultSkins_ServerSelected, + eDefaultSkins_Skin0, + eDefaultSkins_Skin1, + eDefaultSkins_Skin2, + eDefaultSkins_Skin3, + eDefaultSkins_Skin4, + eDefaultSkins_Skin5, + eDefaultSkins_Skin6, + eDefaultSkins_Skin7, + + eDefaultSkins_Count, +}; \ No newline at end of file diff --git a/Minecraft.World/DelayedRelease.cpp b/Minecraft.World/DelayedRelease.cpp new file mode 100644 index 00000000..ebd0398f --- /dev/null +++ b/Minecraft.World/DelayedRelease.cpp @@ -0,0 +1,46 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "DelayedRelease.h" + + + +DelayedRelease::DelayedRelease(Level *level, shared_ptr toRelease, int delay) : Entity(level) +{ + moveTo(toRelease->x, toRelease->y, toRelease->z, 0, 0); + this->toRelease = toRelease; + this->delay = delay; +} + + +bool DelayedRelease::makeStepSound() +{ + return false; +} + + +void DelayedRelease::tick() +{ + if (delay-- <= 0) + { + level->addEntity(toRelease); + remove(); + } +} + +bool DelayedRelease::hurt(DamageSource *source, int damage) +{ + return false; +} + + +void DelayedRelease::defineSynchedData() +{ +} + +void DelayedRelease::readAdditionalSaveData(CompoundTag *tag) +{ +} + +void DelayedRelease::addAdditonalSaveData(CompoundTag *tag) +{ +} \ No newline at end of file diff --git a/Minecraft.World/DelayedRelease.h b/Minecraft.World/DelayedRelease.h new file mode 100644 index 00000000..aaccc843 --- /dev/null +++ b/Minecraft.World/DelayedRelease.h @@ -0,0 +1,30 @@ +#pragma once + +#include "Entity.h" + +class DamageSource; + +class DelayedRelease : public Entity +{ +public: + virtual eINSTANCEOF GetType() { return eTYPE_DELAYEDRELEASE; } + +private: + shared_ptr toRelease; + int delay; + +public: + DelayedRelease(Level *level, shared_ptr toRelease, int delay); + +protected: + virtual bool makeStepSound(); + +public: + virtual void tick(); + virtual bool hurt(DamageSource *source, int damage); + +protected: + virtual void defineSynchedData(); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual void addAdditonalSaveData(CompoundTag *tag); +}; \ No newline at end of file diff --git a/Minecraft.World/DerivedLevelData.cpp b/Minecraft.World/DerivedLevelData.cpp new file mode 100644 index 00000000..71c4d87b --- /dev/null +++ b/Minecraft.World/DerivedLevelData.cpp @@ -0,0 +1,209 @@ +#include "stdafx.h" + +#include "DerivedLevelData.h" + +DerivedLevelData::DerivedLevelData(LevelData *wrapped) +{ + this->wrapped = wrapped; +} + +void DerivedLevelData::setTagData(CompoundTag *tag) +{ + wrapped->setTagData(tag); +} + +CompoundTag *DerivedLevelData::createTag() +{ + return wrapped->createTag(); +} + +CompoundTag *DerivedLevelData::createTag(vector > *players) +{ + return wrapped->createTag(players); +} + +__int64 DerivedLevelData::getSeed() +{ + return wrapped->getSeed(); +} + +int DerivedLevelData::getXSpawn() +{ + return wrapped->getXSpawn(); +} + + +int DerivedLevelData::getYSpawn() +{ + return wrapped->getYSpawn(); +} + +int DerivedLevelData::getZSpawn() +{ + return wrapped->getZSpawn(); +} + +__int64 DerivedLevelData::getTime() +{ + return wrapped->getTime(); +} + +__int64 DerivedLevelData::getSizeOnDisk() +{ + return wrapped->getSizeOnDisk(); +} + +CompoundTag *DerivedLevelData::getLoadedPlayerTag() +{ + return wrapped->getLoadedPlayerTag(); +} + +wstring DerivedLevelData::getLevelName() +{ + return wrapped->getLevelName(); +} + +int DerivedLevelData::getVersion() +{ + return wrapped->getVersion(); +} + +__int64 DerivedLevelData::getLastPlayed() +{ + return wrapped->getLastPlayed(); +} + +bool DerivedLevelData::isThundering() +{ + return wrapped->isThundering(); +} + +int DerivedLevelData::getThunderTime() +{ + return wrapped->getThunderTime(); +} + +bool DerivedLevelData::isRaining() +{ + return wrapped->isRaining(); +} + +int DerivedLevelData::getRainTime() +{ + return wrapped->getRainTime(); +} + +GameType *DerivedLevelData::getGameType() +{ + return wrapped->getGameType(); +} + +void DerivedLevelData::setSeed(__int64 seed) +{ +} + +void DerivedLevelData::setXSpawn(int xSpawn) +{ +} + +void DerivedLevelData::setYSpawn(int ySpawn) +{ +} + +void DerivedLevelData::setZSpawn(int zSpawn) +{ +} + +void DerivedLevelData::setTime(__int64 time) +{ +} + +void DerivedLevelData::setSizeOnDisk(__int64 sizeOnDisk) +{ +} + +void DerivedLevelData::setLoadedPlayerTag(CompoundTag *loadedPlayerTag) +{ +} + +void DerivedLevelData::setDimension(int dimension) +{ +} + +void DerivedLevelData::setSpawn(int xSpawn, int ySpawn, int zSpawn) +{ +} + +void DerivedLevelData::setLevelName(const wstring &levelName) +{ +} + +void DerivedLevelData::setVersion(int version) +{ +} + +void DerivedLevelData::setThundering(bool thundering) +{ +} + +void DerivedLevelData::setThunderTime(int thunderTime) +{ +} + +void DerivedLevelData::setRaining(bool raining) +{ +} + +void DerivedLevelData::setRainTime(int rainTime) +{ +} + +bool DerivedLevelData::isGenerateMapFeatures() +{ + return wrapped->isGenerateMapFeatures(); +} + +void DerivedLevelData::setGameType(GameType *gameType) { +} + +bool DerivedLevelData::isHardcore() +{ + return wrapped->isHardcore(); +} + +LevelType *DerivedLevelData::getGenerator() +{ + return wrapped->getGenerator(); +} + +void DerivedLevelData::setGenerator(LevelType *generator) +{ +} + +bool DerivedLevelData::getAllowCommands() +{ + return wrapped->getAllowCommands(); +} + +void DerivedLevelData::setAllowCommands(bool allowCommands) +{ +} + +bool DerivedLevelData::isInitialized() +{ + return wrapped->isInitialized(); +} + +void DerivedLevelData::setInitialized(bool initialized) +{ +} + +int DerivedLevelData::getXZSize() +{ + return wrapped->getXZSize(); +} + +int DerivedLevelData::getHellScale() +{ + return wrapped->getHellScale(); +} diff --git a/Minecraft.World/DerivedLevelData.h b/Minecraft.World/DerivedLevelData.h new file mode 100644 index 00000000..a39f7d04 --- /dev/null +++ b/Minecraft.World/DerivedLevelData.h @@ -0,0 +1,60 @@ +#pragma once + +#include "LevelData.h" + +class DerivedLevelData : public LevelData +{ +private: + LevelData *wrapped; + +public: + DerivedLevelData(LevelData *wrapped); + +protected: + virtual void setTagData(CompoundTag *tag); // 4J Added + +public: + CompoundTag *createTag(); + CompoundTag *createTag(vector > *players); + __int64 getSeed(); + int getXSpawn(); + int getYSpawn(); + int getZSpawn(); + __int64 getTime(); + __int64 getSizeOnDisk(); + CompoundTag *getLoadedPlayerTag(); + wstring getLevelName(); + int getVersion(); + __int64 getLastPlayed(); + bool isThundering(); + int getThunderTime(); + bool isRaining(); + int getRainTime(); + GameType *getGameType(); + void setSeed(__int64 seed); + void setXSpawn(int xSpawn); + void setYSpawn(int ySpawn); + void setZSpawn(int zSpawn); + void setTime(__int64 time); + void setSizeOnDisk(__int64 sizeOnDisk); + void setLoadedPlayerTag(CompoundTag *loadedPlayerTag); + void setDimension(int dimension); + void setSpawn(int xSpawn, int ySpawn, int zSpawn); + void setLevelName(const wstring &levelName); + void setVersion(int version); + void setThundering(bool thundering); + void setThunderTime(int thunderTime); + void setRaining(bool raining); + void setRainTime(int rainTime); + bool isGenerateMapFeatures(); + void setGameType(GameType *gameType); + bool isHardcore(); + LevelType *getGenerator(); + void setGenerator(LevelType *generator); + bool getAllowCommands(); + void setAllowCommands(bool allowCommands); + bool isInitialized(); + void setInitialized(bool initialized); + int getXZSize(); // 4J Added + int getHellScale(); // 4J Addded +}; diff --git a/Minecraft.World/DescFormatter.h b/Minecraft.World/DescFormatter.h new file mode 100644 index 00000000..5682f910 --- /dev/null +++ b/Minecraft.World/DescFormatter.h @@ -0,0 +1,8 @@ +#pragma once +using namespace std; + +class DescFormatter +{ +public: + virtual wstring format(const wstring& i18nValue); +}; \ No newline at end of file diff --git a/Minecraft.World/DesertBiome.cpp b/Minecraft.World/DesertBiome.cpp new file mode 100644 index 00000000..6928861e --- /dev/null +++ b/Minecraft.World/DesertBiome.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.biome.h" + +DesertBiome::DesertBiome(int id) : Biome(id) +{ + // remove default mob spawn settings + friendlies.clear(); + friendlies_chicken.clear(); // 4J added + friendlies_wolf.clear(); // 4J added + this->topMaterial = (BYTE) Tile::sand_Id; + this->material = (BYTE) Tile::sand_Id; + + decorator->treeCount = -999; + decorator->deadBushCount = 2; + decorator->reedsCount = 50; + decorator->cactusCount = 10; +} + +void DesertBiome::decorate(Level *level, Random *random, int xo, int zo) +{ + Biome::decorate(level, random, xo, zo); + + if (random->nextInt(1000) == 0) + { + int x = xo + random->nextInt(16) + 8; + int z = zo + random->nextInt(16) + 8; + Feature *well = new DesertWellFeature(); + well->place(level, random, x, level->getHeightmap(x, z) + 1, z); + } +} \ No newline at end of file diff --git a/Minecraft.World/DesertBiome.h b/Minecraft.World/DesertBiome.h new file mode 100644 index 00000000..d8fc5fb8 --- /dev/null +++ b/Minecraft.World/DesertBiome.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Biome.h" + +class DesertBiome : public Biome +{ +public: + DesertBiome(int id); + virtual void decorate(Level *level, Random *random, int xo, int zo); +}; \ No newline at end of file diff --git a/Minecraft.World/DesertWellFeature.cpp b/Minecraft.World/DesertWellFeature.cpp new file mode 100644 index 00000000..c89006bf --- /dev/null +++ b/Minecraft.World/DesertWellFeature.cpp @@ -0,0 +1,90 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "DesertWellFeature.h" + +bool DesertWellFeature::place(Level *level, Random *random, int x, int y, int z) +{ + while (level->isEmptyTile(x, y, z) && y > 2) + y--; + + int tile = level->getTile(x, y, z); + if (tile != Tile::sand_Id) + { + return false; + } + + // the surrounding 5x5 area may not be lower than y-1 + for (int ox = -2; ox <= 2; ox++) + { + for (int oz = -2; oz <= 2; oz++) + { + if (level->isEmptyTile(x + ox, y - 1, z + oz) && level->isEmptyTile(x + ox, y - 2, z + oz)) + { + return false; + } + } + } + + // place floor + for (int oy = -1; oy <= 0; oy++) + { + for (int ox = -2; ox <= 2; ox++) + { + for (int oz = -2; oz <= 2; oz++) + { + level->setTileNoUpdate(x + ox, y + oy, z + oz, Tile::sandStone_Id); + } + } + } + + // place water cross + level->setTileNoUpdate(x, y, z, Tile::water_Id); + level->setTileNoUpdate(x - 1, y, z, Tile::water_Id); + level->setTileNoUpdate(x + 1, y, z, Tile::water_Id); + level->setTileNoUpdate(x, y, z - 1, Tile::water_Id); + level->setTileNoUpdate(x, y, z + 1, Tile::water_Id); + + // place "fence" + for (int ox = -2; ox <= 2; ox++) + { + for (int oz = -2; oz <= 2; oz++) + { + if (ox == -2 || ox == 2 || oz == -2 || oz == 2) + { + level->setTileNoUpdate(x + ox, y + 1, z + oz, Tile::sandStone_Id); + } + } + } + level->setTileAndDataNoUpdate(x + 2, y + 1, z, Tile::stoneSlabHalf_Id, StoneSlabTile::SAND_SLAB); + level->setTileAndDataNoUpdate(x - 2, y + 1, z, Tile::stoneSlabHalf_Id, StoneSlabTile::SAND_SLAB); + level->setTileAndDataNoUpdate(x, y + 1, z + 2, Tile::stoneSlabHalf_Id, StoneSlabTile::SAND_SLAB); + level->setTileAndDataNoUpdate(x, y + 1, z - 2, Tile::stoneSlabHalf_Id, StoneSlabTile::SAND_SLAB); + + // place roof + for (int ox = -1; ox <= 1; ox++) + { + for (int oz = -1; oz <= 1; oz++) + { + if (ox == 0 && oz == 0) + { + level->setTileNoUpdate(x + ox, y + 4, z + oz, Tile::sandStone_Id); + } + else + { + level->setTileAndDataNoUpdate(x + ox, y + 4, z + oz, Tile::stoneSlabHalf_Id, StoneSlabTile::SAND_SLAB); + } + } + } + + // place pillars + for (int oy = 1; oy <= 3; oy++) + { + level->setTileNoUpdate(x - 1, y + oy, z - 1, Tile::sandStone_Id); + level->setTileNoUpdate(x - 1, y + oy, z + 1, Tile::sandStone_Id); + level->setTileNoUpdate(x + 1, y + oy, z - 1, Tile::sandStone_Id); + level->setTileNoUpdate(x + 1, y + oy, z + 1, Tile::sandStone_Id); + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/DesertWellFeature.h b/Minecraft.World/DesertWellFeature.h new file mode 100644 index 00000000..380a35e3 --- /dev/null +++ b/Minecraft.World/DesertWellFeature.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Feature.h" + +class DesertWellFeature : public Feature +{ +public: + bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/DetectorRailTile.cpp b/Minecraft.World/DetectorRailTile.cpp new file mode 100644 index 00000000..e01ec259 --- /dev/null +++ b/Minecraft.World/DetectorRailTile.cpp @@ -0,0 +1,115 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.h" +#include "DetectorRailTile.h" +#include "net.minecraft.h" + +DetectorRailTile::DetectorRailTile(int id) : RailTile(id, true) +{ + setTicking(true); + icons = NULL; +} + +int DetectorRailTile::getTickDelay() +{ + return 20; +} + +bool DetectorRailTile::isSignalSource() +{ + return true; +} + +void DetectorRailTile::entityInside(Level *level, int x, int y, int z, shared_ptr entity) +{ + if (level->isClientSide) + { + return; + } + + int data = level->getData(x, y, z); + if ((data & RAIL_DATA_BIT) != 0) + { + return; + } + + checkPressed(level, x, y, z, data); +} + +void DetectorRailTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (level->isClientSide) return; + + int data = level->getData(x, y, z); + if ((data & RAIL_DATA_BIT) == 0) + { + return; + } + + checkPressed(level, x, y, z, data); +} + +bool DetectorRailTile::getSignal(LevelSource *level, int x, int y, int z, int dir) +{ + return (level->getData(x, y, z) & RAIL_DATA_BIT) != 0; +} + +bool DetectorRailTile::getDirectSignal(Level *level, int x, int y, int z, int facing) +{ + if ((level->getData(x, y, z) & RAIL_DATA_BIT) == 0) return false; + return (facing == Facing::UP); +} + +void DetectorRailTile::checkPressed(Level *level, int x, int y, int z, int currentData) +{ + bool wasPressed = (currentData & RAIL_DATA_BIT) != 0; + bool shouldBePressed = false; + + float b = 2 / 16.0f; + vector > *entities = level->getEntitiesOfClass(typeid(Minecart), AABB::newTemp(x + b, y, z + b, x + 1 - b, y + 1 - b, z + 1 - b)); + if (!entities->empty()) + { + shouldBePressed = true; + } + + if (shouldBePressed && !wasPressed) + { + level->setData(x, y, z, currentData | RAIL_DATA_BIT); + level->updateNeighborsAt(x, y, z, id); + level->updateNeighborsAt(x, y - 1, z, id); + level->setTilesDirty(x, y, z, x, y, z); + } + if (!shouldBePressed && wasPressed) + { + level->setData(x, y, z, currentData & RAIL_DIRECTION_MASK); + level->updateNeighborsAt(x, y, z, id); + level->updateNeighborsAt(x, y - 1, z, id); + level->setTilesDirty(x, y, z, x, y, z); + } + + if (shouldBePressed) + { + level->addToTickNextTick(x, y, z, id, getTickDelay()); + } + + delete entities; +} + +void DetectorRailTile::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[2]; + icons[0] = iconRegister->registerIcon(L"detectorRail"); + icons[1] = iconRegister->registerIcon(L"detectorRail_on"); +} + +Icon *DetectorRailTile::getTexture(int face, int data) +{ + if ((data & RAIL_DATA_BIT) != 0) + { + return icons[1]; + } + return icons[0]; +} \ No newline at end of file diff --git a/Minecraft.World/DetectorRailTile.h b/Minecraft.World/DetectorRailTile.h new file mode 100644 index 00000000..dd0e6374 --- /dev/null +++ b/Minecraft.World/DetectorRailTile.h @@ -0,0 +1,31 @@ +#pragma once +#include "RailTile.h" + +class Entity; +class Random; +class Level; +class ChunkRebuildData; + +class DetectorRailTile : public RailTile +{ + friend class ChunkRebuildData; +private: + Icon **icons; + +public: + DetectorRailTile(int id); + virtual int getTickDelay(); + virtual bool isSignalSource(); + virtual void entityInside(Level *level, int x, int y, int z, shared_ptr entity); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual bool getSignal(LevelSource *level, int x, int y, int z, int dir); + virtual bool getDirectSignal(Level *level, int x, int y, int z, int facing); +private: + virtual void checkPressed(Level *level, int x, int y, int z, int currentData); +public: + //@Override + void registerIcons(IconRegister *iconRegister); + + //@Override + Icon *getTexture(int face, int data); +}; diff --git a/Minecraft.World/Difficulty.h b/Minecraft.World/Difficulty.h new file mode 100644 index 00000000..d238ef15 --- /dev/null +++ b/Minecraft.World/Difficulty.h @@ -0,0 +1,10 @@ +#pragma once + +class Difficulty +{ +public: + static const int PEACEFUL = 0; + static const int EASY = 1; + static const int NORMAL = 2; + static const int HARD = 3; +}; \ No newline at end of file diff --git a/Minecraft.World/DigDurabilityEnchantment.cpp b/Minecraft.World/DigDurabilityEnchantment.cpp new file mode 100644 index 00000000..f2ef1656 --- /dev/null +++ b/Minecraft.World/DigDurabilityEnchantment.cpp @@ -0,0 +1,36 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "DigDurabilityEnchantment.h" + +DigDurabilityEnchantment::DigDurabilityEnchantment(int id, int frequency) : Enchantment(id, frequency, EnchantmentCategory::digger) +{ + setDescriptionId(IDS_ENCHANTMENT_DURABILITY); +} + +int DigDurabilityEnchantment::getMinCost(int level) +{ + return 5 + (level - 1) * 8; +} + +int DigDurabilityEnchantment::getMaxCost(int level) +{ + return Enchantment::getMinCost(level) + 50; +} + +int DigDurabilityEnchantment::getMaxLevel() +{ + return 3; +} + +bool DigDurabilityEnchantment::canEnchant(shared_ptr item) +{ + if (item->isDamageableItem()) return true; + return Enchantment::canEnchant(item); +} + +bool DigDurabilityEnchantment::shouldIgnoreDurabilityDrop(shared_ptr item, int level, Random *random) +{ + ArmorItem *armor = dynamic_cast(item->getItem()); + if (armor && random->nextFloat() < 0.6f) return false; + return random->nextInt(level + 1) > 0; +} \ No newline at end of file diff --git a/Minecraft.World/DigDurabilityEnchantment.h b/Minecraft.World/DigDurabilityEnchantment.h new file mode 100644 index 00000000..88f6447d --- /dev/null +++ b/Minecraft.World/DigDurabilityEnchantment.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Enchantment.h" + +class DigDurabilityEnchantment : public Enchantment +{ +public: + DigDurabilityEnchantment(int id, int frequency); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); + virtual bool canEnchant(shared_ptr item); + static bool shouldIgnoreDurabilityDrop(shared_ptr item, int level, Random *random); +}; \ No newline at end of file diff --git a/Minecraft.World/DiggerItem.cpp b/Minecraft.World/DiggerItem.cpp new file mode 100644 index 00000000..144b1a11 --- /dev/null +++ b/Minecraft.World/DiggerItem.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.h" +#include "DiggerItem.h" + +DiggerItem::DiggerItem(int id, int attackDamage, const Tier *tier, TileArray *tiles) : Item( id ), tier( tier ) +{ + //this->tier = tier; + this->tiles = tiles; + maxStackSize = 1; + setMaxDamage(tier->getUses()); + this->speed = tier->getSpeed(); + this->attackDamage = attackDamage + tier->getAttackDamageBonus(); +} + +float DiggerItem::getDestroySpeed(shared_ptr itemInstance, Tile *tile) +{ + for (unsigned int i = 0; i < tiles->length; i++) + if ( (*tiles)[i] == tile) return speed; + return 1; +} + +bool DiggerItem::hurtEnemy(shared_ptr itemInstance, shared_ptr mob, shared_ptr attacker) +{ + itemInstance->hurt(2, attacker); + return true; +} + +bool DiggerItem::mineBlock(shared_ptr itemInstance, Level *level, int tile, int x, int y, int z, shared_ptr owner) +{ + // Don't damage tools if the tile can be destroyed in one hit. + if (Tile::tiles[tile]->getDestroySpeed(level, x, y, z) != 0.0) itemInstance->hurt(1, owner); + return true; +} + +int DiggerItem::getAttackDamage(shared_ptr entity) +{ + return attackDamage; +} + +bool DiggerItem::isHandEquipped() +{ + return true; +} + +int DiggerItem::getEnchantmentValue() +{ + return tier->getEnchantmentValue(); +} + +const Item::Tier *DiggerItem::getTier() +{ + return tier; +} + +bool DiggerItem::isValidRepairItem(shared_ptr source, shared_ptr repairItem) +{ + if (tier->getTierItemId() == repairItem->id) + { + return true; + } + return Item::isValidRepairItem(source, repairItem); +} \ No newline at end of file diff --git a/Minecraft.World/DiggerItem.h b/Minecraft.World/DiggerItem.h new file mode 100644 index 00000000..4a4eeb07 --- /dev/null +++ b/Minecraft.World/DiggerItem.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Item.h" + +class Mob; + +class DiggerItem : public Item +{ +private: + TileArray *tiles; +protected: + float speed; +private: + int attackDamage; + +protected: + const Tier *tier; + + DiggerItem(int id, int attackDamage, const Tier *tier, TileArray *tiles); + +public: + virtual float getDestroySpeed(shared_ptr itemInstance, Tile *tile); + virtual bool hurtEnemy(shared_ptr itemInstance, shared_ptr mob, shared_ptr attacker); + virtual bool mineBlock(shared_ptr itemInstance, Level *level, int tile, int x, int y, int z, shared_ptr owner); + virtual int getAttackDamage(shared_ptr entity); + virtual bool isHandEquipped(); + virtual int getEnchantmentValue(); + + const Tier *getTier(); + bool isValidRepairItem(shared_ptr source, shared_ptr repairItem); +}; \ No newline at end of file diff --git a/Minecraft.World/DiggingEnchantment.cpp b/Minecraft.World/DiggingEnchantment.cpp new file mode 100644 index 00000000..b3775623 --- /dev/null +++ b/Minecraft.World/DiggingEnchantment.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "DiggingEnchantment.h" + +DiggingEnchantment::DiggingEnchantment(int id, int frequency) : Enchantment(id, frequency, EnchantmentCategory::digger) +{ + setDescriptionId(IDS_ENCHANTMENT_DIGGING); +} + +int DiggingEnchantment::getMinCost(int level) +{ + return 1 + 10 * (level - 1); +} + +int DiggingEnchantment::getMaxCost(int level) +{ + return Enchantment::getMinCost(level) + 50; +} + +int DiggingEnchantment::getMaxLevel() +{ + return 5; +} + +bool DiggingEnchantment::canEnchant(shared_ptr item) +{ + if (item->getItem()->id == Item::shears_Id) return true; + return Enchantment::canEnchant(item); +} \ No newline at end of file diff --git a/Minecraft.World/DiggingEnchantment.h b/Minecraft.World/DiggingEnchantment.h new file mode 100644 index 00000000..324f5cdd --- /dev/null +++ b/Minecraft.World/DiggingEnchantment.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Enchantment.h" + +class DiggingEnchantment : public Enchantment +{ +public: + DiggingEnchantment(int id, int frequency); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); + virtual bool canEnchant(shared_ptr item); +}; \ No newline at end of file diff --git a/Minecraft.World/Dimension.cpp b/Minecraft.World/Dimension.cpp new file mode 100644 index 00000000..35e66698 --- /dev/null +++ b/Minecraft.World/Dimension.cpp @@ -0,0 +1,243 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.levelgen.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.storage.h" +#include "dimension.h" +#include "BiomeSource.h" +#include "FixedBiomeSource.h" +#include "OldChunkStorage.h" +#include "HellDimension.h" +#include "NormalDimension.h" +#include "TheEndDimension.h" +#include "net.minecraft.world.level.tile.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\Common\Colours\ColourTable.h" + +void Dimension::init(Level *level) +{ + this->level = level; + this->levelType = level->getLevelData()->getGenerator(); + init(); + updateLightRamp(); +} + +void Dimension::updateLightRamp() +{ + float ambientLight = 0.00f; + for (int i = 0; i <= Level::MAX_BRIGHTNESS; i++) + { + float v = (1 - i / (float) (Level::MAX_BRIGHTNESS)); + brightnessRamp[i] = ((1 - v) / (v * 3 + 1)) * (1 - ambientLight) + ambientLight; + } +} + +void Dimension::init() +{ +#ifdef _OVERRIDE_HEIGHTMAP + // 4J Stu - Added to enable overriding the heightmap from a loaded in data file + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<getLevelData()->getGenerator() == LevelType::lvl_flat) + { + biomeSource = new FixedBiomeSource(Biome::plains, 0.5f, 0.5f); + } + else + { + biomeSource = new BiomeSource(level); + } +} + +Dimension::Dimension() +{ + ultraWarm = false; + hasCeiling = false; + brightnessRamp = new float[Level::MAX_BRIGHTNESS + 1]; + id = 0; +} + +Dimension::~Dimension() +{ + delete[] brightnessRamp; + + if(biomeSource != NULL) + delete biomeSource; +} + +ChunkSource *Dimension::createRandomLevelSource() const +{ +#ifdef _OVERRIDE_HEIGHTMAP + // 4J Stu - Added to enable overriding the heightmap from a loaded in data file + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<getSeed(), level->getLevelData()->isGenerateMapFeatures()); + } + else +#endif + if (levelType == LevelType::lvl_flat) + { + return new FlatLevelSource(level, level->getSeed(), level->getLevelData()->isGenerateMapFeatures()); + } + else + { + return new RandomLevelSource(level, level->getSeed(), level->getLevelData()->isGenerateMapFeatures()); + } +} + +ChunkSource *Dimension::createFlatLevelSource() const +{ + return new FlatLevelSource(level, level->getSeed(), level->getLevelData()->isGenerateMapFeatures()); +} + +ChunkStorage *Dimension::createStorage(File dir) +{ + return new OldChunkStorage(dir, true); +} + +bool Dimension::isValidSpawn(int x, int z) const +{ + int topTile = level->getTopTile(x, z); + + if (topTile != Tile::grass_Id) return false; + + return true; +} + +float Dimension::getTimeOfDay(__int64 time, float a) const +{ + int dayStep = (int) (time % Level::TICKS_PER_DAY); + float td = (dayStep + a) / Level::TICKS_PER_DAY - 0.25f; + if (td < 0) td += 1; + if (td > 1) td -= 1; + float tdo = td; + td = 1 - (float) ((cos(td * PI) + 1) / 2); + td = tdo + (td - tdo) / 3.0f; + return td; +} + +int Dimension::getMoonPhase(__int64 time, float a) const +{ + return ((int) (time / Level::TICKS_PER_DAY)) % 8; +} + +bool Dimension::isNaturalDimension() +{ + return true; +} + +float *Dimension::getSunriseColor(float td, float a) +{ + unsigned int clr1 = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Sky_Dawn_Dark ); // 0xB23333 + double r1 = ( (clr1>>16)&0xFF )/255.0f, g1 = ( (clr1>>8)&0xFF )/255.0, b1 = ( clr1&0xFF )/255.0; + + unsigned int clr2 = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Sky_Dawn_Bright ); // 0xFFE533 + double r2 = ( (clr2>>16)&0xFF )/255.0f, g2 = ( (clr2>>8)&0xFF )/255.0, b2 = ( clr2&0xFF )/255.0; + + float span = 0.4f; + float tt = Mth::cos(td * PI * 2) - 0.0f; + float mid = -0.0f; + if (tt >= mid - span && tt <= mid + span) + { + float aa = ((tt - mid) / span) * 0.5f + 0.5f; + float mix = 1 - (((1 - sin(aa * PI))) * 0.99f); + mix = mix * mix; + //sunriseCol[0] = (aa * 0.3f + 0.7f); + //sunriseCol[1] = (aa * aa * 0.7f + 0.2f); + //sunriseCol[2] = (aa * aa * 0.0f + 0.2f); + sunriseCol[0] = (aa * (r2-r1) + r1); + sunriseCol[1] = (aa * (g2-g1) + g1); + sunriseCol[2] = (aa * (b2-b1) + b1); + sunriseCol[3] = mix; + return sunriseCol; + } + + return NULL; +} + +Vec3 *Dimension::getFogColor(float td, float a) const +{ + float br = Mth::cos(td * PI * 2) * 2 + 0.5f; + if (br < 0.0f) br = 0.0f; + if (br > 1.0f) br = 1.0f; + + unsigned int baseFogColour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Default_Fog_Colour ); + float r = ((baseFogColour >> 16) & 0xff) / 255.0f; + float g = ((baseFogColour >> 8) & 0xff) / 255.0f; + float b = ((baseFogColour) & 0xff) / 255.0f; + r *= br * 0.94f + 0.06f; + g *= br * 0.94f + 0.06f; + b *= br * 0.91f + 0.09f; + + return Vec3::newTemp(r, g, b); +} + +bool Dimension::mayRespawn() const +{ + return true; +} + +Dimension *Dimension::getNew(int id) +{ + if (id == -1) return new HellDimension(); + if (id == 0) return new NormalDimension(); + if (id == 1) return new TheEndDimension(); + + return NULL; +} + +float Dimension::getCloudHeight() +{ + return (float)Level::genDepth; +} + +bool Dimension::hasGround() +{ + return true; +} + +Pos *Dimension::getSpawnPos() +{ + return NULL; +} + +int Dimension::getSpawnYPosition() +{ + if (levelType == LevelType::lvl_flat) + { + return 4; + } + return Level::genDepth / 2; +} + +bool Dimension::hasBedrockFog() +{ + // 4J-PB - turn off bedrock fog if the host player doesn't want it + if(app.GetGameHostOption(eGameHostOption_BedrockFog)==0) + { + return false; + } + + return (levelType != LevelType::lvl_flat && !hasCeiling); +} + +double Dimension::getClearColorScale() +{ + if (levelType == LevelType::lvl_flat) + { + return 1.0; + } + return 1.0 / 32.0; +} + +bool Dimension::isFoggyAt(int x, int z) +{ + return false; +} + +int Dimension::getXZSize() +{ + return level->getLevelData()->getXZSize(); +} diff --git a/Minecraft.World/Dimension.h b/Minecraft.World/Dimension.h new file mode 100644 index 00000000..36df385e --- /dev/null +++ b/Minecraft.World/Dimension.h @@ -0,0 +1,62 @@ +#pragma once +class BiomeSource; +class ChunkSource; +class ChunkStorage; +class Level; +class LevelType; + +#include "Material.h" +#include "Vec3.h" +#include "Pos.h" + +class Dimension +{ +public: + Level *level; + LevelType *levelType; + BiomeSource *biomeSource; + bool ultraWarm ; + bool hasCeiling; + float *brightnessRamp; + int id; + + virtual void init(Level *level); + +protected: + virtual void updateLightRamp(); + virtual void init(); + +public: + Dimension(); + ~Dimension(); + virtual ChunkSource *createRandomLevelSource() const; + virtual ChunkSource *createFlatLevelSource() const; + virtual ChunkStorage *createStorage(File dir); + + virtual bool isValidSpawn(int x, int z) const; + + virtual float getTimeOfDay(__int64 time, float a) const; + virtual int getMoonPhase(__int64 time, float a) const; + virtual bool isNaturalDimension(); +private: + static const int fogColor = 0xc0d8ff; + + float sunriseCol[4]; + +public: + virtual float *getSunriseColor(float td, float a); + virtual Vec3 *getFogColor(float td, float a) const; + virtual bool mayRespawn() const; + static Dimension *getNew(int id); + virtual float getCloudHeight(); + virtual bool hasGround(); + virtual Pos *getSpawnPos(); + + int getSpawnYPosition(); + virtual bool hasBedrockFog(); + double getClearColorScale(); + virtual bool isFoggyAt(int x, int z); + + // 4J Added + virtual int getXZSize(); +}; diff --git a/Minecraft.World/DiodeTile.cpp b/Minecraft.World/DiodeTile.cpp new file mode 100644 index 00000000..c28778d3 --- /dev/null +++ b/Minecraft.World/DiodeTile.cpp @@ -0,0 +1,299 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.h" +#include "DiodeTile.h" + +const double DiodeTile::DELAY_RENDER_OFFSETS[4] = { -1.0f / 16.0f, 1.0f / 16.0f, 3.0f / 16.0f, 5.0f / 16.0f }; +const int DiodeTile::DELAYS[4] = { 1, 2, 3, 4 }; + +DiodeTile::DiodeTile(int id, bool on) : DirectionalTile(id, Material::decoration,isSolidRender()) +{ + this->on = on; + updateDefaultShape(); +} + +// 4J Added override +void DiodeTile::updateDefaultShape() +{ + setShape(0, 0, 0, 1, 2.0f / 16.0f, 1); +} + +bool DiodeTile::isCubeShaped() +{ + return false; +} + +bool DiodeTile::mayPlace(Level *level, int x, int y, int z) +{ + if (!level->isTopSolidBlocking(x, y - 1, z)) + { + return false; + } + return Tile::mayPlace(level, x, y, z); +} + +bool DiodeTile::canSurvive(Level *level, int x, int y, int z) +{ + if (!level->isTopSolidBlocking(x, y - 1, z)) + { + return false; + } + return Tile::canSurvive(level, x, y, z); +} + +void DiodeTile::tick(Level *level, int x, int y, int z, Random *random) +{ + int data = level->getData(x, y, z); + bool sourceOn = getSourceSignal(level, x, y, z, data); + if (on && !sourceOn) + { + level->setTileAndData(x, y, z, Tile::diode_off_Id, data); + } + else if (!on) + { + // when off-diodes are ticked, they always turn on for one tick and + // then off again if necessary + level->setTileAndData(x, y, z, Tile::diode_on_Id, data); + if (!sourceOn) + { + int delay = (data & DELAY_MASK) >> DELAY_SHIFT; + level->addToTickNextTick(x, y, z, Tile::diode_on_Id, DELAYS[delay] * 2); + } + } +} + +Icon *DiodeTile::getTexture(int face, int data) +{ + // down is used by the torch tesselator + if (face == Facing::DOWN) + { + if (on) + { + return Tile::notGate_on->getTexture(face); + } + return Tile::notGate_off->getTexture(face); + } + if (face == Facing::UP) + { + return icon; + } + // edge of stone half-step + return Tile::stoneSlab->getTexture(Facing::UP); +} + +void DiodeTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(on ? L"repeater_lit" : L"repeater"); +} + +bool DiodeTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + if (face == Facing::DOWN || face == Facing::UP) + { + // up and down is a special case handled by the shape renderer + return false; + } + return true; +} + +int DiodeTile::getRenderShape() +{ + return SHAPE_DIODE; +} + +bool DiodeTile::getDirectSignal(Level *level, int x, int y, int z, int dir) +{ + return getSignal(level, x, y, z, dir); +} + +bool DiodeTile::getSignal(LevelSource *level, int x, int y, int z, int facing) +{ + if (!on) + { + return false; + } + + int dir = getDirection(level->getData(x, y, z)); + + if (dir == Direction::SOUTH && facing == Facing::SOUTH) return true; + if (dir == Direction::WEST && facing == Facing::WEST) return true; + if (dir == Direction::NORTH && facing == Facing::NORTH) return true; + if (dir == Direction::EAST && facing == Facing::EAST) return true; + + return false; +} + +void DiodeTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (!canSurvive(level, x, y, z)) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + level->updateNeighborsAt(x + 1, y, z, id); + level->updateNeighborsAt(x - 1, y, z, id); + level->updateNeighborsAt(x, y, z + 1, id); + level->updateNeighborsAt(x, y, z - 1, id); + level->updateNeighborsAt(x, y - 1, z, id); + level->updateNeighborsAt(x, y + 1, z, id); + return; + } + + int data = level->getData(x, y, z); + + bool sourceOn = getSourceSignal(level, x, y, z, data); + int delay = (data & DELAY_MASK) >> DELAY_SHIFT; + if ( (on && !sourceOn) || (!on && sourceOn)) + { + level->addToTickNextTick(x, y, z, id, DELAYS[delay] * 2); + } +} + +bool DiodeTile::getSourceSignal(Level *level, int x, int y, int z, int data) +{ + int dir = getDirection(data); + switch (dir) + { + case Direction::SOUTH: + return level->getSignal(x, y, z + 1, Facing::SOUTH) || (level->getTile(x, y, z + 1) == Tile::redStoneDust_Id && level->getData(x, y, z + 1) > 0); + case Direction::NORTH: + return level->getSignal(x, y, z - 1, Facing::NORTH) || (level->getTile(x, y, z - 1) == Tile::redStoneDust_Id && level->getData(x, y, z - 1) > 0); + case Direction::EAST: + return level->getSignal(x + 1, y, z, Facing::EAST) || (level->getTile(x + 1, y, z) == Tile::redStoneDust_Id && level->getData(x + 1, y, z) > 0); + case Direction::WEST: + return level->getSignal(x - 1, y, z, Facing::WEST) || (level->getTile(x - 1, y, z) == Tile::redStoneDust_Id && level->getData(x - 1, y, z) > 0); + } + return false; +} + +// 4J-PB - Adding a TestUse for tooltip display +bool DiodeTile::TestUse() +{ + return true; +} + +bool DiodeTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if( soundOnly) return false; + + int data = level->getData(x, y, z); + int delay = (data & DELAY_MASK) >> DELAY_SHIFT; + delay = ((delay + 1) << DELAY_SHIFT) & DELAY_MASK; + + level->setData(x, y, z, delay | (data & DIRECTION_MASK)); + return true; +} + +bool DiodeTile::isSignalSource() +{ + return true; +} + +void DiodeTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int dir = (((Mth::floor(by->yRot * 4 / (360) + 0.5)) & 3) + 2) % 4; + level->setData(x, y, z, dir); + + bool sourceOn = getSourceSignal(level, x, y, z, dir); + if (sourceOn) + { + level->addToTickNextTick(x, y, z, id, 1); + } +} + +void DiodeTile::onPlace(Level *level, int x, int y, int z) +{ + level->updateNeighborsAt(x + 1, y, z, id); + level->updateNeighborsAt(x - 1, y, z, id); + level->updateNeighborsAt(x, y, z + 1, id); + level->updateNeighborsAt(x, y, z - 1, id); + level->updateNeighborsAt(x, y - 1, z, id); + level->updateNeighborsAt(x, y + 1, z, id); +} + +void DiodeTile::destroy(Level *level, int x, int y, int z, int data) +{ + if (on) + { + level->updateNeighborsAt(x + 1, y, z, id); + level->updateNeighborsAt(x - 1, y, z, id); + level->updateNeighborsAt(x, y, z + 1, id); + level->updateNeighborsAt(x, y, z - 1, id); + level->updateNeighborsAt(x, y - 1, z, id); + level->updateNeighborsAt(x, y + 1, z, id); + } + Tile::destroy(level, x, y, z, data); +} + +bool DiodeTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +int DiodeTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::diode->id; +} + +void DiodeTile::animateTick(Level *level, int xt, int yt, int zt, Random *random) +{ + if (!on) return; + int data = level->getData(xt, yt, zt); + int dir = getDirection(data); + + double x = xt + 0.5f + (random->nextFloat() - 0.5f) * 0.2; + double y = yt + 0.4f + (random->nextFloat() - 0.5f) * 0.2; + double z = zt + 0.5f + (random->nextFloat() - 0.5f) * 0.2; + + double xo = 0; + double zo = 0; + + if (random->nextInt(2) == 0) + { + // spawn on receiver + switch (dir) + { + case Direction::SOUTH: + zo = -5.0f / 16.0f; + break; + case Direction::NORTH: + zo = 5.0f / 16.0f; + break; + case Direction::EAST: + xo = -5.0f / 16.0f; + break; + case Direction::WEST: + xo = 5.0f / 16.0f; + break; + } + } + else + { + // spawn on transmitter + int delay = (data & DELAY_MASK) >> DELAY_SHIFT; + switch (dir) + { + case Direction::SOUTH: + zo = DiodeTile::DELAY_RENDER_OFFSETS[delay]; + break; + case Direction::NORTH: + zo = -DiodeTile::DELAY_RENDER_OFFSETS[delay]; + break; + case Direction::EAST: + xo = DiodeTile::DELAY_RENDER_OFFSETS[delay]; + break; + case Direction::WEST: + xo = -DiodeTile::DELAY_RENDER_OFFSETS[delay]; + break; + } + } + level->addParticle(eParticleType_reddust, x + xo, y, z + zo, 0, 0, 0); + +} + +int DiodeTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Item::diode_Id; +} diff --git a/Minecraft.World/DiodeTile.h b/Minecraft.World/DiodeTile.h new file mode 100644 index 00000000..003b011f --- /dev/null +++ b/Minecraft.World/DiodeTile.h @@ -0,0 +1,50 @@ +#pragma once +#include "DirectionalTile.h" + +class Player; +class Random; +class Level; + +class DiodeTile : public DirectionalTile +{ + friend class Tile; +public: + static const int DELAY_MASK = DIRECTION_INV_MASK; + static const int DELAY_SHIFT = 2; + + static const double DELAY_RENDER_OFFSETS[4]; + static const int DELAYS[4]; + +private: + bool on; + +protected: + DiodeTile(int id, bool on); +public: + virtual void updateDefaultShape(); // 4J Added override + virtual bool isCubeShaped(); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual bool canSurvive(Level *level, int x, int y, int z); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual Icon *getTexture(int face, int data); + //@Override + void registerIcons(IconRegister *iconRegister); + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + virtual int getRenderShape(); + virtual bool getDirectSignal(Level *level, int x, int y, int z, int dir); + virtual bool getSignal(LevelSource *level, int x, int y, int z, int facing); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); +private: + virtual bool getSourceSignal(Level *level, int x, int y, int z, int data); +public: + virtual bool TestUse(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + virtual bool isSignalSource(); + virtual void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + virtual void onPlace(Level *level, int x, int y, int z); + virtual void destroy(Level *level, int x, int y, int z, int data); + virtual bool isSolidRender(bool isServerLevel = false); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual void animateTick(Level *level, int xt, int yt, int zt, Random *random); + virtual int cloneTileId(Level *level, int x, int y, int z); +}; diff --git a/Minecraft.World/Direction.cpp b/Minecraft.World/Direction.cpp new file mode 100644 index 00000000..408bbab5 --- /dev/null +++ b/Minecraft.World/Direction.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "Direction.h" +#include "Facing.h" + +const int Direction::STEP_X[] = +{ + 0, -1, 0, 1 +}; + +const int Direction::STEP_Z[] = +{ + 1, 0, -1, 0 +}; + +// for [direction] it gives [tile-face] +int Direction::DIRECTION_FACING[4] = +{ + Facing::SOUTH, Facing::WEST, Facing::NORTH, Facing::EAST +}; + +// for [facing] it gives [direction] +int Direction::FACING_DIRECTION[] = +{ + UNDEFINED, UNDEFINED, NORTH, SOUTH, WEST, EAST +}; + +int Direction::DIRECTION_OPPOSITE[4] = +{ + Direction::NORTH, Direction::EAST, Direction::SOUTH, Direction::WEST +}; + +// for [direction] it gives [90 degrees clockwise direction] +int Direction::DIRECTION_CLOCKWISE[] = +{ + Direction::WEST, Direction::NORTH, Direction::EAST, Direction::SOUTH +}; + +// for [direction] it gives [90 degrees counter clockwise direction] +int Direction::DIRECTION_COUNTER_CLOCKWISE[] = +{ + Direction::EAST, Direction::SOUTH, Direction::WEST, Direction::NORTH +}; + +int Direction::RELATIVE_DIRECTION_FACING[4][6] = +{ + // south + { + Facing::UP, Facing::DOWN, Facing::SOUTH, Facing::NORTH, Facing::EAST, Facing::WEST + }, + // west + { + Facing::UP, Facing::DOWN, Facing::EAST, Facing::WEST, Facing::NORTH, Facing::SOUTH + }, + // north + { + Facing::UP, Facing::DOWN, Facing::NORTH, Facing::SOUTH, Facing::WEST, Facing::EAST + }, + // east + { + Facing::UP, Facing::DOWN, Facing::WEST, Facing::EAST, Facing::SOUTH, Facing::NORTH + } +}; diff --git a/Minecraft.World/Direction.h b/Minecraft.World/Direction.h new file mode 100644 index 00000000..aadd2bbb --- /dev/null +++ b/Minecraft.World/Direction.h @@ -0,0 +1,32 @@ +#pragma once + +class Direction +{ +public: + static const int UNDEFINED = -1; + static const int SOUTH = 0; + static const int WEST = 1; + static const int NORTH = 2; + static const int EAST = 3; + + static const int STEP_X[]; + static const int STEP_Z[]; + + // for [direction] it gives [tile-face] + static int DIRECTION_FACING[]; + + // for [facing] it gives [direction] + static int FACING_DIRECTION[]; + + // for [direction] it gives [opposite direction] + static int DIRECTION_OPPOSITE[]; + + // for [direction] it gives [90 degrees clockwise direction] + static int DIRECTION_CLOCKWISE[]; + + // for [direction] it gives [90 degrees counter-clockwise direction] + static int DIRECTION_COUNTER_CLOCKWISE[]; + + // for [direction][world-facing] it gives [tile-facing] + static int RELATIVE_DIRECTION_FACING[4][6]; +}; \ No newline at end of file diff --git a/Minecraft.World/DirectionalTile.cpp b/Minecraft.World/DirectionalTile.cpp new file mode 100644 index 00000000..783e88bd --- /dev/null +++ b/Minecraft.World/DirectionalTile.cpp @@ -0,0 +1,16 @@ +#include "stdafx.h" + +#include "DirectionalTile.h" + +DirectionalTile::DirectionalTile(int id, Material *material) : Tile(id, material) +{ +} + +DirectionalTile::DirectionalTile(int id, Material *material, bool isSolidRender) : Tile(id, material, isSolidRender) +{ +} + +int DirectionalTile::getDirection(int data) +{ + return data & DIRECTION_MASK; +} \ No newline at end of file diff --git a/Minecraft.World/DirectionalTile.h b/Minecraft.World/DirectionalTile.h new file mode 100644 index 00000000..cc4715c6 --- /dev/null +++ b/Minecraft.World/DirectionalTile.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Tile.h" + +class DirectionalTile : public Tile +{ +public: + static const int DIRECTION_MASK = 0x3; + static const int DIRECTION_INV_MASK = 0xC; + +protected: + DirectionalTile(int id, Material *material); + DirectionalTile(int id, Material *material, bool isSolidRender); + +public: + static int getDirection(int data); +}; \ No newline at end of file diff --git a/Minecraft.World/DirectoryLevelStorage.cpp b/Minecraft.World/DirectoryLevelStorage.cpp new file mode 100644 index 00000000..27514c9b --- /dev/null +++ b/Minecraft.World/DirectoryLevelStorage.cpp @@ -0,0 +1,824 @@ +#include "stdafx.h" +#include "System.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.chunk.storage.h" +#include "net.minecraft.world.level.dimension.h" +#include "com.mojang.nbt.h" +#include "File.h" +#include "DataInputStream.h" +#include "FileInputStream.h" +#include "LevelData.h" +#include "DirectoryLevelStorage.h" +#include "ConsoleSaveFileIO.h" + +const wstring DirectoryLevelStorage::sc_szPlayerDir(L"players/"); + +_MapDataMappings::_MapDataMappings() +{ +#ifndef _DURANGO + ZeroMemory(xuids,sizeof(PlayerUID)*MAXIMUM_MAP_SAVE_DATA); +#endif + ZeroMemory(dimensions,sizeof(byte)*(MAXIMUM_MAP_SAVE_DATA/4)); +} + +int _MapDataMappings::getDimension(int id) +{ + int offset = (2*(id%4)); + int val = (dimensions[id>>2] & (3 << offset))>>offset; + + int returnVal=0; + + switch(val) + { + case 0: + returnVal = 0; // Overworld + break; + case 1: + returnVal = -1; // Nether + break; + case 2: + returnVal = 1; // End + break; + default: +#ifndef _CONTENT_PACKAGE + printf("Read invalid dimension from MapDataMapping\n"); + __debugbreak(); +#endif + break; + } + return returnVal; +} + +void _MapDataMappings::setMapping(int id, PlayerUID xuid, int dimension) +{ + xuids[id] = xuid; + + int offset = (2*(id%4)); + + // Reset it first + dimensions[id>>2] &= ~( 2 << offset ); + switch(dimension) + { + case 0: // Overworld + //dimensions[id>>2] &= ~( 2 << offset ); + break; + case -1: // Nether + dimensions[id>>2] |= ( 1 << offset ); + break; + case 1: // End + dimensions[id>>2] |= ( 2 << offset ); + break; + default: +#ifndef _CONTENT_PACKAGE + printf("Trinyg to set a MapDataMapping for an invalid dimension.\n"); + __debugbreak(); +#endif + break; + } +} + +// Old version the only used 1 bit for dimension indexing +_MapDataMappings_old::_MapDataMappings_old() +{ +#ifndef _DURANGO + ZeroMemory(xuids,sizeof(PlayerUID)*MAXIMUM_MAP_SAVE_DATA); +#endif + ZeroMemory(dimensions,sizeof(byte)*(MAXIMUM_MAP_SAVE_DATA/8)); +} + +int _MapDataMappings_old::getDimension(int id) +{ + return dimensions[id>>3] & (128 >> (id%8) ) ? -1 : 0; +} + +void _MapDataMappings_old::setMapping(int id, PlayerUID xuid, int dimension) +{ + xuids[id] = xuid; + if( dimension == 0 ) + { + dimensions[id>>3] &= ~( 128 >> (id%8) ); + } + else + { + dimensions[id>>3] |= ( 128 >> (id%8) ); + } +} + +#ifdef _LARGE_WORLDS +void DirectoryLevelStorage::PlayerMappings::addMapping(int id, int centreX, int centreZ, int dimension, int scale) +{ + __int64 index = ( ((__int64)(centreZ & 0x1FFFFFFF)) << 34) | ( ((__int64)(centreX & 0x1FFFFFFF)) << 5) | ( (scale & 0x7) << 2) | (dimension & 0x3); + m_mappings[index] = id; + //app.DebugPrintf("Adding mapping: %d - (%d,%d)/%d/%d [%I64d - 0x%016llx]\n", id, centreX, centreZ, dimension, scale, index, index); +} + +bool DirectoryLevelStorage::PlayerMappings::getMapping(int &id, int centreX, int centreZ, int dimension, int scale) +{ + //__int64 zMasked = centreZ & 0x1FFFFFFF; + //__int64 xMasked = centreX & 0x1FFFFFFF; + //__int64 zShifted = zMasked << 34; + //__int64 xShifted = xMasked << 5; + //app.DebugPrintf("xShifted = %d (0x%016x), zShifted = %I64d (0x%016llx)\n", xShifted, xShifted, zShifted, zShifted); + __int64 index = ( ((__int64)(centreZ & 0x1FFFFFFF)) << 34) | ( ((__int64)(centreX & 0x1FFFFFFF)) << 5) | ( (scale & 0x7) << 2) | (dimension & 0x3); + AUTO_VAR(it,m_mappings.find(index)); + if(it != m_mappings.end()) + { + id = it->second; + //app.DebugPrintf("Found mapping: %d - (%d,%d)/%d/%d [%I64d - 0x%016llx]\n", id, centreX, centreZ, dimension, scale, index, index); + return true; + } + else + { + //app.DebugPrintf("Failed to find mapping: (%d,%d)/%d/%d [%I64d - 0x%016llx]\n", centreX, centreZ, dimension, scale, index, index); + return false; + } +} + +void DirectoryLevelStorage::PlayerMappings::writeMappings(DataOutputStream *dos) +{ + dos->writeInt(m_mappings.size()); + for(AUTO_VAR(it, m_mappings.begin()); it != m_mappings.end(); ++it) + { + app.DebugPrintf(" -- %lld (0x%016llx) = %d\n", it->first, it->first, it->second); + dos->writeLong(it->first); + dos->writeInt(it->second); + } +} + +void DirectoryLevelStorage::PlayerMappings::readMappings(DataInputStream *dis) +{ + int count = dis->readInt(); + for(unsigned int i = 0; i < count; ++i) + { + __int64 index = dis->readLong(); + int id = dis->readInt(); + m_mappings[index] = id; + app.DebugPrintf(" -- %lld (0x%016llx) = %d\n", index, index, id); + } +} +#endif + +DirectoryLevelStorage::DirectoryLevelStorage(ConsoleSaveFile *saveFile, const File dir, const wstring& levelId, bool createPlayerDir) : sessionId( System::currentTimeMillis() ), + dir( L"" ), playerDir( sc_szPlayerDir ), dataDir( wstring(L"data/") ), levelId(levelId) +{ + m_saveFile = saveFile; + m_bHasLoadedMapDataMappings = false; + +#ifdef _LARGE_WORLDS + m_usedMappings = byteArray(MAXIMUM_MAP_SAVE_DATA/8); +#endif +} + +DirectoryLevelStorage::~DirectoryLevelStorage() +{ + delete m_saveFile; + + for(AUTO_VAR(it,m_cachedSaveData.begin()); it != m_cachedSaveData.end(); ++it) + { + delete it->second; + } + +#ifdef _LARGE_WORLDS + delete m_usedMappings.data; +#endif +} + +void DirectoryLevelStorage::initiateSession() +{ + // 4J Jev, removed try/catch. + + File dataFile = File( dir, wstring(L"session.lock") ); + FileOutputStream fos = FileOutputStream(dataFile); + DataOutputStream dos = DataOutputStream(&fos); + dos.writeLong(sessionId); + dos.close(); + +} + +File DirectoryLevelStorage::getFolder() +{ + return dir; +} + +void DirectoryLevelStorage::checkSession() +{ + // 4J-PB - Not in the Xbox game + + /* + File dataFile = File( dir, wstring(L"session.lock")); + FileInputStream fis = FileInputStream(dataFile); + DataInputStream dis = DataInputStream(&fis); + dis.close(); + */ +} + +ChunkStorage *DirectoryLevelStorage::createChunkStorage(Dimension *dimension) +{ + // 4J Jev, removed try/catch. + + if (dynamic_cast(dimension) != NULL) + { + File dir2 = File(dir, LevelStorage::NETHER_FOLDER); + //dir2.mkdirs(); // 4J Removed + return new OldChunkStorage(dir2, true); + } + if (dynamic_cast(dimension) != NULL) + { + File dir2 = File(dir, LevelStorage::ENDER_FOLDER); + //dir2.mkdirs(); // 4J Removed + return new OldChunkStorage(dir2, true); + } + + return new OldChunkStorage(dir, true); +} + +LevelData *DirectoryLevelStorage::prepareLevel() +{ + // 4J Stu Added +#ifdef _LARGE_WORLDS + ConsoleSavePath mapFile = getDataFile(L"largeMapDataMappings"); +#else + ConsoleSavePath mapFile = getDataFile(L"mapDataMappings"); +#endif + if (!m_bHasLoadedMapDataMappings && !mapFile.getName().empty() && getSaveFile()->doesFileExist( mapFile )) + { + DWORD NumberOfBytesRead; + FileEntry *fileEntry = getSaveFile()->createFile(mapFile); + +#ifdef __PS3__ + // 4J Stu - This version changed happened before initial release + if(getSaveFile()->getSaveVersion() < SAVE_FILE_VERSION_CHANGE_MAP_DATA_MAPPING_SIZE) + { + // Delete the old file + if(fileEntry) getSaveFile()->deleteFile( fileEntry ); + + // Save a new, blank version + saveMapIdLookup(); + } + else +#elif defined(_DURANGO) + // 4J Stu - This version changed happened before initial release + if(getSaveFile()->getSaveVersion() < SAVE_FILE_VERSION_DURANGO_CHANGE_MAP_DATA_MAPPING_SIZE) + { + // Delete the old file + if(fileEntry) getSaveFile()->deleteFile( fileEntry ); + + // Save a new, blank version + saveMapIdLookup(); + } + else +#endif + { + getSaveFile()->setFilePointer(fileEntry,0,NULL, FILE_BEGIN); + +#ifdef _LARGE_WORLDS + byteArray data(fileEntry->getFileSize()); + getSaveFile()->readFile( fileEntry, data.data, fileEntry->getFileSize(), &NumberOfBytesRead); + assert( NumberOfBytesRead == fileEntry->getFileSize() ); + + ByteArrayInputStream bais(data); + DataInputStream dis(&bais); + int count = dis.readInt(); + app.DebugPrintf("Loading %d mappings\n", count); + for(unsigned int i = 0; i < count; ++i) + { + PlayerUID playerUid = dis.readPlayerUID(); +#ifdef _WINDOWS64 + app.DebugPrintf(" -- %d\n", playerUid); +#else + app.DebugPrintf(" -- %ls\n", playerUid.toString().c_str()); +#endif + m_playerMappings[playerUid].readMappings(&dis); + } + dis.readFully(m_usedMappings); +#else + + if(getSaveFile()->getSaveVersion() < END_DIMENSION_MAP_MAPPINGS_SAVE_VERSION) + { + MapDataMappings_old oldMapDataMappings; + getSaveFile()->readFile( fileEntry, + &oldMapDataMappings, // data buffer + sizeof(MapDataMappings_old), // number of bytes to read + &NumberOfBytesRead // number of bytes read + ); + assert( NumberOfBytesRead == sizeof(MapDataMappings_old) ); + + for(unsigned int i = 0; i < MAXIMUM_MAP_SAVE_DATA; ++i) + { + m_saveableMapDataMappings.setMapping(i,oldMapDataMappings.xuids[i],oldMapDataMappings.getDimension(i)); + } + } + else + { + getSaveFile()->readFile( fileEntry, + &m_saveableMapDataMappings, // data buffer + sizeof(MapDataMappings), // number of bytes to read + &NumberOfBytesRead // number of bytes read + ); + assert( NumberOfBytesRead == sizeof(MapDataMappings) ); + } + + memcpy(&m_mapDataMappings,&m_saveableMapDataMappings,sizeof(MapDataMappings)); +#endif + + + // Write out our changes now + if(getSaveFile()->getSaveVersion() < END_DIMENSION_MAP_MAPPINGS_SAVE_VERSION) saveMapIdLookup(); + } + + m_bHasLoadedMapDataMappings = true; + } + + // 4J Jev, removed try/catch + + ConsoleSavePath dataFile = ConsoleSavePath( wstring( L"level.dat" ) ); + + if ( m_saveFile->doesFileExist( dataFile ) ) + { + ConsoleSaveFileInputStream fis = ConsoleSaveFileInputStream(m_saveFile, dataFile); + CompoundTag *root = NbtIo::readCompressed(&fis); + CompoundTag *tag = root->getCompound(L"Data"); + LevelData *ret = new LevelData(tag); + delete root; + return ret; + } + + return NULL; +} + +void DirectoryLevelStorage::saveLevelData(LevelData *levelData, vector > *players) +{ + // 4J Jev, removed try/catch + + CompoundTag *dataTag = levelData->createTag(players); + + CompoundTag *root = new CompoundTag(); + root->put(L"Data", dataTag); + + ConsoleSavePath currentFile = ConsoleSavePath( wstring( L"level.dat" ) ); + + ConsoleSaveFileOutputStream fos = ConsoleSaveFileOutputStream( m_saveFile, currentFile ); + NbtIo::writeCompressed(root, &fos); + + delete root; +} + +void DirectoryLevelStorage::saveLevelData(LevelData *levelData) +{ + // 4J Jev, removed try/catch + + CompoundTag *dataTag = levelData->createTag(); + + CompoundTag *root = new CompoundTag(); + root->put(L"Data", dataTag); + + ConsoleSavePath currentFile = ConsoleSavePath( wstring( L"level.dat" ) ); + + ConsoleSaveFileOutputStream fos = ConsoleSaveFileOutputStream( m_saveFile, currentFile ); + NbtIo::writeCompressed(root, &fos); + + delete root; +} + +void DirectoryLevelStorage::save(shared_ptr player) +{ + // 4J Jev, removed try/catch. + PlayerUID playerXuid = player->getXuid(); +#if defined(__PS3__) || defined(__ORBIS__) + if( playerXuid != INVALID_XUID ) +#else + if( playerXuid != INVALID_XUID && !player->isGuest() ) +#endif + { + CompoundTag *tag = new CompoundTag(); + player->saveWithoutId(tag); +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + ConsoleSavePath realFile = ConsoleSavePath( m_saveFile->getPlayerDataFilenameForSave(playerXuid).c_str() ); +#elif defined(_DURANGO) + ConsoleSavePath realFile = ConsoleSavePath( playerDir.getName() + player->getXuid().toString() + L".dat" ); +#else + ConsoleSavePath realFile = ConsoleSavePath( playerDir.getName() + _toString( player->getXuid() ) + L".dat" ); +#endif + // If saves are disabled (e.g. because we are writing the save buffer to disk) then cache this player data + if(StorageManager.GetSaveDisabled()) + { + ByteArrayOutputStream *bos = new ByteArrayOutputStream(); + NbtIo::writeCompressed(tag,bos); + + AUTO_VAR(it, m_cachedSaveData.find(realFile.getName())); + if(it != m_cachedSaveData.end() ) + { + delete it->second; + } + m_cachedSaveData[realFile.getName()] = bos; + app.DebugPrintf("Cached saving of file %ls due to saves being disabled\n", realFile.getName().c_str() ); + } + else + { + ConsoleSaveFileOutputStream fos = ConsoleSaveFileOutputStream( m_saveFile, realFile ); + NbtIo::writeCompressed(tag, &fos); + } + delete tag; + } + else if( playerXuid != INVALID_XUID ) + { + app.DebugPrintf("Not saving player as their XUID is a guest\n"); + dontSaveMapMappingForPlayer(playerXuid); + } +} + + // 4J Changed return val to bool to check if new player or loaded player +bool DirectoryLevelStorage::load(shared_ptr player) +{ + bool newPlayer = true; + CompoundTag *tag = loadPlayerDataTag( player->getXuid() ); + if (tag != NULL) + { + newPlayer = false; + player->load(tag); + delete tag; + } + return newPlayer; +} + +CompoundTag *DirectoryLevelStorage::loadPlayerDataTag(PlayerUID xuid) +{ + // 4J Jev, removed try/catch. +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + ConsoleSavePath realFile = ConsoleSavePath( m_saveFile->getPlayerDataFilenameForLoad(xuid).c_str() ); +#elif defined(_DURANGO) + ConsoleSavePath realFile = ConsoleSavePath( playerDir.getName() + xuid.toString() + L".dat" ); +#else + ConsoleSavePath realFile = ConsoleSavePath( playerDir.getName() + _toString( xuid ) + L".dat" ); +#endif + AUTO_VAR(it, m_cachedSaveData.find(realFile.getName())); + if(it != m_cachedSaveData.end() ) + { + ByteArrayOutputStream *bos = it->second; + ByteArrayInputStream bis(bos->buf, 0, bos->size()); + CompoundTag *tag = NbtIo::readCompressed(&bis); + bis.reset(); + app.DebugPrintf("Loaded player data from cached file %ls\n", realFile.getName().c_str() ); + return tag; + } + else if ( m_saveFile->doesFileExist( realFile ) ) + { + ConsoleSaveFileInputStream fis = ConsoleSaveFileInputStream(m_saveFile, realFile); + return NbtIo::readCompressed(&fis); + } + return NULL; +} + +// 4J Added function +void DirectoryLevelStorage::clearOldPlayerFiles() +{ + if(StorageManager.GetSaveDisabled() ) return; + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + vector *playerFiles = m_saveFile->getValidPlayerDatFiles(); +#else + vector *playerFiles = m_saveFile->getFilesWithPrefix( playerDir.getName() ); +#endif + + if( playerFiles != NULL ) + { +#ifndef _FINAL_BUILD + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<size(); ++i ) + { + FileEntry *file = playerFiles->at(i); + wstring xuidStr = replaceAll( replaceAll(file->data.filename,playerDir.getName(),L""),L".dat",L""); +#if defined(__PS3__) || defined(__ORBIS__) || defined(_DURANGO) + PlayerUID xuid(xuidStr); +#else + PlayerUID xuid = _fromString(xuidStr); +#endif + deleteMapFilesForPlayer(xuid); + m_saveFile->deleteFile( playerFiles->at(i) ); + } + } + else +#endif + if( playerFiles->size() > MAX_PLAYER_DATA_SAVES ) + { + sort(playerFiles->begin(), playerFiles->end(), FileEntry::newestFirst ); + + for(unsigned int i = MAX_PLAYER_DATA_SAVES; i < playerFiles->size(); ++i ) + { + FileEntry *file = playerFiles->at(i); + wstring xuidStr = replaceAll( replaceAll(file->data.filename,playerDir.getName(),L""),L".dat",L""); +#if defined(__PS3__) || defined(__ORBIS__) || defined(_DURANGO) + PlayerUID xuid(xuidStr); +#else + PlayerUID xuid = _fromString(xuidStr); +#endif + deleteMapFilesForPlayer(xuid); + m_saveFile->deleteFile( playerFiles->at(i) ); + } + } + + delete playerFiles; + } +} + +PlayerIO *DirectoryLevelStorage::getPlayerIO() +{ + return this; +} + +void DirectoryLevelStorage::closeAll() +{ +} + +ConsoleSavePath DirectoryLevelStorage::getDataFile(const wstring& id) +{ + return ConsoleSavePath( dataDir.getName() + id + L".dat" ); +} + +wstring DirectoryLevelStorage::getLevelId() +{ + return levelId; +} + +void DirectoryLevelStorage::flushSaveFile(bool autosave) +{ +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<doesFileExist(gameRulesFiles)) + { + FileEntry *fe = m_saveFile->createFile(gameRulesFiles); + m_saveFile->deleteFile( fe ); + } + } +#endif + m_saveFile->Flush(autosave); +} + +// 4J Added +void DirectoryLevelStorage::resetNetherPlayerPositions() +{ + if(app.GetResetNether()) + { +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + vector *playerFiles = m_saveFile->getValidPlayerDatFiles(); +#else + vector *playerFiles = m_saveFile->getFilesWithPrefix( playerDir.getName() ); +#endif + + if( playerFiles != NULL ) + { + for( AUTO_VAR(it, playerFiles->begin()); it != playerFiles->end(); ++it) + { + FileEntry * realFile = *it; + ConsoleSaveFileInputStream fis = ConsoleSaveFileInputStream(m_saveFile, realFile); + CompoundTag *tag = NbtIo::readCompressed(&fis); + if (tag != NULL) + { + // If the player is in the nether, set their y position above the top of the nether + // This will force the player to be spawned in a valid position in the overworld when they are loaded + if(tag->contains(L"Dimension") && tag->getInt(L"Dimension") == LevelData::DIMENSION_NETHER && tag->contains(L"Pos")) + { + ListTag *pos = (ListTag *) tag->getList(L"Pos"); + pos->get(1)->data = DBL_MAX; + + ConsoleSaveFileOutputStream fos = ConsoleSaveFileOutputStream( m_saveFile, realFile ); + NbtIo::writeCompressed(tag, &fos); + } + delete tag; + } + } + delete playerFiles; + } + } +} + +int DirectoryLevelStorage::getAuxValueForMap(PlayerUID xuid, int dimension, int centreXC, int centreZC, int scale) +{ + int mapId = -1; + bool foundMapping = false; + +#ifdef _LARGE_WORLDS + AUTO_VAR(it, m_playerMappings.find(xuid) ); + if(it != m_playerMappings.end()) + { + foundMapping = it->second.getMapping(mapId, centreXC, centreZC, dimension, scale); + } + + if(!foundMapping) + { + for(unsigned int i = 0; i < m_usedMappings.length; ++i) + { + if(m_usedMappings[i] < 0xFF) + { + unsigned int offset = 0; + for(; offset < 8; ++offset) + { + if( !(m_usedMappings[i] & (1<= 0 && mapId < MAXIMUM_MAP_SAVE_DATA ) + { + m_mapDataMappings.setMapping(mapId, xuid, dimension); + m_saveableMapDataMappings.setMapping(mapId, xuid, dimension); + + // If we had an old map file for a mapping that is no longer valid, delete it + std::wstring id = wstring( L"map_" ) + _toString(mapId); + ConsoleSavePath file = getDataFile(id); + + if(m_saveFile->doesFileExist(file) ) + { + AUTO_VAR(it, find(m_mapFilesToDelete.begin(), m_mapFilesToDelete.end(), mapId)); + if(it != m_mapFilesToDelete.end()) m_mapFilesToDelete.erase(it); + + m_saveFile->deleteFile( m_saveFile->createFile(file) ); + } + } +#endif + return mapId; +} + +void DirectoryLevelStorage::saveMapIdLookup() +{ + if(StorageManager.GetSaveDisabled() ) return; + +#ifdef _LARGE_WORLDS + ConsoleSavePath file = getDataFile(L"largeMapDataMappings"); +#else + ConsoleSavePath file = getDataFile(L"mapDataMappings"); +#endif + + if (!file.getName().empty()) + { + DWORD NumberOfBytesWritten; + FileEntry *fileEntry = m_saveFile->createFile(file); + m_saveFile->setFilePointer(fileEntry,0,NULL, FILE_BEGIN); + +#ifdef _LARGE_WORLDS + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + dos.writeInt(m_playerMappings.size()); + app.DebugPrintf("Saving %d mappings\n", m_playerMappings.size()); + for(AUTO_VAR(it,m_playerMappings.begin()); it != m_playerMappings.end(); ++it) + { +#ifdef _WINDOWS64 + app.DebugPrintf(" -- %d\n", it->first); +#else + app.DebugPrintf(" -- %ls\n", it->first.toString().c_str()); +#endif + dos.writePlayerUID(it->first); + it->second.writeMappings(&dos); + } + dos.write(m_usedMappings); + m_saveFile->writeFile( fileEntry, + baos.buf.data, // data buffer + baos.size(), // number of bytes to write + &NumberOfBytesWritten // number of bytes written + ); +#else + m_saveFile->writeFile( fileEntry, + &m_saveableMapDataMappings, // data buffer + sizeof(MapDataMappings), // number of bytes to write + &NumberOfBytesWritten // number of bytes written + ); + assert( NumberOfBytesWritten == sizeof(MapDataMappings) ); +#endif + } +} + +void DirectoryLevelStorage::dontSaveMapMappingForPlayer(PlayerUID xuid) +{ +#ifdef _LARGE_WORLDS + AUTO_VAR(it, m_playerMappings.find(xuid) ); + if(it != m_playerMappings.end()) + { + for(AUTO_VAR(itMap, it->second.m_mappings.begin()); itMap != it->second.m_mappings.end(); ++itMap) + { + int index = itMap->second / 8; + int offset = itMap->second % 8; + m_usedMappings[index] &= ~(1< player) +{ + PlayerUID playerXuid = player->getXuid(); + if(playerXuid != INVALID_XUID) deleteMapFilesForPlayer(playerXuid); +} + +void DirectoryLevelStorage::deleteMapFilesForPlayer(PlayerUID xuid) +{ +#ifdef _LARGE_WORLDS + AUTO_VAR(it, m_playerMappings.find(xuid) ); + if(it != m_playerMappings.end()) + { + for(AUTO_VAR(itMap, it->second.m_mappings.begin()); itMap != it->second.m_mappings.end(); ++itMap) + { + std::wstring id = wstring( L"map_" ) + _toString(itMap->second); + ConsoleSavePath file = getDataFile(id); + + if(m_saveFile->doesFileExist(file) ) + { + // If we can't actually delete this file, store the name so we can delete it later + if(StorageManager.GetSaveDisabled()) m_mapFilesToDelete.push_back(itMap->second); + else m_saveFile->deleteFile( m_saveFile->createFile(file) ); + } + + int index = itMap->second / 8; + int offset = itMap->second % 8; + m_usedMappings[index] &= ~(1<doesFileExist(file) ) + { + // If we can't actually delete this file, store the name so we can delete it later + if(StorageManager.GetSaveDisabled()) m_mapFilesToDelete.push_back(i); + else m_saveFile->deleteFile( m_saveFile->createFile(file) ); + } + m_mapDataMappings.setMapping(i,INVALID_XUID,0); + m_saveableMapDataMappings.setMapping(i,INVALID_XUID,0); + break; + } + } +#endif +} + +void DirectoryLevelStorage::saveAllCachedData() +{ + if(StorageManager.GetSaveDisabled() ) return; + + // Save any files that were saved while saving was disabled + for(AUTO_VAR(it, m_cachedSaveData.begin()); it != m_cachedSaveData.end(); ++it) + { + ByteArrayOutputStream *bos = it->second; + + ConsoleSavePath realFile = ConsoleSavePath( it->first ); + ConsoleSaveFileOutputStream fos = ConsoleSaveFileOutputStream( m_saveFile, realFile ); + + app.DebugPrintf("Actually writing cached file %ls\n",it->first.c_str() ); + fos.write(bos->buf, 0, bos->size() ); + delete bos; + } + m_cachedSaveData.clear(); + + for(AUTO_VAR(it, m_mapFilesToDelete.begin()); it != m_mapFilesToDelete.end(); ++it) + { + std::wstring id = wstring( L"map_" ) + _toString(*it); + ConsoleSavePath file = getDataFile(id); + if(m_saveFile->doesFileExist(file) ) + { + m_saveFile->deleteFile( m_saveFile->createFile(file) ); + } + } + m_mapFilesToDelete.clear(); +} diff --git a/Minecraft.World/DirectoryLevelStorage.h b/Minecraft.World/DirectoryLevelStorage.h new file mode 100644 index 00000000..820ef31f --- /dev/null +++ b/Minecraft.World/DirectoryLevelStorage.h @@ -0,0 +1,144 @@ +#pragma once +using namespace std; + +#ifdef _LARGE_WORLDS +// 51 maps per player (7x7 overworld, 1 nether, 1 end) * 100 players rounded up to power of 2 +#define MAXIMUM_MAP_SAVE_DATA 8192//65536 + +// 4J Stu - These are special map slots that are used on local machines. They will never be an actual map, +// but are placeholders for when we get updated with the correct id +#define MAP_OVERWORLD_DEFAULT_INDEX 65535 +#define MAP_NETHER_DEFAULT_INDEX 65534 +#define MAP_END_DEFAULT_INDEX 65533 +#else +#define MAXIMUM_MAP_SAVE_DATA 256 + +// 4J Stu - These are special map slots that are used on local machines. They will never be an actual map, +// but are placeholders for when we get updated with the correct id +#define MAP_OVERWORLD_DEFAULT_INDEX 255 +#define MAP_NETHER_DEFAULT_INDEX 254 +#define MAP_END_DEFAULT_INDEX 253 +#endif + +// The save file version in which we added the End dimension map mappings +#define END_DIMENSION_MAP_MAPPINGS_SAVE_VERSION 5 + +#include "File.h" +#include "LevelStorage.h" +#include "PlayerIO.h" + +#include "ConsoleSavePath.h" +class ConsoleSaveFile; + +// 4J Stu - Added this which we will write out as a file. Map id's are stored in itemInstances +// as the auxValue, so we can have at most 65536 maps. As we currently have a limit of 80 players +// with 3 maps each we should not hit this limit. +typedef struct _MapDataMappings +{ + PlayerUID xuids[MAXIMUM_MAP_SAVE_DATA]; + byte dimensions[MAXIMUM_MAP_SAVE_DATA/4]; + + _MapDataMappings(); + int getDimension(int id); + void setMapping(int id, PlayerUID xuid, int dimension); +} MapDataMappings; + +// Old version the only used 1 bit for dimension indexing +typedef struct _MapDataMappings_old +{ + PlayerUID xuids[MAXIMUM_MAP_SAVE_DATA]; + byte dimensions[MAXIMUM_MAP_SAVE_DATA/8]; + + _MapDataMappings_old(); + int getDimension(int id); + void setMapping(int id, PlayerUID xuid, int dimension); +} MapDataMappings_old; + +class DirectoryLevelStorage : public LevelStorage, public PlayerIO +{ +private: + /* 4J Jev, Probably no need for this as theres no exceptions being thrown. + static const Logger *logger = Logger::getLogger("Minecraft"); */ + + const File dir; + //const File playerDir; + const ConsoleSavePath playerDir; + //const File dataDir; + const ConsoleSavePath dataDir; + const __int64 sessionId; + const wstring levelId; + + static const wstring sc_szPlayerDir; + // 4J Added +#ifdef _LARGE_WORLDS + class PlayerMappings + { + friend class DirectoryLevelStorage; + private: + unordered_map<__int64, short> m_mappings; + + public: + void addMapping(int id, int centreX, int centreZ, int dimension, int scale); + bool getMapping(int &id, int centreX, int centreZ, int dimension, int scale); + void writeMappings(DataOutputStream *dos); + void readMappings(DataInputStream *dis); + }; +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) || defined(_DURANGO) + unordered_map m_playerMappings; +#else + unordered_map m_playerMappings; +#endif + byteArray m_usedMappings; +#else + MapDataMappings m_mapDataMappings; + MapDataMappings m_saveableMapDataMappings; +#endif + bool m_bHasLoadedMapDataMappings; + + unordered_map m_cachedSaveData; + vector m_mapFilesToDelete; // Temp list of files that couldn't be deleted immediately due to saving being disabled + +protected: + ConsoleSaveFile *m_saveFile; + +public: + virtual ConsoleSaveFile *getSaveFile() { return m_saveFile; } + virtual void flushSaveFile(bool autosave); + +public: + DirectoryLevelStorage(ConsoleSaveFile *saveFile, const File dir, const wstring& levelId, bool createPlayerDir); + ~DirectoryLevelStorage(); + +private: + void initiateSession(); + +protected: + File getFolder(); + +public: + void checkSession(); + virtual ChunkStorage *createChunkStorage(Dimension *dimension); + LevelData *prepareLevel(); + virtual void saveLevelData(LevelData *levelData, vector > *players); + virtual void saveLevelData(LevelData *levelData); + virtual void save(shared_ptr player); + virtual bool load(shared_ptr player); // 4J Changed return val to bool to check if new player or loaded player + virtual CompoundTag *loadPlayerDataTag(PlayerUID xuid); + virtual void clearOldPlayerFiles(); // 4J Added + PlayerIO *getPlayerIO(); + virtual void closeAll(); + ConsoleSavePath getDataFile(const wstring& id); + wstring getLevelId(); + + // 4J Added + virtual int getAuxValueForMap(PlayerUID xuid, int dimension, int centreXC, int centreZC, int scale); + virtual void saveMapIdLookup(); + virtual void deleteMapFilesForPlayer(shared_ptr player); + virtual void saveAllCachedData(); + void resetNetherPlayerPositions(); // 4J Added + static wstring getPlayerDir() { return sc_szPlayerDir; } + +private: + void dontSaveMapMappingForPlayer(PlayerUID xuid); + void deleteMapFilesForPlayer(PlayerUID xuid); +}; diff --git a/Minecraft.World/DirectoryLevelStorageSource.cpp b/Minecraft.World/DirectoryLevelStorageSource.cpp new file mode 100644 index 00000000..b4beb8b5 --- /dev/null +++ b/Minecraft.World/DirectoryLevelStorageSource.cpp @@ -0,0 +1,139 @@ +#include "stdafx.h" +#include "File.h" +#include "LevelData.h" +#include "LevelSummary.h" +#include "com.mojang.nbt.h" +#include "DirectoryLevelStorage.h" +#include "DirectoryLevelStorageSource.h" + +#include "ConsoleSaveFileIO.h" +#include "ConsoleSaveFileOriginal.h" + +class LevelStorage; + +DirectoryLevelStorageSource::DirectoryLevelStorageSource(const File dir) : baseDir( dir ) +{ + //if (!dir.exists()) dir.mkdirs(); // 4J Removed + //this->baseDir = dir; +} + +wstring DirectoryLevelStorageSource::getName() +{ + return L"Old Format"; +} + +vector *DirectoryLevelStorageSource::getLevelList() +{ + // 4J Stu - We don't use directory list with the Xbox save locations + vector *levels = new vector; +#if 0 + for (int i = 0; i < 5; i++) + { + wstring levelId = wstring(L"World").append( _toString( (i+1) ) ); + + LevelData *levelData = getDataTagFor(saveFile, levelId); + if (levelData != NULL) + { + levels->push_back(new LevelSummary(levelId, L"", levelData->getLastPlayed(), levelData->getSizeOnDisk(), levelData.getGameType(), false, levelData->isHardcore())); + } + } +#endif + return levels; +} + +void DirectoryLevelStorageSource::clearAll() +{ +} + +LevelData *DirectoryLevelStorageSource::getDataTagFor(ConsoleSaveFile *saveFile, const wstring& levelId) +{ + //File dataFile(dir, L"level.dat"); + ConsoleSavePath dataFile = ConsoleSavePath( wstring( L"level.dat" ) ); + if ( saveFile->doesFileExist( dataFile ) ) + { + ConsoleSaveFileInputStream fis = ConsoleSaveFileInputStream(saveFile, dataFile); + CompoundTag *root = NbtIo::readCompressed(&fis); + CompoundTag *tag = root->getCompound(L"Data"); + LevelData *ret = new LevelData(tag); + delete root; + return ret; + } + + return NULL; +} + +void DirectoryLevelStorageSource::renameLevel(const wstring& levelId, const wstring& newLevelName) +{ + ConsoleSaveFileOriginal tempSave(levelId); + + //File dataFile = File(dir, L"level.dat"); + ConsoleSavePath dataFile = ConsoleSavePath( wstring( L"level.dat" ) ); + if ( tempSave.doesFileExist( dataFile ) ) + { + ConsoleSaveFileInputStream fis = ConsoleSaveFileInputStream(&tempSave, dataFile); + CompoundTag *root = NbtIo::readCompressed(&fis); + CompoundTag *tag = root->getCompound(L"Data"); + tag->putString(L"LevelName", newLevelName); + + ConsoleSaveFileOutputStream fos = ConsoleSaveFileOutputStream(&tempSave, dataFile); + NbtIo::writeCompressed(root, &fos); + } +} + +bool DirectoryLevelStorageSource::isNewLevelIdAcceptable(const wstring& levelId) +{ + // 4J Jev, removed try/catch. + + File levelFolder = File(baseDir, levelId); + if (levelFolder.exists()) + { + return false; + } + + levelFolder.mkdir(); + + return true; +} + +void DirectoryLevelStorageSource::deleteLevel(const wstring& levelId) +{ + File dir = File(baseDir, levelId); + if (!dir.exists()) return; + + deleteRecursive(dir.listFiles()); + dir._delete(); +} + +void DirectoryLevelStorageSource::deleteRecursive(vector *files) +{ + AUTO_VAR(itEnd, files->end()); + for (AUTO_VAR(it, files->begin()); it != itEnd; it++) + { + File *file = *it; + if (file->isDirectory()) + { + deleteRecursive(file->listFiles()); + } + file->_delete(); + } +} + +shared_ptr DirectoryLevelStorageSource::selectLevel(ConsoleSaveFile *saveFile, const wstring& levelId, bool createPlayerDir) +{ + return shared_ptr (new DirectoryLevelStorage(saveFile, baseDir, levelId, createPlayerDir)); +} + +bool DirectoryLevelStorageSource::isConvertible(ConsoleSaveFile *saveFile, const wstring& levelId) +{ + return false; +} + +bool DirectoryLevelStorageSource::requiresConversion(ConsoleSaveFile *saveFile, const wstring& levelId) +{ + return false; +} + +bool DirectoryLevelStorageSource::convertLevel(ConsoleSaveFile *saveFile, const wstring& levelId, ProgressListener *progress) +{ + return false; +} diff --git a/Minecraft.World/DirectoryLevelStorageSource.h b/Minecraft.World/DirectoryLevelStorageSource.h new file mode 100644 index 00000000..9f6069ab --- /dev/null +++ b/Minecraft.World/DirectoryLevelStorageSource.h @@ -0,0 +1,34 @@ +#pragma once +using namespace std; + +#include "LevelStorageSource.h" +#include "File.h" + +class ProgressListener; +class LevelData; +class ConsoleSaveFile; + +class DirectoryLevelStorageSource : public LevelStorageSource +{ +protected: + const File baseDir; + +public: + DirectoryLevelStorageSource(const File dir); + virtual wstring getName(); + virtual vector *getLevelList(); + virtual void clearAll(); + virtual LevelData *getDataTagFor(ConsoleSaveFile *saveFile, const wstring& levelId); + virtual void renameLevel(const wstring& levelId, const wstring& newLevelName); + virtual bool isNewLevelIdAcceptable(const wstring& levelId); + virtual void deleteLevel(const wstring& levelId); + +protected: + static void deleteRecursive(vector *files); + +public: + virtual shared_ptr selectLevel(ConsoleSaveFile *saveFile, const wstring& levelId, bool createPlayerDir); + virtual bool isConvertible(ConsoleSaveFile *saveFile, const wstring& levelId); + virtual bool requiresConversion(ConsoleSaveFile *saveFile, const wstring& levelId); + virtual bool convertLevel(ConsoleSaveFile *saveFile, const wstring& levelId, ProgressListener *progress); +}; \ No newline at end of file diff --git a/Minecraft.World/DirtTile.cpp b/Minecraft.World/DirtTile.cpp new file mode 100644 index 00000000..76cdfef3 --- /dev/null +++ b/Minecraft.World/DirtTile.cpp @@ -0,0 +1,6 @@ +#include "stdafx.h" +#include "DirtTile.h" + +DirtTile::DirtTile(int id) : Tile(id, Material::dirt) +{ +} \ No newline at end of file diff --git a/Minecraft.World/DirtTile.h b/Minecraft.World/DirtTile.h new file mode 100644 index 00000000..31790886 --- /dev/null +++ b/Minecraft.World/DirtTile.h @@ -0,0 +1,9 @@ +#pragma once +#include "Tile.h" + +class DirtTile : public Tile +{ + friend class Tile; +protected: + DirtTile(int id); +}; \ No newline at end of file diff --git a/Minecraft.World/DisconnectPacket.cpp b/Minecraft.World/DisconnectPacket.cpp new file mode 100644 index 00000000..da20686a --- /dev/null +++ b/Minecraft.World/DisconnectPacket.cpp @@ -0,0 +1,48 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "SharedConstants.h" +#include "PacketListener.h" +#include "DisconnectPacket.h" + + + +DisconnectPacket::DisconnectPacket() +{ + reason = eDisconnect_None; +} + +DisconnectPacket::DisconnectPacket(eDisconnectReason reason) +{ + this->reason = reason; +} + +void DisconnectPacket::read(DataInputStream *dis) //throws IOException +{ + reason = (eDisconnectReason)dis->readInt(); +} + +void DisconnectPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt((int)reason); +} + +void DisconnectPacket::handle(PacketListener *listener) +{ + listener->handleDisconnect(shared_from_this()); +} + +int DisconnectPacket::getEstimatedSize() +{ + return sizeof(eDisconnectReason); +} + +bool DisconnectPacket::canBeInvalidated() +{ + return true; +} + +bool DisconnectPacket::isInvalidatedBy(shared_ptr packet) +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/DisconnectPacket.h b/Minecraft.World/DisconnectPacket.h new file mode 100644 index 00000000..34983754 --- /dev/null +++ b/Minecraft.World/DisconnectPacket.h @@ -0,0 +1,73 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class DisconnectPacket : public Packet, public enable_shared_from_this +{ +public: + + enum eDisconnectReason + { + eDisconnect_None = 0, + eDisconnect_Quitting, + eDisconnect_Closed, + + eDisconnect_LoginTooLong, + eDisconnect_IllegalStance, + eDisconnect_IllegalPosition, + eDisconnect_MovedTooQuickly, + eDisconnect_NoFlying, + eDisconnect_Kicked, + + eDisconnect_TimeOut, + eDisconnect_Overflow, + eDisconnect_EndOfStream, + eDisconnect_ServerFull, + eDisconnect_OutdatedServer, + eDisconnect_OutdatedClient, + eDisconnect_UnexpectedPacket, + + eDisconnect_ConnectionCreationFailed, + eDisconnect_NoMultiplayerPrivilegesHost, + eDisconnect_NoMultiplayerPrivilegesJoin, + + eDisconnect_NoUGC_AllLocal, + eDisconnect_NoUGC_Single_Local, + eDisconnect_ContentRestricted_AllLocal, + eDisconnect_ContentRestricted_Single_Local, +#ifndef __PS3__ + eDisconnect_NoUGC_Remote, +#endif + + eDisconnect_NoFriendsInGame, + eDisconnect_Banned, + eDisconnect_NotFriendsWithHost, + eDisconnect_NATMismatch, +#ifdef __ORBIS__ + eDisconnect_NetworkError, +#endif +#ifdef _XBOX_ONE + eDisconnect_ExitedGame, +#endif + }; + + // 4J Stu - The reason was a string, but we need to send a non-locale specific reason + eDisconnectReason reason; + + DisconnectPacket(); + DisconnectPacket(eDisconnectReason reason); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + +public: + static shared_ptr create() { return shared_ptr(new DisconnectPacket()); } + virtual int getId() { return 255; } +}; + + diff --git a/Minecraft.World/DispenserTile.cpp b/Minecraft.World/DispenserTile.cpp new file mode 100644 index 00000000..286737c9 --- /dev/null +++ b/Minecraft.World/DispenserTile.cpp @@ -0,0 +1,573 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.h" +#include "DispenserTile.h" +#include "net.minecraft.h" +#include "Mob.h" + +DispenserTile::DispenserTile(int id) : EntityTile(id, Material::stone) +{ + random = new Random(); + + iconTop = NULL; + iconFront = NULL; + iconFrontVertical = NULL; +} + +int DispenserTile::getTickDelay() +{ + return 4; +} + +int DispenserTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::dispenser_Id; +} + +void DispenserTile::onPlace(Level *level, int x, int y, int z) +{ + EntityTile::onPlace(level, x, y, z); + recalcLockDir(level, x, y, z); +} + +void DispenserTile::recalcLockDir(Level *level, int x, int y, int z) +{ + if (level->isClientSide) + { + return; + } + + int n = level->getTile(x, y, z - 1); // face = 2 + int s = level->getTile(x, y, z + 1); // face = 3 + int w = level->getTile(x - 1, y, z); // face = 4 + int e = level->getTile(x + 1, y, z); // face = 5 + + int lockDir = 3; + if (Tile::solid[n] && !Tile::solid[s]) lockDir = 3; + if (Tile::solid[s] && !Tile::solid[n]) lockDir = 2; + if (Tile::solid[w] && !Tile::solid[e]) lockDir = 5; + if (Tile::solid[e] && !Tile::solid[w]) lockDir = 4; + level->setData(x, y, z, lockDir); +} + +Icon *DispenserTile::getTexture(int face, int data) +{ + int dir = data & FACING_MASK; + + if (face == dir) + { + if (dir == Facing::UP || dir == Facing::DOWN) + { + return iconFrontVertical; + } + else + { + return iconFront; + } + } + + if (dir == Facing::UP || dir == Facing::DOWN) + { + return iconTop; + } + else if (face == Facing::UP || face == Facing::DOWN) + { + return iconTop; + } + + return icon; +} + +void DispenserTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"furnace_side"); + iconTop = iconRegister->registerIcon(L"furnace_top"); + iconFront = iconRegister->registerIcon(L"dispenser_front"); + iconFrontVertical = iconRegister->registerIcon(L"dispenser_front_vertical"); +} + +// 4J-PB - Adding a TestUse for tooltip display +bool DispenserTile::TestUse() +{ + return true; +} + +bool DispenserTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if( soundOnly) return false; + + if (level->isClientSide) + { + return true; + } + + shared_ptr trap = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + player->openTrap(trap); + + return true; +} + +void DispenserTile::fireArrow(Level *level, int x, int y, int z, Random *random) +{ + const int lockDir = level->getData(x, y, z); + //const float power = 1.1f; + const int accuracy = 6; + //bool bLaunched=true; + + int xd = 0, zd = 0; + if (lockDir == Facing::SOUTH) + { + zd = 1; + } + else if (lockDir == Facing::NORTH) + { + zd = -1; + } + else if (lockDir == Facing::EAST) + { + xd = 1; + } + else + { + xd = -1; + } + + shared_ptr trap = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + if(trap != NULL) + { + int slot=trap->getRandomSlot(); + + if (slot < 0) + { + level->levelEvent(LevelEvent::SOUND_CLICK_FAIL, x, y, z, 0); + } + else + { + double xp = x + xd * 0.6 + 0.5; + double yp = y + 0.5; + double zp = z + zd * 0.6 + 0.5; + shared_ptr item=trap->getItem(slot); + int result = dispenseItem(trap, level, item, random, x, y, z, xd, zd, xp, yp, zp); + if (result == REMOVE_ITEM) + { + trap->removeItem(slot, 1); + } + else if (result == DISPENSE_ITEM) + { + item = trap->removeItem(slot, 1); + throwItem(level, item, random, accuracy, xd, zd, xp, yp, zp); + level->levelEvent(LevelEvent::SOUND_CLICK, x, y, z, 0); + } + + level->levelEvent(LevelEvent::PARTICLES_SHOOT, x, y, z, (xd + 1) + (zd + 1) * 3); + } + } +} + +void DispenserTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (type > 0 && Tile::tiles[type]->isSignalSource()) + { + bool signal = level->hasNeighborSignal(x, y, z) || level->hasNeighborSignal(x, y + 1, z); + if (signal) + { + level->addToTickNextTick(x, y, z, this->id, getTickDelay()); + } + } +} + +void DispenserTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (!level->isClientSide && ( level->hasNeighborSignal(x, y, z) || level->hasNeighborSignal(x, y + 1, z))) + { + fireArrow(level, x, y, z, random); + } +} + +shared_ptr DispenserTile::newTileEntity(Level *level) +{ + return shared_ptr( new DispenserTileEntity() ); +} + +void DispenserTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int dir = (Mth::floor(by->yRot * 4 / (360) + 0.5)) & 3; + + if (dir == 0) level->setData(x, y, z, Facing::NORTH); + if (dir == 1) level->setData(x, y, z, Facing::EAST); + if (dir == 2) level->setData(x, y, z, Facing::SOUTH); + if (dir == 3) level->setData(x, y, z, Facing::WEST); +} + +void DispenserTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + shared_ptr container = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + if (container != NULL ) + { + for (unsigned int i = 0; i < container->getContainerSize(); i++) + { + shared_ptr item = container->getItem(i); + if (item != NULL) + { + float xo = random->nextFloat() * 0.8f + 0.1f; + float yo = random->nextFloat() * 0.8f + 0.1f; + float zo = random->nextFloat() * 0.8f + 0.1f; + + while (item->count > 0) + { + int count = random->nextInt(21) + 10; + if (count > item->count) count = item->count; + item->count -= count; + + shared_ptr newItem = shared_ptr( new ItemInstance(item->id, count, item->getAuxValue()) ); + newItem->set4JData( item->get4JData() ); + shared_ptr itemEntity = shared_ptr( new ItemEntity(level, x + xo, y + yo, z + zo, newItem ) ); + float pow = 0.05f; + itemEntity->xd = (float) random->nextGaussian() * pow; + itemEntity->yd = (float) random->nextGaussian() * pow + 0.2f; + itemEntity->zd = (float) random->nextGaussian() * pow; + if (item->hasTag()) + { + itemEntity->getItem()->setTag((CompoundTag *) item->getTag()->copy()); + } + level->addEntity(itemEntity); + } + + // 4J Stu - Fix for duplication glitch + container->setItem(i,nullptr); + } + } + } + EntityTile::onRemove(level, x, y, z, id, data); +} + +void DispenserTile::throwItem(Level *level, shared_ptr item, Random *random, int accuracy, int xd, int zd, double xp, double yp, double zp) +{ + shared_ptr itemEntity = shared_ptr(new ItemEntity(level, xp, yp - 0.3, zp, item)); + + double pow = random->nextDouble() * 0.1 + 0.2; + itemEntity->xd = xd * pow; + itemEntity->yd = .2f; + itemEntity->zd = zd * pow; + + itemEntity->xd += (random->nextGaussian()) * 0.0075f * accuracy; + itemEntity->yd += (random->nextGaussian()) * 0.0075f * accuracy; + itemEntity->zd += (random->nextGaussian()) * 0.0075f * accuracy; + + level->addEntity(itemEntity); +} + +int DispenserTile::dispenseItem(shared_ptr trap, Level *level, shared_ptr item, Random *random, int x, int y, int z, int xd, int zd, double xp, double yp, double zp) +{ + float power = 1.1f; + int accuracy = 6; + + // 4J-PB - moved to a switch + switch(item->id) + { + case Item::arrow_Id: + { + int currentProjectiles = level->countInstanceOf(eTYPE_PROJECTILE,false); + if(currentProjectiles < Level::MAX_DISPENSABLE_PROJECTILES) // 4J - added limit + { + shared_ptr arrow = shared_ptr( new Arrow(level, xp, yp, zp) ); + arrow->shoot(xd, .1f, zd, power, (float) accuracy); + arrow->pickup = Arrow::PICKUP_ALLOWED; + level->addEntity(arrow); + level->levelEvent(LevelEvent::SOUND_LAUNCH, x, y, z, 0); + return REMOVE_ITEM; + } + else + { + // some negative sound effect? + level->levelEvent(LevelEvent::SOUND_CLICK_FAIL, x, y, z, 0); + + // not sending a message here, since we will probably get flooded with them when people have automatic dispensers for spawn eggs + return LEAVE_ITEM; + } + } + break; + case Item::egg_Id: + { + int currentProjectiles = level->countInstanceOf(eTYPE_PROJECTILE,false); + if(currentProjectiles < Level::MAX_DISPENSABLE_PROJECTILES) // 4J - added limit + { + shared_ptr egg = shared_ptr( new ThrownEgg(level, xp, yp, zp) ); + egg->shoot(xd, .1f, zd, power, (float) accuracy); + level->addEntity(egg); + level->levelEvent(LevelEvent::SOUND_LAUNCH, x, y, z, 0); + return REMOVE_ITEM; + } + else + { + // some negative sound effect? + level->levelEvent(LevelEvent::SOUND_CLICK_FAIL, x, y, z, 0); + + // not sending a message here, since we will probably get flooded with them when people have automatic dispensers for spawn eggs + return LEAVE_ITEM; + } + } + break; + case Item::snowBall_Id: + { + int currentProjectiles = level->countInstanceOf(eTYPE_PROJECTILE,false); + if(currentProjectiles < Level::MAX_DISPENSABLE_PROJECTILES) // 4J - added limit + { + shared_ptr snowball = shared_ptr( new Snowball(level, xp, yp, zp) ); + snowball->shoot(xd, .1f, zd, power, (float) accuracy); + level->addEntity(snowball); + level->levelEvent(LevelEvent::SOUND_LAUNCH, x, y, z, 0); + return REMOVE_ITEM; + } + else + { + // some negative sound effect? + level->levelEvent(LevelEvent::SOUND_CLICK_FAIL, x, y, z, 0); + + // not sending a message here, since we will probably get flooded with them when people have automatic dispensers for spawn eggs + return LEAVE_ITEM; + } + } + break; + case Item::potion_Id: + { + int currentProjectiles = level->countInstanceOf(eTYPE_PROJECTILE,false); + if(currentProjectiles < Level::MAX_DISPENSABLE_PROJECTILES) // 4J - added limit + { + if(PotionItem::isThrowable(item->getAuxValue())) + { + shared_ptr potion = shared_ptr(new ThrownPotion(level, xp, yp, zp, item->getAuxValue())); + potion->shoot(xd, .1f, zd, power * 1.25f, accuracy * .5f); + level->addEntity(potion); + level->levelEvent(LevelEvent::SOUND_LAUNCH, x, y, z, 0); + } + else + { + shared_ptr itemEntity = shared_ptr( new ItemEntity(level, xp, yp - 0.3, zp, item) ); + + double pow = random->nextDouble() * 0.1 + 0.2; + itemEntity->xd = xd * pow; + itemEntity->yd = .2f; + itemEntity->zd = zd * pow; + + itemEntity->xd += (random->nextGaussian()) * 0.0075f * accuracy; + itemEntity->yd += (random->nextGaussian()) * 0.0075f * accuracy; + itemEntity->zd += (random->nextGaussian()) * 0.0075f * accuracy; + + level->addEntity(itemEntity); + level->levelEvent(LevelEvent::SOUND_CLICK, x, y, z, 0); + } + return REMOVE_ITEM; + } + else + { + // some negative sound effect? + level->levelEvent(LevelEvent::SOUND_CLICK_FAIL, x, y, z, 0); + + // not sending a message here, since we will probably get flooded with them when people have automatic dispensers for spawn eggs + return LEAVE_ITEM; + } + } + break; + case Item::expBottle_Id: + { + int currentProjectiles = level->countInstanceOf(eTYPE_PROJECTILE,false); + if(currentProjectiles < Level::MAX_DISPENSABLE_PROJECTILES) // 4J - added limit + { + shared_ptr expBottle = shared_ptr( new ThrownExpBottle(level, xp, yp, zp) ); + expBottle->shoot(xd, .1f, zd, power * 1.25f, accuracy * .5f); + level->addEntity(expBottle); + level->levelEvent(LevelEvent::SOUND_LAUNCH, x, y, z, 0); + return REMOVE_ITEM; + } + else + { + // some negative sound effect? + level->levelEvent(LevelEvent::SOUND_CLICK_FAIL, x, y, z, 0); + + // not sending a message here, since we will probably get flooded with them when people have automatic dispensers for spawn eggs + return LEAVE_ITEM; + } + } + break; + case Item::fireball_Id: // TU9 + { + int currentFireballs = level->countInstanceOf(eTYPE_SMALL_FIREBALL,true); + if(currentFireballs < Level::MAX_DISPENSABLE_FIREBALLS) // 4J - added limit + { + shared_ptr fireball = shared_ptr( new SmallFireball(level, xp + xd * .3, yp, zp + zd * .3, xd + random->nextGaussian() * .05, random->nextGaussian() * .05, zd + random->nextGaussian() * .05)); + level->addEntity(fireball); + level->levelEvent(LevelEvent::SOUND_BLAZE_FIREBALL, x, y, z, 0); + return REMOVE_ITEM; + } + else + { + // some negative sound effect? + level->levelEvent(LevelEvent::SOUND_CLICK_FAIL, x, y, z, 0); + + // not sending a message here, since we will probably get flooded with them when people have automatic dispensers for spawn eggs + return LEAVE_ITEM; + } + } + break; + case Item::monsterPlacer_Id: + { + int iResult=0; + //MonsterPlacerItem *spawnEgg = (MonsterPlacerItem *)item->getItem(); + shared_ptr newEntity = MonsterPlacerItem::canSpawn(item->getAuxValue(), level,&iResult); + + shared_ptr mob = dynamic_pointer_cast(newEntity); + if (mob != NULL) + { + // 4J-PB - Changed the line below slightly since mobs were sticking to the dispenser rather than dropping down when fired + mob->moveTo(xp + xd * 0.4, yp - 0.3, zp + zd * 0.4, level->random->nextFloat() * 360, 0); + mob->finalizeMobSpawn(); + level->addEntity(mob); + level->levelEvent(LevelEvent::SOUND_LAUNCH, x, y, z, 0); + return REMOVE_ITEM; + } + else + { + // some negative sound effect? + level->levelEvent(LevelEvent::SOUND_CLICK_FAIL, x, y, z, 0); + + // not sending a message here, since we will probably get flooded with them when people have automatic dispensers for spawn eggs + return LEAVE_ITEM; + } + } + break; + case Item::bucket_lava_Id: + case Item::bucket_water_Id: + { + BucketItem *pBucket = (BucketItem *) item->getItem(); + + if (pBucket->emptyBucket(level, x, y, z, x + xd, y, z + zd)) + { + item->id = Item::bucket_empty_Id; + item->count = 1; + return LEAVE_ITEM; + } + return DISPENSE_ITEM; + } + break; + case Item::bucket_empty_Id: + { + int xt = x + xd; + int zt = z + zd; + Material *pMaterial=level->getMaterial(xt, y, zt); + int data = level->getData(xt, y, zt); + + if (pMaterial == Material::water && data == 0) + { + level->setTile(xt, y, zt, 0); + + if (--item->count == 0) + { + item->id = Item::bucket_water_Id; + item->count = 1; + } + else if (trap->addItem(shared_ptr(new ItemInstance(Item::bucket_water))) < 0) + { + throwItem(level, shared_ptr(new ItemInstance(Item::bucket_water)), random, 6, xd, zd, xp, yp, zp); + } + + return LEAVE_ITEM; + } + else if (pMaterial == Material::lava && data == 0) + { + level->setTile(xt, y, zt, 0); + + if (--item->count == 0) + { + item->id = Item::bucket_lava_Id; + item->count = 1; + } + else if (trap->addItem(shared_ptr(new ItemInstance(Item::bucket_lava))) < 0) + { + throwItem(level, shared_ptr(new ItemInstance(Item::bucket_lava)), random, 6, xd, zd, xp, yp, zp); + } + + return LEAVE_ITEM; + } + return DISPENSE_ITEM; + } + + break; + // TU12 + case Item::minecart_Id: + case Item::minecart_chest_Id: + case Item::minecart_furnace_Id: + { + xp = x + (xd < 0 ? xd * 0.8 : xd * 1.8f) + Mth::abs(zd) * 0.5f; + zp = z + (zd < 0 ? zd * 0.8 : zd * 1.8f) + Mth::abs(xd) * 0.5f; + + if (RailTile::isRail(level, x + xd, y, z + zd)) + { + yp = y + 0.5f; + } + else if (level->isEmptyTile(x + xd, y, z + zd) && RailTile::isRail(level, x + xd, y - 1, z + zd)) + { + yp = y - 0.5f; + } + else + { + return DISPENSE_ITEM; + } + + if( level->countInstanceOf(eTYPE_MINECART, true) < Level::MAX_CONSOLE_MINECARTS ) // 4J - added limit + { + shared_ptr minecart = shared_ptr(new Minecart(level, xp, yp, zp, ((MinecartItem *) item->getItem())->type)); + level->addEntity(minecart); + level->levelEvent(LevelEvent::SOUND_CLICK, x, y, z, 0); + + return REMOVE_ITEM; + } + else + { + return DISPENSE_ITEM; + } + } + break; + + case Item::boat_Id: + { + bool bLaunchBoat=false; + + xp = x + (xd < 0 ? xd * 0.8 : xd * 1.8f) + Mth::abs(zd) * 0.5f; + zp = z + (zd < 0 ? zd * 0.8 : zd * 1.8f) + Mth::abs(xd) * 0.5f; + + if (level->getMaterial(x + xd, y, z + zd) == Material::water) + { + bLaunchBoat=true; + yp = y + 1.0f; + } + else if (level->isEmptyTile(x + xd, y, z + zd) && level->getMaterial(x + xd, y - 1, z + zd) == Material::water) + { + bLaunchBoat=true; + yp = y; + } + + // check the limit on boats + if( bLaunchBoat && level->countInstanceOf(eTYPE_BOAT, true) < Level::MAX_XBOX_BOATS ) // 4J - added limit + { + shared_ptr boat = shared_ptr(new Boat(level, xp, yp, zp)); + level->addEntity(boat); + level->levelEvent(LevelEvent::SOUND_CLICK, x, y, z, 0); + return REMOVE_ITEM; + } + else + { + return DISPENSE_ITEM; + } + } + break; + } + + return DISPENSE_ITEM; +} diff --git a/Minecraft.World/DispenserTile.h b/Minecraft.World/DispenserTile.h new file mode 100644 index 00000000..5bc892aa --- /dev/null +++ b/Minecraft.World/DispenserTile.h @@ -0,0 +1,58 @@ +#pragma once +#include "EntityTile.h" + +class Player; +class Mob; +class ChunkRebuildData; +class DispenserTile : public EntityTile +{ + friend class Tile; + friend class ChunkRebuildData; + +private: + static const int DISPENSE_ITEM = 0; + static const int REMOVE_ITEM = 1; + static const int LEAVE_ITEM = 2; + +public: + static const int FACING_MASK = 0x7; + +protected: + Random *random; + + Icon *iconTop; + Icon *iconFront; + Icon *iconFrontVertical; + +protected: + DispenserTile(int id); + +public: + virtual int getTickDelay(); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual void onPlace(Level *level, int x, int y, int z); + +private: + void recalcLockDir(Level *level, int x, int y, int z); + +public: + virtual Icon *getTexture(int face, int data); + //@Override + void registerIcons(IconRegister *iconRegister); + virtual bool TestUse(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + +private: + void fireArrow(Level *level, int x, int y, int z, Random *random); + +public: + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual shared_ptr newTileEntity(Level *level); + virtual void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); + +private: + static void throwItem(Level *level, shared_ptr item, Random *random, int accuracy, int xd, int zd, double xp, double yp, double zp); + static int dispenseItem(shared_ptr trap, Level *level, shared_ptr item, Random *random, int x, int y, int z, int xd, int zd, double xp, double yp, double zp); +}; \ No newline at end of file diff --git a/Minecraft.World/DispenserTileEntity.cpp b/Minecraft.World/DispenserTileEntity.cpp new file mode 100644 index 00000000..2c4705ea --- /dev/null +++ b/Minecraft.World/DispenserTileEntity.cpp @@ -0,0 +1,224 @@ +using namespace std; + +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "TileEntity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "DispenserTileEntity.h" + + + +DispenserTileEntity::DispenserTileEntity() : TileEntity() +{ + items = new ItemInstanceArray(9); + random = new Random(); +} + +DispenserTileEntity::~DispenserTileEntity() +{ + delete[] items->data; + delete items; + + delete random; +} + +unsigned int DispenserTileEntity::getContainerSize() +{ + return 9; +} + +shared_ptr DispenserTileEntity::getItem(unsigned int slot) +{ + return items->data[slot]; +} + +shared_ptr DispenserTileEntity::removeItem(unsigned int slot, int count) +{ + if (items->data[slot] != NULL) + { + if (items->data[slot]->count <= count) + { + shared_ptr item = items->data[slot]; + items->data[slot] = nullptr; + this->setChanged(); + // 4J Stu - Fix for duplication glitch + if(item->count <= 0) return nullptr; + return item; + } + else + { + shared_ptr i = items->data[slot]->remove(count); + if (items->data[slot]->count == 0) items->data[slot] = nullptr; + this->setChanged(); + // 4J Stu - Fix for duplication glitch + if(i->count <= 0) return nullptr; + return i; + } + } + return nullptr; +} + +shared_ptr DispenserTileEntity::removeItemNoUpdate(int slot) +{ + if (items->data[slot] != NULL) + { + shared_ptr item = items->data[slot]; + items->data[slot] = nullptr; + return item; + } + return nullptr; +} + +// 4J-PB added for spawn eggs not being useable due to limits, so add them in again +void DispenserTileEntity::AddItemBack(shared_ptritem, unsigned int slot) +{ + if (items->data[slot] != NULL) + { + // just increment the count of the items + if(item->id==items->data[slot]->id) + { + items->data[slot]->count++; + this->setChanged(); + } + } + else + { + items->data[slot] = item; + if (item != NULL && item->count > getMaxStackSize()) item->count = getMaxStackSize(); + this->setChanged(); + } +} +/** +* Removes an item with the given id and returns true if one was found. +* +* @param itemId +* @return +*/ +bool DispenserTileEntity::removeProjectile(int itemId) +{ + for (unsigned int i = 0; i < items->length; i++) + { + if (items->data[i] != NULL && items->data[i]->id == itemId) + { + shared_ptr removedItem = removeItem(i, 1); + return removedItem != NULL; + } + } + return false; +} + +int DispenserTileEntity::getRandomSlot() +{ + int replaceSlot = -1; + int replaceOdds = 1; + for (unsigned int i = 0; i < items->length; i++) + { + if (items->data[i] != NULL && random->nextInt(replaceOdds++) == 0) + { + replaceSlot = i; + } + } + + return replaceSlot; +} + +void DispenserTileEntity::setItem(unsigned int slot, shared_ptr item) +{ + items->data[slot] = item; + if (item != NULL && item->count > getMaxStackSize()) item->count = getMaxStackSize(); + this->setChanged(); +} + +int DispenserTileEntity::addItem(shared_ptr item) +{ + for (int i = 0; i < items->length; i++) + { + if ((*items)[i] == NULL || (*items)[i]->id == 0) + { + (*items)[i] = item; + return i; + } + } + + return -1; +} + +int DispenserTileEntity::getName() +{ + return IDS_TILE_DISPENSER; +} + +void DispenserTileEntity::load(CompoundTag *base) +{ + TileEntity::load(base); + ListTag *inventoryList = (ListTag *) base->getList(L"Items"); + items = new ItemInstanceArray(getContainerSize()); + for (int i = 0; i < inventoryList->size(); i++) + { + CompoundTag *tag = inventoryList->get(i); + unsigned int slot = tag->getByte(L"Slot") & 0xff; + if (slot >= 0 && slot < items->length) (*items)[slot] = ItemInstance::fromTag(tag); + } +} + +void DispenserTileEntity::save(CompoundTag *base) +{ + TileEntity::save(base); + ListTag *listTag = new ListTag; + + for (unsigned int i = 0; i < items->length; i++) + { + if (items->data[i] != NULL) + { + CompoundTag *tag = new CompoundTag(); + tag->putByte(L"Slot", (byte) i); + items->data[i]->save(tag); + listTag->add(tag); + } + } + base->put(L"Items", listTag); +} + +int DispenserTileEntity::getMaxStackSize() +{ + return Container::LARGE_MAX_STACK_SIZE; +} + +bool DispenserTileEntity::stillValid(shared_ptr player) +{ + if (level->getTileEntity(x, y, z) != shared_from_this() ) return false; + if (player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) > 8 * 8) return false; + return true; +} + +void DispenserTileEntity::setChanged() +{ + return TileEntity::setChanged(); +} + +void DispenserTileEntity::startOpen() +{ +} + +void DispenserTileEntity::stopOpen() +{ +} + +// 4J Added +shared_ptr DispenserTileEntity::clone() +{ + shared_ptr result = shared_ptr( new DispenserTileEntity() ); + TileEntity::clone(result); + + for (unsigned int i = 0; i < items->length; i++) + { + if (items->data[i] != NULL) + { + result->items->data[i] = ItemInstance::clone(items->data[i]); + } + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/DispenserTileEntity.h b/Minecraft.World/DispenserTileEntity.h new file mode 100644 index 00000000..8519ad54 --- /dev/null +++ b/Minecraft.World/DispenserTileEntity.h @@ -0,0 +1,52 @@ +#pragma once + +using namespace std; + +#include "com.mojang.nbt.h" +#include "TileEntity.h" +#include "Container.h" + +class Player; +class Random; +class Level; +class CompoundTag; + +class DispenserTileEntity: public TileEntity, public Container +{ +public: + eINSTANCEOF GetType() { return eTYPE_DISPENSERTILEENTITY; } + static TileEntity *create() { return new DispenserTileEntity(); } + + +using TileEntity::setChanged; + +private: + ItemInstanceArray *items; + Random *random; + +public: + DispenserTileEntity(); + virtual ~DispenserTileEntity(); + + virtual unsigned int getContainerSize(); + virtual shared_ptr getItem(unsigned int slot); + virtual shared_ptr removeItem(unsigned int slot, int count); + shared_ptr removeItemNoUpdate(int slot); + bool removeProjectile(int itemId); + int getRandomSlot(); + virtual void setItem(unsigned int slot, shared_ptr item); + virtual int addItem(shared_ptr item); + virtual int getName(); + virtual void load(CompoundTag *base); + virtual void save(CompoundTag *base); + virtual int getMaxStackSize(); + virtual bool stillValid(shared_ptr player); + virtual void setChanged(); + + void startOpen(); + void stopOpen(); + + // 4J Added + virtual shared_ptr clone(); + void AddItemBack(shared_ptritem, unsigned int slot); +}; \ No newline at end of file diff --git a/Minecraft.World/Distort.cpp b/Minecraft.World/Distort.cpp new file mode 100644 index 00000000..f8fc70c8 --- /dev/null +++ b/Minecraft.World/Distort.cpp @@ -0,0 +1,13 @@ +#include "stdafx.h" +#include "Distort.h" + +Distort::Distort(Synth *source, Synth *distort) +{ + this->source = source; + this->distort = distort; +} + +double Distort::getValue(double x, double y) +{ + return source->getValue(x + distort->getValue(x, y), y); +} diff --git a/Minecraft.World/Distort.h b/Minecraft.World/Distort.h new file mode 100644 index 00000000..a98982ff --- /dev/null +++ b/Minecraft.World/Distort.h @@ -0,0 +1,14 @@ +#pragma once +#include "Synth.h" + +class Distort: public Synth +{ +private: + Synth *source; + Synth *distort; + +public: + Distort(Synth *source, Synth *distort); + + virtual double getValue(double x, double y); +}; \ No newline at end of file diff --git a/Minecraft.World/DoorInfo.cpp b/Minecraft.World/DoorInfo.cpp new file mode 100644 index 00000000..0c61b635 --- /dev/null +++ b/Minecraft.World/DoorInfo.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" + +#include "DoorInfo.h" + +DoorInfo::DoorInfo(int x, int y, int z, int insideDx, int insideDy, int timeStamp) : x(x), y(y), z(z), insideDx(insideDx), insideDz(insideDy) +{ + removed = false; + bookings = 0; + + this->timeStamp = timeStamp; +} + +int DoorInfo::distanceTo(int x2, int y2, int z2) +{ + return (int) sqrt((float)distanceToSqr(x2, y2, z2)); +} + +int DoorInfo::distanceToSqr(int x2, int y2, int z2) +{ + int dx = x2 - x; + int dy = y2 - y; + int dz = z2 - z; + return dx * dx + dy * dy + dz * dz; +} + +int DoorInfo::distanceToInsideSqr(int x2, int y2, int z2) +{ + int dx = x2 - x - insideDx; + int dy = y2 - y; + int dz = z2 - z - insideDz; + return dx * dx + dy * dy + dz * dz; +} + +int DoorInfo::getIndoorX() +{ + return x + insideDx; +} + +int DoorInfo::getIndoorY() +{ + return y; +} + +int DoorInfo::getIndoorZ() +{ + return z + insideDz; +} + +bool DoorInfo::isInsideSide(int testX, int testZ) +{ + int vdx = testX - x; + int vdz = testZ - z; + return vdx * insideDx + vdz * insideDz >= 0; +} + +void DoorInfo::resetBookingCount() +{ + bookings = 0; +} + +void DoorInfo::incBookingCount() +{ + ++bookings; +} + +int DoorInfo::getBookingsCount() +{ + return bookings; +} \ No newline at end of file diff --git a/Minecraft.World/DoorInfo.h b/Minecraft.World/DoorInfo.h new file mode 100644 index 00000000..b9382fcd --- /dev/null +++ b/Minecraft.World/DoorInfo.h @@ -0,0 +1,29 @@ +#pragma once + +class DoorInfo +{ +public: + const int x, y, z; + const int insideDx, insideDz; + int timeStamp; + bool removed; + +private: + // this is used for mobs to see how many mobs are + // present, it's not 100% accurate but may be good enough + int bookings; + +public: + DoorInfo(int x, int y, int z, int insideDx, int insideDy, int timeStamp); + + int distanceTo(int x2, int y2, int z2); + int distanceToSqr(int x2, int y2, int z2); + int distanceToInsideSqr(int x2, int y2, int z2); + int getIndoorX(); + int getIndoorY(); + int getIndoorZ(); + bool isInsideSide(int testX, int testZ); + void resetBookingCount(); + void incBookingCount(); + int getBookingsCount(); +}; \ No newline at end of file diff --git a/Minecraft.World/DoorInteractGoal.cpp b/Minecraft.World/DoorInteractGoal.cpp new file mode 100644 index 00000000..1e3bfe11 --- /dev/null +++ b/Minecraft.World/DoorInteractGoal.cpp @@ -0,0 +1,73 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.pathfinder.h" +#include "net.minecraft.world.level.tile.h" +#include "DoorInteractGoal.h" + +DoorInteractGoal::DoorInteractGoal(Mob *mob) +{ + doorX = doorY = doorZ = 0; + doorTile = NULL; + passed = false; + doorOpenDirX = doorOpenDirZ = 0.0f; + + this->mob = mob; +} + +bool DoorInteractGoal::canUse() +{ + if (!mob->horizontalCollision) return false; + PathNavigation *pathNav = mob->getNavigation(); + Path *path = pathNav->getPath(); + if (path == NULL || path->isDone() || !pathNav->canOpenDoors()) return false; + + for (int i = 0; i < min(path->getIndex() + 2, path->getSize()); ++i) + { + Node *n = path->get(i); + doorX = n->x; + doorY = n->y + 1; + doorZ = n->z; + if (mob->distanceToSqr(doorX, mob->y, doorZ) > 1.5 * 1.5) continue; + doorTile = getDoorTile(doorX, doorY, doorZ); + if (doorTile == NULL) continue; + return true; + } + + doorX = Mth::floor(mob->x); + doorY = Mth::floor(mob->y + 1); + doorZ = Mth::floor(mob->z); + doorTile = getDoorTile(doorX, doorY, doorZ); + return doorTile != NULL; +} + +bool DoorInteractGoal::canContinueToUse() +{ + return !passed; +} + +void DoorInteractGoal::start() +{ + passed = false; + doorOpenDirX = (float) (doorX + 0.5f - mob->x); + doorOpenDirZ = (float) (doorZ + 0.5f - mob->z); +} + +void DoorInteractGoal::tick() +{ + float newDoorDirX = (float) (doorX + 0.5f - mob->x); + float newDoorDirZ = (float) (doorZ + 0.5f - mob->z); + float dot = doorOpenDirX * newDoorDirX + doorOpenDirZ * newDoorDirZ; + if (dot < 0) + { + passed = true; + } +} + +DoorTile *DoorInteractGoal::getDoorTile(int x, int y, int z) +{ + int tileId = mob->level->getTile(x, y, z); + if (tileId != Tile::door_wood_Id) return NULL; + return (DoorTile *) Tile::tiles[tileId]; +} \ No newline at end of file diff --git a/Minecraft.World/DoorInteractGoal.h b/Minecraft.World/DoorInteractGoal.h new file mode 100644 index 00000000..e5041a16 --- /dev/null +++ b/Minecraft.World/DoorInteractGoal.h @@ -0,0 +1,29 @@ +#pragma once + +#include "Goal.h" + +class DoorTile; + +class DoorInteractGoal : public Goal +{ +protected: + Mob *mob; // Owner of this goal + int doorX, doorY, doorZ; + DoorTile *doorTile; + +private: + bool passed; + float doorOpenDirX, doorOpenDirZ; + +public: + DoorInteractGoal(Mob *mob); + virtual ~DoorInteractGoal() {} + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + virtual void tick(); + +private: + DoorTile *getDoorTile(int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/DoorItem.cpp b/Minecraft.World/DoorItem.cpp new file mode 100644 index 00000000..d59ac8d9 --- /dev/null +++ b/Minecraft.World/DoorItem.cpp @@ -0,0 +1,75 @@ +using namespace std; + +#include "stdafx.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.item.h" +#include "Facing.h" +#include "Material.h" +#include "GenericStats.h" +#include "DoorItem.h" + +DoorItem::DoorItem(int id, Material *material) : Item(id) +{ + this->material = material; + maxStackSize = 1; +} + +bool DoorItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + if (face != Facing::UP) return false; + y++; + + Tile *tile; + + if (material == Material::wood) tile = Tile::door_wood; + else tile = Tile::door_iron; + + if (!player->mayBuild(x, y, z) || !player->mayBuild(x, y + 1, z)) return false; + if (!tile->mayPlace(level, x, y, z)) return false; + + // 4J-PB - Adding a test only version to allow tooltips to be displayed + if(bTestUseOnOnly) return true; + + // 4J-JEV: Hook for durango 'BlockPlaced' event. + player->awardStat(GenericStats::blocksPlaced(tile->id),GenericStats::param_blocksPlaced(tile->id,instance->getAuxValue(),1)); + + int dir = Mth::floor(((player->yRot + 180) * 4) / 360 - 0.5) & 3; + place(level, x, y, z, dir, tile); + + instance->count--; + return true; +} + +void DoorItem::place(Level *level, int x, int y, int z, int dir, Tile *tile) +{ + + int xra = 0; + int zra = 0; + if (dir == 0) zra = +1; + if (dir == 1) xra = -1; + if (dir == 2) zra = -1; + if (dir == 3) xra = +1; + + + int solidLeft = (level->isSolidBlockingTile(x - xra, y, z - zra) ? 1 : 0) + (level->isSolidBlockingTile(x - xra, y + 1, z - zra) ? 1 : 0); + int solidRight = (level->isSolidBlockingTile(x + xra, y, z + zra) ? 1 : 0) + (level->isSolidBlockingTile(x + xra, y + 1, z + zra) ? 1 : 0); + + bool doorLeft = (level->getTile(x - xra, y, z - zra) == tile->id) || (level->getTile(x - xra, y + 1, z - zra) == tile->id); + bool doorRight = (level->getTile(x + xra, y, z + zra) == tile->id) || (level->getTile(x + xra, y + 1, z + zra) == tile->id); + + bool flip = false; + if (doorLeft && !doorRight) flip = true; + else if (solidRight > solidLeft) flip = true; + + level->noNeighborUpdate = true; + level->setTileAndData(x, y, z, tile->id, dir); + level->setTileAndData(x, y + 1, z, tile->id, 8 | (flip ? 1 : 0)); + level->noNeighborUpdate = false; + level->updateNeighborsAt(x, y, z, tile->id); + level->updateNeighborsAt(x, y + 1, z, tile->id); +} + diff --git a/Minecraft.World/DoorItem.h b/Minecraft.World/DoorItem.h new file mode 100644 index 00000000..843c768b --- /dev/null +++ b/Minecraft.World/DoorItem.h @@ -0,0 +1,20 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class Player; +class Material; +class Level; + +class DoorItem : public Item +{ +private: + Material *material; + +public: + DoorItem(int id, Material *material); + + virtual bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + static void place(Level *level, int x, int y, int z, int dir, Tile *tile); +}; diff --git a/Minecraft.World/DoorTile.cpp b/Minecraft.World/DoorTile.cpp new file mode 100644 index 00000000..be31be27 --- /dev/null +++ b/Minecraft.World/DoorTile.cpp @@ -0,0 +1,341 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "DoorTile.h" +#include "LevelEvent.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.h" +#include "net.minecraft.h" + +const wstring DoorTile::TEXTURES[] = { L"doorWood_lower", L"doorWood_upper", L"doorIron_lower", L"doorIron_upper" }; + +DoorTile::DoorTile(int id, Material *material) : Tile(id, material,isSolidRender()) +{ + icons = NULL; + + if (material == Material::metal) + { + texBase = 2; + } + else + { + texBase = 0; + } + + float r = 0.5f; + float h = 1.0f; + Tile::setShape(0.5f - r, 0, 0.5f - r, 0.5f + r, h, 0.5f + r); +} + +Icon *DoorTile::getTexture(int face, int data) +{ + return icons[texBase]; +} + +Icon *DoorTile::getTexture(LevelSource *level, int x, int y, int z, int face) +{ + if (face == Facing::UP || face == Facing::DOWN) return icons[texBase]; + + int compositeData = getCompositeData(level, x, y, z); + int dir = compositeData & C_DIR_MASK; + bool isOpen = (compositeData & C_OPEN_MASK) != 0; + bool flip = false; + bool upper = (compositeData & C_IS_UPPER_MASK) != 0; + + if (isOpen) + { + if (dir == 0 && face == 2) flip = !flip; + else if (dir == 1 && face == 5) flip = !flip; + else if (dir == 2 && face == 3) flip = !flip; + else if (dir == 3 && face == 4) flip = !flip; + } + else + { + if (dir == 0 && face == 5) flip = !flip; + else if (dir == 1 && face == 3) flip = !flip; + else if (dir == 2 && face == 4) flip = !flip; + else if (dir == 3 && face == 2) flip = !flip; + if ((compositeData & C_RIGHT_HINGE_MASK) != 0) flip = !flip; + } + + return icons[texBase + (flip ? DOOR_TILE_TEXTURE_COUNT : 0) + (upper ? 1 : 0)]; +} + +void DoorTile::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[DOOR_TILE_TEXTURE_COUNT * 2]; + + for (int i = 0; i < DOOR_TILE_TEXTURE_COUNT; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURES[i]); + icons[i + DOOR_TILE_TEXTURE_COUNT] = new FlippedIcon(icons[i], true, false); + } +} + +bool DoorTile::blocksLight() +{ + return false; +} + +bool DoorTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool DoorTile::isCubeShaped() +{ + return false; +} + +int DoorTile::getRenderShape() +{ + return Tile::SHAPE_DOOR; +} + +AABB *DoorTile::getTileAABB(Level *level, int x, int y, int z) +{ + updateShape(level, x, y, z); + AABB *retval = Tile::getTileAABB(level, x, y, z); + return retval; +} + +AABB *DoorTile::getAABB(Level *level, int x, int y, int z) +{ + updateShape(level, x, y, z); + AABB *retval = Tile::getAABB(level, x, y, z); + return retval; +} + +void DoorTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + setShape(getCompositeData(level,x, y, z)); +} + +int DoorTile::getDir(LevelSource *level, int x, int y, int z) +{ + return getCompositeData(level, x, y, z) & C_DIR_MASK; +} + +bool DoorTile::isOpen(LevelSource *level, int x, int y, int z) +{ + return (getCompositeData(level, x, y, z) & C_OPEN_MASK) != 0; +} + +void DoorTile::setShape(int compositeData) +{ + float r = 3 / 16.0f; + Tile::setShape(0, 0, 0, 1, 2, 1); + int dir = compositeData & C_DIR_MASK; + bool open = (compositeData & C_OPEN_MASK) != 0; + bool hasRightHinge = (compositeData & C_RIGHT_HINGE_MASK) != 0; + if (dir == 0) + { + if (open) + { + if (!hasRightHinge) setShape(0, 0, 0, 1, 1, r); + else setShape(0, 0, 1 - r, 1, 1, 1); + } + else setShape(0, 0, 0, r, 1, 1); + } + else if (dir == 1) + { + if (open) + { + if (!hasRightHinge) setShape(1 - r, 0, 0, 1, 1, 1); + else setShape(0, 0, 0, r, 1, 1); + } + else setShape(0, 0, 0, 1, 1, r); + } + else if (dir == 2) + { + if (open) + { + if (!hasRightHinge) setShape(0, 0, 1 - r, 1, 1, 1); + else setShape(0, 0, 0, 1, 1, r); + } + else setShape(1 - r, 0, 0, 1, 1, 1); + } + else if (dir == 3) + { + if (open) + { + if (!hasRightHinge) setShape(0, 0, 0, r, 1, 1); + else setShape(1 - r, 0, 0, 1, 1, 1); + } + else setShape(0, 0, 1 - r, 1, 1, 1); + } +} + +void DoorTile::attack(Level *level, int x, int y, int z, shared_ptr player) +{ + // Fix for #92957 - TU11: Content: Multiplayer: Wooden Doors splits in half and glitch in open / close motion while being mined. + // In lastest PC version this is commented out, so do that now to fix bug above + //use(level, x, y, z, player); +} + +// 4J-PB - Adding a TestUse for tooltip display +bool DoorTile::TestUse() +{ + return true; +} + +bool DoorTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if(soundOnly) + { + // 4J - added - just do enough to play the sound + if (material != Material::metal) + { + level->levelEvent(player, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); + } + return false; + } + + if (material == Material::metal) return true; + + int compositeData = getCompositeData(level, x, y, z); + int lowerData = compositeData & C_LOWER_DATA_MASK; + lowerData ^= 4; + if ((compositeData & C_IS_UPPER_MASK) == 0) + { + level->setData(x, y, z, lowerData);//, Tile.UPDATE_CLIENTS); + level->setTilesDirty(x, y, z, x, y, z); + } + else + { + level->setData(x, y - 1, z, lowerData);//, Tile.UPDATE_CLIENTS); + level->setTilesDirty(x, y - 1, z, x, y, z); + } + + level->levelEvent(player, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); + return true; +} + +void DoorTile::setOpen(Level *level, int x, int y, int z, bool shouldOpen) +{ + int compositeData = getCompositeData(level, x, y, z); + bool isOpen = (compositeData & C_OPEN_MASK) != 0; + if (isOpen == shouldOpen) return; + + int lowerData = compositeData & C_LOWER_DATA_MASK; + lowerData ^= 4; + if ((compositeData & C_IS_UPPER_MASK) == 0) + { + level->setData(x, y, z, lowerData);//, Tile.UPDATE_CLIENTS); + level->setTilesDirty(x, y, z, x, y, z); + } + else + { + level->setData(x, y - 1, z, lowerData);//, Tile.UPDATE_CLIENTS); + level->setTilesDirty(x, y - 1, z, x, y, z); + } + + level->levelEvent(nullptr, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); +} + +void DoorTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + int data = level->getData(x, y, z); + if ((data & UPPER_BIT) == 0) + { + bool spawn = false; + if (level->getTile(x, y + 1, z) != id) + { + level->setTile(x, y, z, 0); + spawn = true; + } + if (!level->isSolidBlockingTile(x, y - 1, z)) + { + level->setTile(x, y, z, 0); + spawn = true; + if (level->getTile(x, y + 1, z) == id) + { + level->setTile(x, y + 1, z, 0); + } + } + if (spawn) + { + if (!level->isClientSide) + { + spawnResources(level, x, y, z, data, 0); + } + } + else + { + bool signal = level->hasNeighborSignal(x, y, z) || level->hasNeighborSignal(x, y + 1, z); + if ((signal || (type > 0 && Tile::tiles[type]->isSignalSource())) && type != id) + { + setOpen(level, x, y, z, signal); + } + } + } + else + { + if (level->getTile(x, y - 1, z) != id) + { + level->setTile(x, y, z, 0); + } + if (type > 0 && type != id) + { + neighborChanged(level, x, y - 1, z, type); + } + } +} + +int DoorTile::getResource(int data, Random *random, int playerBonusLevel) +{ + if ((data & 8) != 0) return 0; + if (material == Material::metal) return Item::door_iron->id; + return Item::door_wood->id; +} + +HitResult *DoorTile::clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b) +{ + updateShape(level, xt, yt, zt); + return Tile::clip(level, xt, yt, zt, a, b); +} + +bool DoorTile::mayPlace(Level *level, int x, int y, int z) +{ + if (y >= Level::maxBuildHeight - 1) return false; + + return (level->isTopSolidBlocking(x, y - 1, z) && Tile::mayPlace(level, x, y, z) && Tile::mayPlace(level, x, y + 1, z)); +} + +bool DoorTile::isOpen(int data) +{ + return (data & 4) != 0; +} + +int DoorTile::getPistonPushReaction() +{ + return Material::PUSH_DESTROY; +} + +int DoorTile::getCompositeData(LevelSource *level, int x, int y, int z) +{ + int data = level->getData(x, y, z); + bool isUpper = (data & UPPER_BIT) != 0; + int lowerData; + int upperData; + if (isUpper) + { + lowerData = level->getData(x, y - 1, z); + upperData = data; + } + else + { + lowerData = data; + upperData = level->getData(x, y + 1, z); + } + + // bits: dir, dir, open/closed, isUpper, isRightHinge + bool isRightHinge = (upperData & 1) != 0; + return (lowerData & C_LOWER_DATA_MASK) | (isUpper ? 8 : 0) | (isRightHinge ? 16 : 0); +} + +int DoorTile::cloneTileId(Level *level, int x, int y, int z) +{ + return material == Material::metal ? Item::door_iron_Id : Item::door_wood_Id; +} diff --git a/Minecraft.World/DoorTile.h b/Minecraft.World/DoorTile.h new file mode 100644 index 00000000..59a8b414 --- /dev/null +++ b/Minecraft.World/DoorTile.h @@ -0,0 +1,60 @@ +#pragma once +#include "Tile.h" +#include "Definitions.h" + +class Player; +class HitResult; +class ChunkRebuildData; + +class DoorTile : public Tile +{ + friend class Tile; + friend class ChunkRebuildData; +public: + static const int UPPER_BIT = 8; + static const int C_DIR_MASK = 3; + static const int C_OPEN_MASK = 4; + static const int C_LOWER_DATA_MASK = 7; + static const int C_IS_UPPER_MASK = 8; + static const int C_RIGHT_HINGE_MASK = 16; + +private: + static const int DOOR_TILE_TEXTURE_COUNT = 4; + static const wstring TEXTURES[]; + int texBase; + Icon **icons; + +protected: + DoorTile(int id, Material *material); +public: + virtual Icon *getTexture(int face, int data); + //@Override + Icon *getTexture(LevelSource *level, int x, int y, int z, int face); + //@Override + void registerIcons(IconRegister *iconRegister); + virtual bool blocksLight(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual int getRenderShape(); + virtual AABB *getTileAABB(Level *level, int x, int y, int z); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + int getDir(LevelSource *level, int x, int y, int z); + bool isOpen(LevelSource *level, int x, int y, int z); +private: + using Tile::setShape; + virtual void setShape(int compositeData); +public: + virtual void attack(Level *level, int x, int y, int z, shared_ptr player); + virtual bool TestUse(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + void setOpen(Level *level, int x, int y, int z, bool shouldOpen); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual HitResult *clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b); + virtual bool mayPlace(Level *level, int x, int y, int z); + static bool isOpen(int data); + virtual int getPistonPushReaction(); + int getCompositeData(LevelSource *level, int x, int y, int z); + virtual int cloneTileId(Level *level, int x, int y, int z); +}; diff --git a/Minecraft.World/DoubleTag.h b/Minecraft.World/DoubleTag.h new file mode 100644 index 00000000..130c3ebb --- /dev/null +++ b/Minecraft.World/DoubleTag.h @@ -0,0 +1,37 @@ +#pragma once +#include "InputOutputStream.h" +#include "Tag.h" + +class DoubleTag : public Tag +{ +public: + double data; + DoubleTag(const wstring &name) : Tag(name) {} + DoubleTag(const wstring &name, double data) : Tag(name) {this->data = data; } + + void write(DataOutput *dos) { dos->writeDouble(data); } + void load(DataInput *dis) { data = dis->readDouble(); } + + byte getId() { return TAG_Double; } + wstring toString() + { + static wchar_t buf[32]; + swprintf(buf,32,L"%f",data); + return wstring( buf ); + } + + Tag *copy() + { + return new DoubleTag(getName(), data); + } + + bool equals(Tag *obj) + { + if (Tag::equals(obj)) + { + DoubleTag *o = (DoubleTag *) obj; + return data == o->data; + } + return false; + } +}; \ No newline at end of file diff --git a/Minecraft.World/DownfallLayer.cpp b/Minecraft.World/DownfallLayer.cpp new file mode 100644 index 00000000..30775218 --- /dev/null +++ b/Minecraft.World/DownfallLayer.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.newbiome.layer.h" + +DownfallLayer::DownfallLayer(shared_ptrparent) : Layer(0) +{ + this->parent = parent; +} + +intArray DownfallLayer::getArea(int xo, int yo, int w, int h) +{ + intArray b = parent->getArea(xo, yo, w, h); + + intArray result = IntCache::allocate(w * h); + for (int i = 0; i < w * h; i++) + { + result[i] = Biome::biomes[b[i]]->getDownfallInt(); + } + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/DownfallLayer.h b/Minecraft.World/DownfallLayer.h new file mode 100644 index 00000000..e708253c --- /dev/null +++ b/Minecraft.World/DownfallLayer.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Layer.h" + +class DownfallLayer : public Layer +{ +public: + DownfallLayer(shared_ptrparent); + intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/DownfallMixerLayer.cpp b/Minecraft.World/DownfallMixerLayer.cpp new file mode 100644 index 00000000..fcca5bdd --- /dev/null +++ b/Minecraft.World/DownfallMixerLayer.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.newbiome.layer.h" + +DownfallMixerLayer::DownfallMixerLayer(shared_ptrdownfall, shared_ptrparent, int layer) : Layer(0) +{ + this->parent = parent; + this->downfall = downfall; + this->layer = layer; +} + +intArray DownfallMixerLayer::getArea(int xo, int yo, int w, int h) +{ + intArray b = parent->getArea(xo, yo, w, h); + intArray d = downfall->getArea(xo, yo, w, h); + + intArray result = IntCache::allocate(w * h); + for (int i = 0; i < w * h; i++) + { + result[i] = d[i] + (Biome::biomes[b[i]]->getDownfallInt() - d[i]) / (layer + 1); + } + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/DownfallMixerLayer.h b/Minecraft.World/DownfallMixerLayer.h new file mode 100644 index 00000000..58e6151d --- /dev/null +++ b/Minecraft.World/DownfallMixerLayer.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Layer.h" + +class DownfallMixerLayer : public Layer +{ +private: + shared_ptr downfall; + int layer; + +public: + DownfallMixerLayer(shared_ptr downfall, shared_ptr parent, int layer); + intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/DragonFireball.cpp b/Minecraft.World/DragonFireball.cpp new file mode 100644 index 00000000..e3b5d579 --- /dev/null +++ b/Minecraft.World/DragonFireball.cpp @@ -0,0 +1,86 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.h" +#include "JavaMath.h" +#include "DragonFireball.h" + + + +const double DragonFireball::SPLASH_RANGE = 4.0; +const double DragonFireball::SPLASH_RANGE_SQ = DragonFireball::SPLASH_RANGE * DragonFireball::SPLASH_RANGE; + +DragonFireball::DragonFireball(Level *level) : Fireball(level) +{ + setSize(5 / 16.0f, 5 / 16.0f); +} + +DragonFireball::DragonFireball(Level *level, shared_ptr mob, double xa, double ya, double za) : Fireball(level, mob, xa, ya, za) +{ + setSize(5 / 16.0f, 5 / 16.0f); +} + +DragonFireball::DragonFireball(Level *level, double x, double y, double z, double xa, double ya, double za) : Fireball(level, x, y, z, xa, ya, za) +{ + setSize(5 / 16.0f, 5 / 16.0f); +} + +void DragonFireball::onHit(HitResult *res) +{ + if (!level->isClientSide) + { + AABB *aoe = bb->grow(SPLASH_RANGE, SPLASH_RANGE / 2, SPLASH_RANGE); + vector > *entitiesOfClass = level->getEntitiesOfClass(typeid(Mob), aoe); + + if (entitiesOfClass != NULL && !entitiesOfClass->empty()) + { + //for (Entity e : entitiesOfClass) + for( AUTO_VAR(it, entitiesOfClass->begin()); it != entitiesOfClass->end(); ++it) + { + //shared_ptr e = *it; + shared_ptr e = dynamic_pointer_cast( *it ); + double dist = distanceToSqr(e); + if (dist < SPLASH_RANGE_SQ) + { + double scale = 1.0 - (sqrt(dist) / SPLASH_RANGE); + if (e == res->entity) + { + scale = 1; + } + e->hurt(DamageSource::dragonbreath, 8*scale); + } + } + } + delete entitiesOfClass; + level->levelEvent(LevelEvent::ENDERDRAGON_FIREBALL_SPLASH, (int) Math::round(x), (int) Math::round(y), (int) Math::round(z), 0); + + remove(); + } +} + +bool DragonFireball::isPickable() +{ + return false; +} + +bool DragonFireball::hurt(DamageSource *source, int damage) +{ + return false; +} + +bool DragonFireball::shouldBurn() +{ + return false; +} + +int DragonFireball::getIcon() +{ + return 15 + 14 * 16; +} + +ePARTICLE_TYPE DragonFireball::getTrailParticleType() +{ + return eParticleType_dragonbreath; +} \ No newline at end of file diff --git a/Minecraft.World/DragonFireball.h b/Minecraft.World/DragonFireball.h new file mode 100644 index 00000000..9ce199ed --- /dev/null +++ b/Minecraft.World/DragonFireball.h @@ -0,0 +1,37 @@ +#pragma once + +#include "Fireball.h" + +class HitResult; + +class DragonFireball : public Fireball +{ +public: + eINSTANCEOF GetType() { return eTYPE_DRAGON_FIREBALL; } + static Entity *create(Level *level) { return new DragonFireball(level); } + +public: + static const double SPLASH_RANGE; + +private: + static const double SPLASH_RANGE_SQ; + +public: + DragonFireball(Level *level); + DragonFireball(Level *level, shared_ptr mob, double xa, double ya, double za); + DragonFireball(Level *level, double x, double y, double z, double xa, double ya, double za); + +protected: + virtual void onHit(HitResult *res); + +public: + virtual bool isPickable(); + virtual bool hurt(DamageSource *source, int damage); + + virtual bool shouldBurn(); + virtual int getIcon(); + +protected: +// 4J Added TU9 + virtual ePARTICLE_TYPE getTrailParticleType(); +}; \ No newline at end of file diff --git a/Minecraft.World/DungeonFeature.cpp b/Minecraft.World/DungeonFeature.cpp new file mode 100644 index 00000000..cefc49ca --- /dev/null +++ b/Minecraft.World/DungeonFeature.cpp @@ -0,0 +1,191 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "DungeonFeature.h" +#include "net.minecraft.world.level.tile.h" + +void DungeonFeature::addRoom(int xOffs, int zOffs, byteArray blocks, double xRoom, double yRoom, double zRoom) +{ + addTunnel(xOffs, zOffs, blocks, xRoom, yRoom, zRoom, 1 + random->nextFloat() * 6, 0, 0, -1, -1, 0.5); +} + +void DungeonFeature::addTunnel(int xOffs, int zOffs, byteArray blocks, double xCave, double yCave, double zCave, float thickness, float yRot, float xRot, int step, int dist, double yScale) +{ + double xMid = xOffs * 16 + 8; + double zMid = zOffs * 16 + 8; + + float yRota = 0; + float xRota = 0; + Random *random = new Random(this->random->nextLong()); + + if (dist <= 0) + { + int max = radius * 16 - 16; + dist = max - random->nextInt(max / 4); + } + bool singleStep = false; + + if (step == -1) + { + step = dist / 2; + singleStep = true; + } + + + int splitPoint = random->nextInt(dist / 2) + dist / 4; + bool steep = random->nextInt(6) == 0; + + for (; step < dist; step++) + { + double rad = 1.5 + (Mth::sin(step * PI / dist) * thickness) * 1; + double yRad = rad * yScale; + + float xc = Mth::cos(xRot); + float xs = Mth::sin(xRot); + xCave += Mth::cos(yRot) * xc; + yCave += xs; + zCave += Mth::sin(yRot) * xc; + + if (steep) + { + xRot *= 0.92f; + } + else + { + xRot *= 0.7f; + } + xRot += xRota * 0.1f; + yRot += yRota * 0.1f; + + xRota *= 0.90f; + yRota *= 0.75f; + xRota += (random->nextFloat() - random->nextFloat()) * random->nextFloat() * 2; + yRota += (random->nextFloat() - random->nextFloat()) * random->nextFloat() * 4; + + + if (!singleStep && step == splitPoint && thickness > 1) + { + addTunnel(xOffs, zOffs, blocks, xCave, yCave, zCave, random->nextFloat() * 0.5f + 0.5f, yRot - PI / 2, xRot / 3, step, dist, 1.0); + addTunnel(xOffs, zOffs, blocks, xCave, yCave, zCave, random->nextFloat() * 0.5f + 0.5f, yRot + PI / 2, xRot / 3, step, dist, 1.0); + return; + } + if (!singleStep && random->nextInt(4) == 0) continue; + + { + double xd = xCave - xMid; + double zd = zCave - zMid; + double remaining = dist - step; + double rr = (thickness + 2) + 16; + if (xd * xd + zd * zd - (remaining * remaining) > rr * rr) + { + return; + } + } + + if (xCave < xMid - 16 - rad * 2 || zCave < zMid - 16 - rad * 2 || xCave > xMid + 16 + rad * 2 || zCave > zMid + 16 + rad * 2) continue; + + int x0 = Mth::floor(xCave - rad) - xOffs * 16 - 1; + int x1 = Mth::floor(xCave + rad) - xOffs * 16 + 1; + + int y0 = Mth::floor(yCave - yRad) - 1; + int y1 = Mth::floor(yCave + yRad) + 1; + + int z0 = Mth::floor(zCave - rad) - zOffs * 16 - 1; + int z1 = Mth::floor(zCave + rad) - zOffs * 16 + 1; + + if (x0 < 0) x0 = 0; + if (x1 > 16) x1 = 16; + + if (y0 < 1) y0 = 1; + if (y1 > Level::genDepth - 8) y1 = Level::genDepth - 8; + + if (z0 < 0) z0 = 0; + if (z1 > 16) z1 = 16; + + bool detectedWater = false; + for (int xx = x0; !detectedWater && xx < x1; xx++) + { + for (int zz = z0; !detectedWater && zz < z1; zz++) + { + for (int yy = y1 + 1; !detectedWater && yy >= y0 - 1; yy--) + { + int p = (xx * 16 + zz) * Level::genDepth + yy; + if (yy < 0 || yy >= Level::genDepth) continue; + if (blocks[p] == Tile::water_Id || blocks[p] == Tile::calmWater_Id) + { + detectedWater = true; + } + if (yy != y0 - 1 && xx != x0 && xx != x1 - 1 && zz != z0 && zz != z1 - 1) + { + yy = y0; + } + } + } + } + if (detectedWater) continue; + + for (int xx = x0; xx < x1; xx++) + { + double xd = ((xx + xOffs * 16 + 0.5) - xCave) / rad; + for (int zz = z0; zz < z1; zz++) + { + double zd = ((zz + zOffs * 16 + 0.5) - zCave) / rad; + int p = (xx * 16 + zz) * Level::genDepth + y1; + bool hasGrass = false; + for (int yy = y1 - 1; yy >= y0; yy--) + { + double yd = (yy + 0.5 - yCave) / yRad; + if (yd > -0.7 && xd * xd + yd * yd + zd * zd < 1) + { + int block = blocks[p]; + if (block == Tile::grass_Id) hasGrass = true; + if (block == Tile::rock_Id || block == Tile::dirt_Id || block == Tile::grass_Id) + { + if (yy < 10) + { + blocks[p] = (byte) Tile::lava_Id; + } + else + { + blocks[p] = (byte) 0; + if (hasGrass && blocks[p - 1] == Tile::dirt_Id) blocks[p - 1] = (byte) Tile::grass_Id; + } + } + } + p--; + } + } + } + if (singleStep) break; + } + +} + +void DungeonFeature::addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks) +{ + int caves = random->nextInt(random->nextInt(random->nextInt(40) + 1) + 1); + if (random->nextInt(15) != 0) caves = 0; + + for (int cave = 0; cave < caves; cave++) + { + double xCave = x * 16 + random->nextInt(16); + double yCave = random->nextInt(random->nextInt(Level::genDepth - 8) + 8); + double zCave = z * 16 + random->nextInt(16); + + int tunnels = 1; + if (random->nextInt(4) == 0) + { + addRoom(xOffs, zOffs, blocks, xCave, yCave, zCave); + tunnels += random->nextInt(4); + } + + for (int i = 0; i < tunnels; i++) + { + + float yRot = random->nextFloat() * PI * 2; + float xRot = ((random->nextFloat() - 0.5f) * 2) / 8; + float thickness = random->nextFloat() * 2 + random->nextFloat(); + + addTunnel(xOffs, zOffs, blocks, xCave, yCave, zCave, thickness, yRot, xRot, 0, 0, 1.0); + } + } +} \ No newline at end of file diff --git a/Minecraft.World/DungeonFeature.h b/Minecraft.World/DungeonFeature.h new file mode 100644 index 00000000..488ca59c --- /dev/null +++ b/Minecraft.World/DungeonFeature.h @@ -0,0 +1,12 @@ +#pragma once +#include "LargeFeature.h" + +class Level; + +class DungeonFeature : public LargeFeature +{ + void addRoom(int xOffs, int zOffs, byteArray blocks, double xRoom, double yRoom, double zRoom); + void addTunnel(int xOffs, int zOffs, byteArray blocks, double xCave, double yCave, double zCave, float thickness, float yRot, float xRot, int step, int dist, double yScale); + + virtual void addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks); +}; diff --git a/Minecraft.World/DurangoStats.cpp b/Minecraft.World/DurangoStats.cpp new file mode 100644 index 00000000..c9b51c84 --- /dev/null +++ b/Minecraft.World/DurangoStats.cpp @@ -0,0 +1,1221 @@ +#include "stdafx.h" + +#include "ItemStat.h" + +#include "Achievement.h" +#include "Achievements.h" + +#include "DamageSource.h" +#include "Player.h" +#include "ItemInstance.h" +#include "Tile.h" +#include "Item.h" +#include "Level.h" + +#include "..\Minecraft.Client\Minecraft.h" +#include "LevelData.h" +#include "LevelSettings.h" + +#include "..\Minecraft.Client\LocalPlayer.h" +#include "..\Minecraft.Client\MultiPlayerLocalPlayer.h" + +#include "EntityIO.h" + +#include "..\Minecraft.Client\Durango\ServiceConfig\Events-XBLA.8-149E11AEEvents.h" + +#include "DurangoStats.h" + + /////////////////// + // Ds Item Event // + /////////////////// + +string DsItemEvent::nameMethods[] = { + "NONE", "itemPickedUp", "itemCrafted", + "itemTakenFromChest", "itemTakenFromEnderchest", + "itemBought", "itemSmithed", "blockMined", "blockPlaced", "MAX" +}; + +DsItemEvent::DsItemEvent(int id, const wstring &name) : Stat(id,name) {} + +bool DsItemEvent::onLeaderboard(ELeaderboardId leaderboard, eAcquisitionMethod methodId, Param *param) +{ + switch (methodId) + { + case eAcquisitionMethod_Pickedup: + switch (param->itemId) + { + case Item::egg_Id: + case Tile::mushroom1_Id: + case Tile::mushroom2_Id: + return leaderboard == eLeaderboardId_FARMING; + } + break; + + case eAcquisitionMethod_Mined: + switch (param->itemId) + { + case Tile::dirt_Id: + case Tile::stoneBrick_Id: + case Tile::sand_Id: + case Tile::rock_Id: + case Tile::gravel_Id: + case Tile::clay_Id: + case Tile::obsidian_Id: + return leaderboard == eLeaderboardId_MINING; + + case Tile::crops_Id: + case Tile::pumpkin_Id: + case Tile::reeds_Id: + return leaderboard == eLeaderboardId_FARMING; + } + break; + } + + return false; +} + + +// 4J-JEV, for tiles/items we want to record stats together. +int DsItemEvent::mergeIds(int itemId) +{ + switch (itemId) + { + default: + return itemId; + + case Tile::mushroom1_Id: + case Tile::mushroom2_Id: + return Tile::mushroom1_Id; + + case Tile::dirt_Id: + case Tile::grass_Id: + case Tile::farmland_Id: + return Tile::dirt_Id; + + case Tile::redstoneLight_Id: + case Tile::redstoneLight_lit_Id: + return Tile::redstoneLight_Id; + + case Tile::redStoneOre_Id: + case Tile::redStoneOre_lit_Id: + return Tile::redStoneOre_Id; + } +} + +void DsItemEvent::handleParamBlob(shared_ptr player, byteArray paramBlob) +{ + if (paramBlob.length == sizeof(Param)) + { + Param *param = (Param *) paramBlob.data; + + // Combine block ids. + param->itemId = mergeIds( param->itemId ); + + // Send farming leaderboard updates. + if ( (param->methodId == eAcquisitionMethod_Pickedup && onLeaderboard(eLeaderboardId_FARMING, eAcquisitionMethod_Pickedup, param)) + || (param->methodId == eAcquisitionMethod_Mined && onLeaderboard(eLeaderboardId_FARMING, eAcquisitionMethod_Mined, param)) ) + { + EventWriteLeaderboardTotals( + DurangoStats::getUserId(player), //UserId + DurangoStats::getPlayerSession(), // PlayerSessionId + player->level->difficulty, // Difficulty, + eLeaderboardId_FARMING, // ScoreboardId + param->itemCount); + + app.DebugPrintf("<%ls>\tscoreboardFarming(%i:%i:%i)\n", DurangoStats::getUserId(player), + player->level->difficulty, eLeaderboardId_FARMING, param->itemCount); + } + + // Send mining leaderboard updates. + if ( param->methodId == eAcquisitionMethod_Mined && onLeaderboard(eLeaderboardId_MINING, eAcquisitionMethod_Mined, param) ) + { + EventWriteLeaderboardTotals( + DurangoStats::getUserId(player), //UserId + DurangoStats::getPlayerSession(), // PlayerSessionId + player->level->difficulty, // Difficulty, + eLeaderboardId_MINING, // ScoreboardId + param->itemCount); + app.DebugPrintf("<%ls>\tscoreboardMining(%i:%i:%i)\n", DurangoStats::getUserId(player), + player->level->difficulty, eLeaderboardId_MINING, param->itemCount); + } + + // Debug printout. + string method = nameMethods[(int)param->methodId]; + app.DebugPrintf("<%ls>\t%s(%i:%i:%i)\n", DurangoStats::getUserId(player), + method.c_str(), param->itemId, param->itemAux, param->itemCount); + + // Split on acquisition method, then send relevant events. + if (param->methodId == eAcquisitionMethod_Placed) + { + EventWriteBlockPlaced( + DurangoStats::getUserId(player), + DurangoStats::getPlayerSession(), + player->level->difficulty, + param->itemId, + param->itemAux, + param->itemCount + ); + } + else if (param->methodId == eAcquisitionMethod_Mined) + { + EventWriteBlockBroken( + DurangoStats::getUserId(player), + DurangoStats::getPlayerSession(), + player->level->difficulty, + param->itemId, + param->itemAux, + param->itemCount + ); + } + else + { + EventWriteMcItemAcquired( + DurangoStats::getUserId(player), + 0, // TODO + DurangoStats::getPlayerSession(), + 0, + 0, + player->level->difficulty, + param->itemId, + param->methodId, + 0, 0, 0, // (x,y,z) + param->itemAux, + param->itemCount + ); + } + } +} + +/* 4J-JEV: note that mined events will only fire with 'Instant_Mine' on if you are carrying an appropriate + * tool for the block that you are mining. + */ +byteArray DsItemEvent::createParamBlob(eAcquisitionMethod eMethod, int itemId, int itemAux, int itemCount) +{ + byteArray output; + Param param = { eMethod, itemId, itemAux, itemCount }; + output.data = (byte *) new Param(param); + output.length = sizeof(Param); + return output; +} + + + /////////////////// + // Ds Mob Killed // + /////////////////// + +DsMobKilled::DsMobKilled(int id, const wstring &name) : Stat(id,name) {} + +void DsMobKilled::handleParamBlob(shared_ptr player, byteArray paramBlob) +{ + if (paramBlob.length == sizeof(Param)) + { + Param *param = (Param *) paramBlob.data; + + if (param->mobType < 0) return; + + if (EventWriteMobKilled(DurangoStats::getUserId(player), + 0, + DurangoStats::getPlayerSession(), + DurangoStats::getMultiplayerCorrelationId(), + 0, + player->level->difficulty, + DurangoStats::getPlayerSession(), // ROUND ID + 0, + param->weaponId, + param->mobType, + param->isRanged?1:0, + 0, 0, 0, // (x,y,z), + 0, + param->distance, + param->mobType) + == 0) + { + app.DebugPrintf("<%ls>\t%s(%i:%i:%i:%i)\n", DurangoStats::getUserId(player), + (param->isRanged?"mobShotWithEntity":"mobKilledInMelee"), + param->mobType, param->weaponId, param->distance, param->damage); + } + + switch(EntityIO::getClass(param->mobType)) + { + case eTYPE_MONSTER: + if(param->mobType != SPIDER_JOCKEY_ID) break; + // Fallthrough is spider jockey + case eTYPE_ZOMBIE: + case eTYPE_SKELETON: + case eTYPE_CREEPER: + case eTYPE_SPIDER: + case eTYPE_PIGZOMBIE: + case eTYPE_SLIME: + + if (EventWriteLeaderboardTotals( + DurangoStats::getUserId(player), //UserId + DurangoStats::getPlayerSession(), // PlayerSessionId + player->level->difficulty, // Difficulty, + eLeaderboardId_KILLING, // ScoreboardId + 1) // Count + == 0) + { + app.DebugPrintf("<%ls>\tscoreboardKills(%i:%i:1)\n", DurangoStats::getUserId(player), + player->level->difficulty, eLeaderboardId_KILLING); + } + } + } +} + +byteArray DsMobKilled::createParamBlob(shared_ptr player, shared_ptr mob, DamageSource *dmgSrc) +{ + // 4J-JEV: Get the id we use for Durango Server Stats. + int mob_networking_id; + eINSTANCEOF mobEType = mob->GetType(); + if ( (mobEType == eTYPE_SPIDER) && (mob->rider.lock() != NULL) && (mob->rider.lock()->GetType() == eTYPE_SKELETON) ) + { + mob_networking_id = SPIDER_JOCKEY_ID; // Spider jockey only a concept for leaderboards. + } + else if ( (mobEType == eTYPE_SKELETON) && (mob->riding != NULL) && (mob->riding->GetType() == eTYPE_SPIDER) ) + { + mob_networking_id = SPIDER_JOCKEY_ID; // Spider jockey only a concept for leaderboards. + } + else + { + mob_networking_id = EntityIO::eTypeToIoid(mobEType); + } + + // Kill made with projectile, arrow/ghast/fireball/snowball. + // NB: Snowball kills would make an awesome achievement. ("Not a snowball's chance...") + if ( dmgSrc->isProjectile() ) + { + byteArray output; + Param param = { + DsMobKilled::RANGED, + mob_networking_id, + EntityIO::eTypeToIoid(dmgSrc->getDirectEntity()->GetType()), + mob->distanceTo(player->x, player->y, player->z), + 0/*not needed*/ + }; + output.data = (byte*) new Param(param); + output.length = sizeof(Param); + return output; + } + + // Kill made in melee, use itemInHand as weapon. + shared_ptr item = player->getCarriedItem(); + byteArray output; + Param param = { + DsMobKilled::MELEE, + mob_networking_id, + (item != NULL ? item->getItem()->id : 0), + mob->distanceTo(player->x, player->y, player->z), + 0/*not needed*/ + }; + output.data = (byte*) new Param(param); + output.length = sizeof(Param); + return output; +} + + + ///////////////////// + // Ds Mob Interact // + ///////////////////// + +string DsMobInteract::nameInteract[] = { + "unknownMobInteraction", "mobBred", "mobTamed", "mobCured", "mobCrafted", "mobSheared" +}; + +DsMobInteract::DsMobInteract(int id, const wstring &name) : Stat(id,name) {} + +void DsMobInteract::handleParamBlob(shared_ptr player, byteArray paramBlob) +{ + if (paramBlob.length == sizeof(Param)) + { + Param *param = (Param *) paramBlob.data; + + if (param->mobId < 0) return; + + app.DebugPrintf("<%ls>\t%s(%i)\n", DurangoStats::getUserId(player), + nameInteract[param->interactionType].c_str(), param->mobId); + + EventWriteMobInteract( + DurangoStats::getUserId(player), + DurangoStats::getPlayerSession(), + param->mobId, + param->interactionType ); + } +} + +byteArray DsMobInteract::createParamBlob(eInteract interactionId, int entityId) +{ + byteArray output; + Param param = { interactionId, EntityIO::eTypeToIoid((eINSTANCEOF)entityId) }; + output.data = (byte*) new Param(param); + output.length = sizeof(Param); + return output; +} + + + /////////////// + // Ds Travel // + /////////////// + +string DsTravel::nameMethods[eMethod_MAX] = + { + "Walk", "Swim", "Fall", "Climb", "Cart", "Boat", "Pig", "Time" + }; + +unsigned int DsTravel::CACHE_SIZES[eMethod_MAX] = + { + 40, // WALK - Meters? + 20, // SWIM - Meters? + 0, // FALL - Meters? - Fall event naturally only sends on land, no caching necessary. + 10, // CLIMB - Meters? + 70, // CART - Meters? + 70, // BOAT - Meters? + 10, // PIG - Meters? + 20*60*5, // TIME - GameTicks (20*60*5 ~ 5 mins) + }; + +DsTravel::DsTravel(int id, const wstring &name) : Stat(id,name) +{ + ZeroMemory(¶m_cache, sizeof(unsigned int)*eMethod_MAX*MAX_LOCAL_PLAYERS); +} + +void DsTravel::handleParamBlob(shared_ptr player, byteArray paramBlob) +{ + if (paramBlob.length == sizeof(Param)) + { + Param *param = (Param*) paramBlob.data; + + int newDistance = cache(player->GetXboxPad(), *param); + + if ( newDistance > 0 ) write(player, param->method, newDistance); + } +} + +byteArray DsTravel::createParamBlob(eMethod method, int distance) +{ + byteArray output; + Param param = { method, distance }; + output.data = (byte*) new Param(param); + output.length = sizeof(Param); + return output; +} + +int DsTravel::cache(int iPad, Param ¶m) +{ + if ( (eMethod_walk <= param.method) && (param.method < eMethod_MAX) ) + { + param_cache[iPad][param.method] += param.distance; + + if (param_cache[iPad][param.method] > CACHE_SIZES[param.method]) + { + int out = param_cache[iPad][param.method]; + param_cache[iPad][param.method] = 0; + return out; + } + } + + return 0; +} + +void DsTravel::flush(shared_ptr player) +{ + int iPad = player->GetXboxPad(); + for (int i = 0; i < eMethod_MAX; i++) + { + if (param_cache[iPad][i] > 0) + { + write( player, (eMethod) i, param_cache[iPad][i] ); + param_cache[iPad][i] = 0; + } + } +} + +void DsTravel::write(shared_ptr player, eMethod method, int distance) +{ + if (player == nullptr) return; + + app.DebugPrintf("<%ls>\t%s(%i)\n", DurangoStats::getUserId(player), nameMethods[method].c_str(), distance); + + if (method == DsTravel::eMethod_time) + { + EventWriteIncTimePlayed( + DurangoStats::getUserId(player), + DurangoStats::getPlayerSession(), + player->level->difficulty, + distance + ); + } + else if ( (eMethod_walk <= method) && (method < eMethod_MAX) ) + { + EventWriteIncDistanceTravelled( + DurangoStats::getUserId(player), + DurangoStats::getPlayerSession(), + player->level->difficulty, + distance, + method + ); + + switch(method) + { + case eMethod_walk: + case eMethod_fall: + case eMethod_minecart: + case eMethod_boat: + EventWriteLeaderboardTotals( + DurangoStats::getUserId(player), //UserId + DurangoStats::getPlayerSession(), // PlayerSessionId + player->level->difficulty, // Difficulty, + eLeaderboardId_TRAVELLING, // ScoreboardId + distance); + break; + } + } +} + + ////////////////// + // Ds Item Used // + ////////////////// + +DsItemUsed::DsItemUsed(int id, const wstring &name) : Stat(id,name) {} + +void DsItemUsed::handleParamBlob(shared_ptr player, byteArray paramBlob) +{ + if (paramBlob.length == sizeof(Param)) + { + Param *param = (Param*) paramBlob.data; + app.DebugPrintf("<%ls>\titemUsed(%i,%i,%i)\n", DurangoStats::getUserId(player), + param->itemId, + param->aux, + param->count + ); + + EventWriteMcItemUsed( + DurangoStats::getUserId(player), + 0, // SectionId, + DurangoStats::getPlayerSession(), + 0, // MultiplayerCorrelationId, + 0, // Gameplay Mode, + player->level->difficulty, + param->itemId, + 0, 0, 0, // (x,y,z) + param->aux, + param->count, + param->hunger + ); + } +} + +byteArray DsItemUsed::createParamBlob(int itemId, int aux, int count, int health, int hunger) +{ + byteArray output; + Param param = { itemId, aux, count, health, hunger }; + output.data = (byte*) new Param(param); + output.length = sizeof(Param); + return output; +} + + + //////////////////// + // Ds Achievement // + //////////////////// + +DsAchievement::DsAchievement(int id, const wstring &name) : Stat(id,name) {} + +void DsAchievement::handleParamBlob(shared_ptr player, byteArray paramBlob) +{ + if (paramBlob.length == sizeof(SmallParam)) + { + SmallParam *paramS = (SmallParam*) paramBlob.data; + assert( DurangoStats::binaryAchievement(paramS->award) ); + app.DebugPrintf("<%ls>\tAchievement(%i)\n", DurangoStats::getUserId(player), paramS->award); + + bool canAward = true; + if(paramS->award == eAward_stayinFrosty) + { + canAward = !player->m_bHasAwardedStayinFrosty; + player->m_bHasAwardedStayinFrosty = true; + } + + if(canAward) + { + EventWriteAchievementGet( + DurangoStats::getUserId(player), + DurangoStats::getPlayerSession(), + paramS->award + ); + } + } + else if (paramBlob.length == sizeof(LargeParam)) + { + LargeParam *paramL = (LargeParam*) paramBlob.data; + assert( DurangoStats::enhancedAchievement(paramL->award) ); + + switch(paramL->award) + { + case eAward_musicToMyEars: + app.DebugPrintf("<%ls>\tmusicToMyEars(%i)\n", DurangoStats::getUserId(player), paramL->count); + EventWritePlayedMusicDisc( + DurangoStats::getUserId(player), + DurangoStats::getPlayerSession(), + paramL->count + ); + break; + + case eAward_chestfulOfCobblestone: + app.DebugPrintf("<%ls>\tchestfulOfCobblestone(%i)\n", DurangoStats::getUserId(player), paramL->count); + EventWriteChestfulOfCobblestone( + DurangoStats::getUserId(player), + DurangoStats::getPlayerSession(), + paramL->count + ); + break; + + case eAward_overkill: + app.DebugPrintf("<%ls>\toverkill(%i)\n", DurangoStats::getUserId(player), paramL->count); + EventWriteOverkill( + DurangoStats::getUserId(player), + DurangoStats::getPlayerSession(), + paramL->count + ); + break; + + case eAward_OnARail: + app.DebugPrintf("<%ls>\nonARail(%i)\n", DurangoStats::getUserId(player), paramL->count); + EventWriteOnARail( + DurangoStats::getUserId(player), + DurangoStats::getPlayerSession(), + paramL->count + ); + break; + } + } + else assert(false); // Unsuitable paramBlob length. +} + +byteArray DsAchievement::createSmallParamBlob(eAward award) +{ + byteArray output; + SmallParam param = { award }; + output.data = (byte*) new SmallParam(param); + output.length = sizeof(SmallParam); + return output; +} + +byteArray DsAchievement::createLargeParamBlob(eAward award, int count) +{ + byteArray output; + LargeParam param = { award, count }; + output.data = (byte*) new LargeParam(param); + output.length = sizeof(LargeParam); + return output; +} + + + ////////////////////////// + // Ds Changed Dimension // + ////////////////////////// + +DsChangedDimension::DsChangedDimension(int id, const wstring &name) : Stat(id,name) {} + +void DsChangedDimension::handleParamBlob(shared_ptr player, byteArray paramBlob) +{ + if (paramBlob.length == sizeof(Param)) + { + Param *param = (Param*) paramBlob.data; + app.DebugPrintf("<%ls>\tchangedDimension(%i:%i)\n", DurangoStats::getUserId(player), + param->fromDimId, param->toDimId); + + // No longer used. + } +} + +byteArray DsChangedDimension::createParamBlob(int fromDimId, int toDimId) +{ + byteArray output; + Param param = { fromDimId, toDimId }; + output.data = (byte*) new Param(param); + output.length = sizeof(Param); + return output; +} + + + ////////////////////// + // Ds Entered Biome // + ////////////////////// + +DsEnteredBiome::DsEnteredBiome(int id, const wstring &name) : Stat(id,name) {} + +void DsEnteredBiome::handleParamBlob(shared_ptr player, byteArray paramBlob) +{ + if (paramBlob.length == sizeof(Param)) + { + Param *param = (Param*) paramBlob.data; + app.DebugPrintf("<%ls>\tenteredBiome(%i)\n", DurangoStats::getUserId(player), param->biomeId); + + EventWriteEnteredNewBiome( + DurangoStats::getUserId(player), + DurangoStats::getPlayerSession(), + param->biomeId + ); + } +} + +byteArray DsEnteredBiome::createParamBlob(int biomeId) +{ + byteArray output; + Param param = { biomeId }; + output.data = (byte*) new Param(param); + output.length = sizeof(Param); + return output; +} + + //////////////////////// + // DURANGO STATISTICS // + //////////////////////// + +DurangoStats::DurangoStats() +{ + // Hopefully only using the first parameter + itemsAcquired = new DsItemEvent( itemsAcquired_Id, L"itemsAcquired" ); + itemsAcquired->postConstruct(); + + itemUsed = new DsItemUsed( itemUsed_Id, L"itemUsed" ); + itemUsed->postConstruct(); + + travel = new DsTravel( travel_Id, L"travel" ); + travel->setAwardLocallyOnly()->postConstruct(); + + mobKilled = new DsMobKilled( mobKilled_Id, L"mobKilled" ); + mobKilled->postConstruct(); + + mobInteract = new DsMobInteract( mobInteract_Id, L"mobInteract" ); + mobInteract->postConstruct(); + + changedDimension = new DsChangedDimension( changedDimension_Id, L"changedDimension" ); + changedDimension->postConstruct(); + + enteredBiome = new DsEnteredBiome( enteredBiome_Id, L"enteredBiome" ); + enteredBiome->postConstruct(); + + achievement = new DsAchievement( binAchievement_Id, L"achievement" ); + achievement->postConstruct(); + + achievementLocal = new DsAchievement( binAchievementLocal_Id, L"achievementLocal" ); + achievementLocal->setAwardLocallyOnly(); + achievementLocal->postConstruct(); + + EventRegisterXBLA_149E11AE(); +} + +DurangoStats::~DurangoStats() +{ + EventUnregisterXBLA_149E11AE(); +} + +Stat *DurangoStats::get_stat(int i) +{ + switch (i) + { + case itemsAcquired_Id: return (Stat*) itemsAcquired; + case itemUsed_Id: return (Stat*) itemUsed; + case travel_Id: return (Stat*) travel; + case mobKilled_Id: return (Stat*) mobKilled; + case mobInteract_Id: return (Stat*) mobInteract; + case changedDimension_Id: return (Stat*) changedDimension; + case enteredBiome_Id: return (Stat*) enteredBiome; + case binAchievement_Id: return (Stat*) achievement; + case binAchievementLocal_Id: return (Stat*) achievementLocal; + + // Unrecognised stat id + default: assert(false); break; + } + + return NULL; +} + +Stat* DurangoStats::get_walkOneM() +{ + return travel; +} + +Stat* DurangoStats::get_swimOneM() +{ + return travel; +} + +Stat* DurangoStats::get_fallOneM() +{ + return travel; +} + +Stat* DurangoStats::get_climbOneM() +{ + return travel; +} + +Stat* DurangoStats::get_minecartOneM() +{ + return travel; +} + +Stat* DurangoStats::get_boatOneM() +{ + return travel; +} + +Stat* DurangoStats::get_pigOneM() +{ + return travel; +} + +Stat *DurangoStats::get_cowsMilked() +{ + return get_itemsCrafted(Item::milk_Id); +} + +Stat* DurangoStats::get_killMob() +{ + return mobKilled; +} + +Stat* DurangoStats::get_breedEntity(eINSTANCEOF entityId) +{ + return mobInteract; +} + +Stat* DurangoStats::get_tamedEntity(eINSTANCEOF entityId) +{ + return mobInteract; +} + +Stat* DurangoStats::get_curedEntity(eINSTANCEOF entityId) +{ + return mobInteract; +} + +Stat* DurangoStats::get_craftedEntity(eINSTANCEOF entityId) +{ + return mobInteract; +} + +Stat* DurangoStats::get_shearedEntity(eINSTANCEOF entityId) +{ + return mobInteract; +} + +Stat* DurangoStats::get_timePlayed() +{ + return travel; +} + +Stat* DurangoStats::get_blocksPlaced(int blockId) +{ + return (Stat*) itemsAcquired; +} + +Stat* DurangoStats::get_blocksMined(int blockId) +{ + return (Stat*) itemsAcquired; +} + +Stat* DurangoStats::get_itemsCollected(int itemId, int itemAux) +{ + return (Stat*) itemsAcquired; +} + +Stat* DurangoStats::get_itemsCrafted(int itemId) +{ + switch (itemId) + { + // 4J-JEV: These items can be crafted trivially to and from their block equivalents, + // 'Acquire Hardware' also relies on 'Count_Crafted(IronIngot) == Count_Forged(IronIngot)" on the Stats server. + case Item::ironIngot_Id: + case Item::goldIngot_Id: + case Item::diamond_Id: + case Item::redStone_Id: + case Item::emerald_Id: + return NULL; + + case Item::dye_powder_Id: + default: + return (Stat*) itemsAcquired; + } + +} + +Stat* DurangoStats::get_itemsSmelted(int itemId) +{ + // 4J-JEV: Context needed for itemCrafted for iron/gold being smelted. + return (Stat*) itemsAcquired; +} + +Stat* DurangoStats::get_itemsUsed(int itemId) +{ + return (Stat*) itemUsed; +} + +Stat *DurangoStats::get_itemsBought(int itemId) +{ + return (Stat*) itemsAcquired; +} + +Stat* DurangoStats::get_changedDimension(int from, int to) +{ + return (Stat*) changedDimension; +} + +Stat* DurangoStats::get_enteredBiome(int biomeId) +{ + return (Stat*) enteredBiome; +} + +Stat* DurangoStats::get_achievement(eAward achievementId) +{ + // Special case for 'binary' achievements. + if ( binaryAchievement(achievementId) + || enhancedAchievement(achievementId) ) + { + switch (achievementId) + { + case eAward_chestfulOfCobblestone: + case eAward_TakingInventory: + return achievementLocal; + + default: + return achievement; + } + } + else if (achievementId == eAward_zombieDoctor) + { + return get_curedEntity(eTYPE_ZOMBIE); + } + + // Other achievements awarded through more detailed generic events. + return NULL; +} + +byteArray DurangoStats::getParam_walkOneM(int distance) +{ + return DsTravel::createParamBlob(DsTravel::eMethod_walk, distance); +} + +byteArray DurangoStats::getParam_swimOneM(int distance) +{ + return DsTravel::createParamBlob(DsTravel::eMethod_swim,distance); +} + +byteArray DurangoStats::getParam_fallOneM(int distance) +{ + return DsTravel::createParamBlob(DsTravel::eMethod_fall,distance); +} + +byteArray DurangoStats::getParam_climbOneM(int distance) +{ + return DsTravel::createParamBlob(DsTravel::eMethod_climb,distance); +} + +byteArray DurangoStats::getParam_minecartOneM(int distance) +{ + return DsTravel::createParamBlob(DsTravel::eMethod_minecart,distance); +} + +byteArray DurangoStats::getParam_boatOneM(int distance) +{ + return DsTravel::createParamBlob(DsTravel::eMethod_boat,distance); +} + +byteArray DurangoStats::getParam_pigOneM(int distance) +{ + return DsTravel::createParamBlob(DsTravel::eMethod_pig,distance); +} + +byteArray DurangoStats::getParam_cowsMilked() +{ + return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Crafted, Item::milk_Id, 0, 1); +} + +byteArray DurangoStats::getParam_blocksPlaced(int blockId, int data, int count) +{ + return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Placed, blockId, data, count); +} + +byteArray DurangoStats::getParam_blocksMined(int blockId, int data, int count) +{ + return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Mined, blockId, data, count); +} + +byteArray DurangoStats::getParam_itemsCollected(int id, int aux, int count) +{ + return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Pickedup, id, aux, count); +} + +byteArray DurangoStats::getParam_itemsCrafted(int id, int aux, int count) +{ + return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Crafted, id, aux, count); +} + +byteArray DurangoStats::getParam_itemsUsed(shared_ptr player, shared_ptr itm) +{ + return DsItemUsed::createParamBlob( + itm->getItem()->id, itm->getAuxValue(), itm->GetCount(), + player->getHealth(), player->getFoodData()->getFoodLevel() + ); +} + +byteArray DurangoStats::getParam_itemsBought(int id, int aux, int count) +{ + return DsItemEvent::createParamBlob(DsItemEvent::eAcquisitionMethod_Bought, id, aux, count); +} + +byteArray DurangoStats::getParam_mobKill(shared_ptr player, shared_ptr mob, DamageSource *dmgSrc) +{ + return DsMobKilled::createParamBlob(player,mob,dmgSrc); +} + +byteArray DurangoStats::getParam_breedEntity(eINSTANCEOF entityId) +{ + return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Breed, entityId); +} + +byteArray DurangoStats::getParam_tamedEntity(eINSTANCEOF entityId) +{ + return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Tamed, entityId); +} + +byteArray DurangoStats::getParam_curedEntity(eINSTANCEOF entityId) +{ + return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Cured, entityId); +} + +byteArray DurangoStats::getParam_craftedEntity(eINSTANCEOF entityId) +{ + return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Crafted, entityId); +} + +byteArray DurangoStats::getParam_shearedEntity(eINSTANCEOF entityId) +{ + return DsMobInteract::createParamBlob(DsMobInteract::eInteract_Sheared, entityId); +} + +byteArray DurangoStats::getParam_time(int timediff) +{ + return DsTravel::createParamBlob(DsTravel::eMethod_time, timediff); +} + +byteArray DurangoStats::getParam_changedDimension(int from, int to) +{ + return DsChangedDimension::createParamBlob(from,to); +} + +byteArray DurangoStats::getParam_enteredBiome(int biomeId) +{ + return DsEnteredBiome::createParamBlob(biomeId); +} + +byteArray DurangoStats::getParam_achievement(eAward id) +{ + if (binaryAchievement(id)) + { + return DsAchievement::createSmallParamBlob(id); + } + else if (enhancedAchievement(id)) + { + assert(false); // Should be calling the appropriate getParam function. + } + else if (id == eAward_zombieDoctor) + { + return getParam_curedEntity(eTYPE_ZOMBIE); + } + + // If its not a binary achievement, + // don't bother constructing the param blob. + return getParam_noArgs(); +} + +byteArray DurangoStats::getParam_onARail(int dist) +{ + return DsAchievement::createLargeParamBlob(eAward_OnARail, dist); +} + +byteArray DurangoStats::getParam_chestfulOfCobblestone(int count) +{ + return DsAchievement::createLargeParamBlob(eAward_chestfulOfCobblestone, count); +} + +byteArray DurangoStats::getParam_overkill(int dmg) +{ + return DsAchievement::createLargeParamBlob(eAward_overkill, dmg); +} + +byteArray DurangoStats::getParam_musicToMyEars(int recordId) +{ + return DsAchievement::createLargeParamBlob(eAward_musicToMyEars, recordId); +} + +bool DurangoStats::binaryAchievement(eAward achievementId) +{ + switch (achievementId) + { + case eAward_InToTheNether: + case eAward_theEnd: + case eAward_WhenPigsFly: + case eAward_diamondsToYou: + case eAward_stayinFrosty: + case eAward_renewableEnergy: + case eAward_ironMan: + case eAward_winGame: + case /*maybe*/ eAward_TakingInventory: + return true; + default: + return false; + } +} + +/** 4J-JEV, + Basically achievements with an inconsequential extra parameter + that I thought best not to / prefered not to / couldn't be bothered to make class handlers for. + (Motivation: it would be nice for players to see how close they were/are to achieving these things). +*/ +bool DurangoStats::enhancedAchievement(eAward achievementId) +{ + switch (achievementId) + { + //case eAward_TakingInventory: + case eAward_musicToMyEars: + case eAward_chestfulOfCobblestone: + case eAward_overkill: + case eAward_OnARail: + return true; + default: + return false; + } +} + + +void DurangoStats::generatePlayerSession() +{ + DurangoStats *dsInstance = (DurangoStats *) GenericStats::getInstance(); + CoCreateGuid( &dsInstance->playerSessionId ); +} + +LPCGUID DurangoStats::getPlayerSession() +{ + DurangoStats *dsInstance = (DurangoStats *) GenericStats::getInstance(); + LPCGUID lpcguid = &dsInstance->playerSessionId; + return lpcguid; +} + +void DurangoStats::setMultiplayerCorrelationId(Platform::String^ mcpId) +{ + ((DurangoStats*)GenericStats::getInstance())->multiplayerCorrelationId = mcpId; +} + +LPCWSTR DurangoStats::getMultiplayerCorrelationId() +{ + return ((DurangoStats*)GenericStats::getInstance())->multiplayerCorrelationId->Data(); +} + +LPCWSTR DurangoStats::getUserId(shared_ptr player) +{ + return getUserId(player->GetXboxPad()); +} + +LPCWSTR DurangoStats::getUserId(int iPad) +{ + static wstring cache = L""; + PlayerUID uid = INVALID_XUID; + ProfileManager.GetXUID(iPad, &uid, true); + cache = uid.toString(); + return cache.c_str(); +} + +void DurangoStats::playerSessionStart(PlayerUID uid, shared_ptr plr) +{ + if (plr != NULL && plr->level != NULL && plr->level->getLevelData() != NULL) + { + //wprintf(uid.toString().c_str()); + + //EventWritePlayerSessionStart( + app.DebugPrintf(">>>\tPlayerSessionStart(%ls,%s,%ls,%i,%i)\n", + uid.toString(), + DurangoStats::getPlayerSession(), + DurangoStats::getMultiplayerCorrelationId(), + plr->level->getLevelData()->getGameType()->isSurvival(), + plr->level->difficulty + ); + + EventWritePlayerSessionStart( + uid.toString().c_str(), + DurangoStats::getPlayerSession(), + DurangoStats::getMultiplayerCorrelationId(), + plr->level->getLevelData()->getGameType()->isSurvival(), + plr->level->difficulty + ); + } +} + +void DurangoStats::playerSessionStart(int iPad) +{ + PlayerUID puid; shared_ptr plr; + ProfileManager.GetXUID(iPad, &puid, true); + plr = Minecraft::GetInstance()->localplayers[iPad]; + playerSessionStart(puid,plr); +} + +void DurangoStats::playerSessionPause(int iPad) +{ + shared_ptr plr = Minecraft::GetInstance()->localplayers[iPad]; + if (plr != NULL && plr->level != NULL && plr->level->getLevelData() != NULL) + { + PlayerUID puid; + ProfileManager.GetXUID(iPad, &puid, true); + + //EventWritePlayerSessionPause( + app.DebugPrintf(">>>\tPlayerSessionPause(%ls,%s,%ls)\n", + puid.toString().c_str(), + DurangoStats::getPlayerSession(), + DurangoStats::getMultiplayerCorrelationId() + ); + + EventWritePlayerSessionPause( + puid.toString().c_str(), + DurangoStats::getPlayerSession(), + DurangoStats::getMultiplayerCorrelationId() + ); + } +} + +void DurangoStats::playerSessionResume(int iPad) +{ + shared_ptr plr = Minecraft::GetInstance()->localplayers[iPad]; + if (plr != NULL && plr->level != NULL && plr->level->getLevelData() != NULL) + { + PlayerUID puid; + ProfileManager.GetXUID(iPad, &puid, true); + + //EventWritePlayerSessionResume( + app.DebugPrintf(">>>\tPlayerSessionResume(%ls,%s,%ls,%i,%i)\n", + puid.toString().c_str(), + DurangoStats::getPlayerSession(), + DurangoStats::getMultiplayerCorrelationId(), + plr->level->getLevelData()->getGameType()->isSurvival(), + plr->level->difficulty + ); + + EventWritePlayerSessionResume( + puid.toString().c_str(), + DurangoStats::getPlayerSession(), + DurangoStats::getMultiplayerCorrelationId(), + plr->level->getLevelData()->getGameType()->isSurvival(), + plr->level->difficulty + ); + } +} + +void DurangoStats::playerSessionEnd(int iPad) +{ + shared_ptr plr = Minecraft::GetInstance()->localplayers[iPad]; + if (plr != NULL) + { + DurangoStats::getInstance()->travel->flush(plr); + } +} \ No newline at end of file diff --git a/Minecraft.World/DurangoStats.h b/Minecraft.World/DurangoStats.h new file mode 100644 index 00000000..1b2f7723 --- /dev/null +++ b/Minecraft.World/DurangoStats.h @@ -0,0 +1,311 @@ +#pragma once + +#include "GeneralStat.h" + +#include "GenericStats.h" + +enum ELeaderboardId +{ + eLeaderboardId_TRAVELLING = 0, + eLeaderboardId_MINING = 1, + eLeaderboardId_FARMING = 2, + eLeaderboardId_KILLING = 3 +}; + +class DsItemEvent : public Stat +{ +public: + static string nameMethods[]; + + enum eAcquisitionMethod + { + eAcquisitionMethod_None = 0, + + eAcquisitionMethod_Pickedup, + eAcquisitionMethod_Crafted, + eAcquisitionMethod_TakenFromChest, + eAcquisitionMethod_TakenFromEnderchest, + eAcquisitionMethod_Bought, + eAcquisitionMethod_Smithed, + eAcquisitionMethod_Mined, + + eAcquisitionMethod_Placed, + + eAcquisitionMethod_MAX + }; + + typedef struct _Param + { + int methodId, itemId, itemAux, itemCount; + } Param; + + DsItemEvent(int id, const wstring &name); + + bool onLeaderboard(ELeaderboardId leaderboard, eAcquisitionMethod methodId, Param *param); + int mergeIds(int itemId); + + virtual void handleParamBlob(shared_ptr plr, byteArray param); + static byteArray createParamBlob(eAcquisitionMethod methodId, int itemId, int itemAux, int itemCount); +}; + +class DsMobKilled : public Stat +{ +public: + static const bool RANGED = true; + static const bool MELEE = false; + + static const int SPIDER_JOCKEY_ID = 49; + + DsMobKilled(int id, const wstring &name); + + typedef struct { bool isRanged; int mobType, weaponId, distance, damage; } Param; + virtual void handleParamBlob(shared_ptr plr, byteArray param); + static byteArray createParamBlob(shared_ptr plr, shared_ptr mob, DamageSource *dmgSrc); +}; + +class DsMobInteract : public Stat +{ +protected: + static string nameInteract[]; + +public: + enum eInteract + { + eInteract_None = 0, + eInteract_Breed, + eInteract_Tamed, + eInteract_Cured, + eInteract_Crafted, + eInteract_Sheared + }; + + DsMobInteract(int id, const wstring &name); + + typedef struct { int interactionType, mobId; } Param; + virtual void handleParamBlob(shared_ptr plr, byteArray param); + static byteArray createParamBlob(eInteract interactionId, int entityId); +}; + +class DsTravel : public Stat +{ +public: + static string nameMethods[]; + + enum eMethod { + eMethod_walk, + eMethod_swim, + eMethod_fall, + eMethod_climb, + eMethod_minecart, + eMethod_boat, + eMethod_pig, + + eMethod_time, // Time is a dimension too right... + + eMethod_MAX + }; + + static unsigned int CACHE_SIZES[eMethod_MAX]; + + DsTravel(int id, const wstring &name); + + typedef struct { eMethod method; int distance; } Param; + virtual void handleParamBlob(shared_ptr plr, byteArray paramBlob); + static byteArray createParamBlob(eMethod method, int distance); + + void flush(shared_ptr plr); + +protected: + unsigned int param_cache[MAX_LOCAL_PLAYERS][eMethod_MAX]; + int cache(int iPad, Param ¶m); + void write(shared_ptr plr, eMethod method, int distance); +}; + +class DsItemUsed : public Stat +{ +public: + DsItemUsed(int id, const wstring &name); + typedef struct { int itemId, aux, count, health, hunger; } Param; + virtual void handleParamBlob(shared_ptr plr, byteArray paramBlob); + static byteArray createParamBlob(int itemId, int aux, int count, int health, int hunger); +}; + +class DsAchievement : public Stat +{ +public: + DsAchievement(int id, const wstring &name); + + virtual void handleParamBlob(shared_ptr plr, byteArray paramBlob); + + typedef struct { eAward award; } SmallParam; + static byteArray createSmallParamBlob(eAward id); + + typedef struct { eAward award; int count; } LargeParam; + static byteArray createLargeParamBlob(eAward id, int count); +}; + +class DsChangedDimension : public Stat +{ +public: + DsChangedDimension(int id, const wstring &name); + typedef struct { int fromDimId, toDimId; } Param; + virtual void handleParamBlob(shared_ptr plr, byteArray paramBlob); + static byteArray createParamBlob(int fromDimId, int toDimId); +}; + +class DsEnteredBiome : public Stat +{ +public: + DsEnteredBiome(int id, const wstring &name); + typedef struct { int biomeId; } Param; + virtual void handleParamBlob(shared_ptr plr, byteArray paramBlob); + static byteArray createParamBlob(int biomeId); +}; + +class DurangoStats : public GenericStats +{ +public: + static DurangoStats *getInstance() { return (DurangoStats*) GenericStats::getInstance(); } + +protected: + enum { + itemsAcquired_Id = 1, + itemUsed_Id, + travel_Id, + mobKilled_Id, + mobInteract_Id, + binAchievement_Id, + binAchievementLocal_Id, + changedDimension_Id, + enteredBiome_Id, + }; + + DsItemEvent *itemsAcquired; + DsTravel *travel; + + DsMobKilled *mobKilled; + DsMobInteract *mobInteract; + + DsAchievement *achievement; + DsAchievement *achievementLocal; + + DsItemUsed *itemUsed; + + DsChangedDimension *changedDimension; + DsEnteredBiome *enteredBiome; + + GUID playerSessionId; + Platform::String^ multiplayerCorrelationId; + +public: + DurangoStats(); + ~DurangoStats(); + + virtual Stat *get_stat(int i); + + +protected: + // Stats + + virtual Stat* get_walkOneM(); + virtual Stat* get_swimOneM(); + virtual Stat* get_fallOneM(); + virtual Stat* get_climbOneM(); + virtual Stat* get_minecartOneM(); + virtual Stat* get_boatOneM(); + virtual Stat* get_pigOneM(); + + virtual Stat* get_cowsMilked(); + + // Kills. + virtual Stat* get_killMob(); + + // Mob-Interactions. + virtual Stat* get_breedEntity(eINSTANCEOF entityId); + virtual Stat* get_tamedEntity(eINSTANCEOF entityId); + virtual Stat* get_curedEntity(eINSTANCEOF entityId); + virtual Stat* get_craftedEntity(eINSTANCEOF entityId); + virtual Stat* get_shearedEntity(eINSTANCEOF entityId); + + virtual Stat* get_timePlayed(); + + virtual Stat* get_blocksPlaced(int blockId); + virtual Stat* get_blocksMined(int blockId); + virtual Stat* get_itemsCollected(int itemId, int itemAux); + virtual Stat* get_itemsCrafted(int itemId); + virtual Stat* get_itemsSmelted(int itemId); + virtual Stat* get_itemsUsed(int itemId); + virtual Stat* get_itemsBought(int itemId); + + virtual Stat* get_changedDimension(int from, int to); + virtual Stat* get_enteredBiome(int biomeId); + + // Achievements + + virtual Stat* get_achievement(eAward achievementId); + + + // Parameters + + virtual byteArray getParam_walkOneM(int distance); + virtual byteArray getParam_swimOneM(int distance); + virtual byteArray getParam_fallOneM(int distance); + virtual byteArray getParam_climbOneM(int distance); + virtual byteArray getParam_minecartOneM(int distance); + virtual byteArray getParam_boatOneM(int distance); + virtual byteArray getParam_pigOneM(int distance); + + virtual byteArray getParam_cowsMilked(); + + virtual byteArray getParam_blocksPlaced(int blockId, int data, int count); + virtual byteArray getParam_blocksMined(int blockId, int data, int count); + virtual byteArray getParam_itemsCollected(int id, int aux, int count); + virtual byteArray getParam_itemsCrafted(int id, int aux, int count); + virtual byteArray getParam_itemsUsed(shared_ptr plr, shared_ptr itm); + virtual byteArray getParam_itemsBought(int id, int aux, int count); + + virtual byteArray getParam_mobKill(shared_ptr plr, shared_ptr mob, DamageSource *dmgSrc); + + virtual byteArray getParam_breedEntity(eINSTANCEOF entityId); + virtual byteArray getParam_tamedEntity(eINSTANCEOF entityId); + virtual byteArray getParam_curedEntity(eINSTANCEOF entityId); + virtual byteArray getParam_craftedEntity(eINSTANCEOF entityId); + virtual byteArray getParam_shearedEntity(eINSTANCEOF entityId); + + virtual byteArray getParam_time(int timediff); + + virtual byteArray getParam_changedDimension(int from, int to); + virtual byteArray getParam_enteredBiome(int biomeId); + + virtual byteArray getParam_achievement(eAward id); + + virtual byteArray getParam_onARail(int dist); + virtual byteArray getParam_chestfulOfCobblestone(int count); + // virtual byteArray getParam_openInventory( + virtual byteArray getParam_overkill(int dmg); + virtual byteArray getParam_musicToMyEars(int recordId); + + // Helpers + +public: + // Achievements that have no parameters, you have earned them or not. + static bool binaryAchievement(eAward achievementId); + + // Achievements that have parameters, but the event is specifically for this achievement. + static bool enhancedAchievement(eAward achievementId); + + static void generatePlayerSession(); + static LPCGUID getPlayerSession(); + + static void setMultiplayerCorrelationId(Platform::String^ mpcId); + static LPCWSTR getMultiplayerCorrelationId(); + + static LPCWSTR getUserId(shared_ptr plr); + static LPCWSTR getUserId(int iPad); + + static void playerSessionStart(PlayerUID,shared_ptr); + static void playerSessionStart(int iPad); + static void playerSessionPause(int iPad); + static void playerSessionResume(int iPad); + static void playerSessionEnd(int iPad); +}; \ No newline at end of file diff --git a/Minecraft.World/DyePowderItem.cpp b/Minecraft.World/DyePowderItem.cpp new file mode 100644 index 00000000..a05b8354 --- /dev/null +++ b/Minecraft.World/DyePowderItem.cpp @@ -0,0 +1,317 @@ +using namespace std; + +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.global.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.h" +#include "Material.h" +#include "DyePowderItem.h" + +DyePowderItem::DyePowderItem(int id) : Item( id ) +{ + setStackedByData(true); + setMaxDamage(0); + icons = NULL; +} + +const unsigned int DyePowderItem::COLOR_DESCS[] = +{ + IDS_ITEM_DYE_POWDER_BLACK, + IDS_ITEM_DYE_POWDER_RED, + IDS_ITEM_DYE_POWDER_GREEN, + IDS_ITEM_DYE_POWDER_BROWN, + IDS_ITEM_DYE_POWDER_BLUE, + IDS_ITEM_DYE_POWDER_PURPLE, + IDS_ITEM_DYE_POWDER_CYAN, + IDS_ITEM_DYE_POWDER_SILVER, + IDS_ITEM_DYE_POWDER_GRAY, + IDS_ITEM_DYE_POWDER_PINK, + IDS_ITEM_DYE_POWDER_LIME, + IDS_ITEM_DYE_POWDER_YELLOW, + IDS_ITEM_DYE_POWDER_LIGHT_BLUE, + IDS_ITEM_DYE_POWDER_MAGENTA, + IDS_ITEM_DYE_POWDER_ORANGE, + IDS_ITEM_DYE_POWDER_WHITE +}; + +const unsigned int DyePowderItem::COLOR_USE_DESCS[] = +{ + IDS_DESC_DYE_BLACK, + IDS_DESC_DYE_RED, + IDS_DESC_DYE_GREEN, + IDS_DESC_DYE_BROWN, + IDS_DESC_DYE_BLUE, + IDS_DESC_DYE_PURPLE, + IDS_DESC_DYE_CYAN, + IDS_DESC_DYE_LIGHTGRAY, + IDS_DESC_DYE_GRAY, + IDS_DESC_DYE_PINK, + IDS_DESC_DYE_LIME, + IDS_DESC_DYE_YELLOW, + IDS_DESC_DYE_LIGHTBLUE, + IDS_DESC_DYE_MAGENTA, + IDS_DESC_DYE_ORANGE, + IDS_DESC_DYE_WHITE +}; + +const wstring DyePowderItem::COLOR_TEXTURES[] = +{ L"dyePowder_black", L"dyePowder_red", L"dyePowder_green", L"dyePowder_brown", L"dyePowder_blue", L"dyePowder_purple", L"dyePowder_cyan", L"dyePowder_silver", L"dyePowder_gray", L"dyePowder_pink", +L"dyePowder_lime", L"dyePowder_yellow", L"dyePowder_lightBlue", L"dyePowder_magenta", L"dyePowder_orange", L"dyePowder_white"}; + +const int DyePowderItem::COLOR_RGB[] = +{ + 0x1e1b1b, + 0xb3312c, + 0x3b511a, + 0x51301a, + 0x253192, + 0x7b2fbe, + 0xababab, + 0x287697, + 0x434343, + 0xd88198, + 0x41cd34, + 0xdecf2a, + 0x6689d3, + 0xc354cd, + 0xeb8844, + 0xf0f0f0 +}; + +const int DyePowderItem::BLACK = 0; +const int DyePowderItem::RED = 1; +const int DyePowderItem::GREEN = 2; +const int DyePowderItem::BROWN = 3; +const int DyePowderItem::BLUE = 4; +const int DyePowderItem::PURPLE = 5; +const int DyePowderItem::CYAN = 6; +const int DyePowderItem::SILVER = 7; +const int DyePowderItem::GRAY = 8; +const int DyePowderItem::PINK = 9; +const int DyePowderItem::LIME = 10; +const int DyePowderItem::YELLOW = 11; +const int DyePowderItem::LIGHT_BLUE = 12; +const int DyePowderItem::MAGENTA = 13; +const int DyePowderItem::ORANGE = 14; +const int DyePowderItem::WHITE = 15; + +Icon *DyePowderItem::getIcon(int itemAuxValue) +{ + int colorValue = Mth::clamp(itemAuxValue, 0, 15); + return icons[colorValue]; +} + +unsigned int DyePowderItem::getDescriptionId(shared_ptr itemInstance) +{ + int colorValue = Mth::clamp(itemInstance->getAuxValue(), 0, 15); + return COLOR_DESCS[colorValue]; +} + +unsigned int DyePowderItem::getUseDescriptionId(shared_ptr itemInstance) +{ + return COLOR_USE_DESCS[itemInstance->getAuxValue()]; +} + +bool DyePowderItem::useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + if (!player->mayBuild(x, y, z)) return false; + + // 4J-PB - Adding a test only version to allow tooltips to be displayed + if (itemInstance->getAuxValue() == WHITE) + { + // bone meal is a fertilizer, so instantly grow trees and stuff + + int tile = level->getTile(x, y, z); + if (tile == Tile::sapling_Id) + { + if(!bTestUseOnOnly) + { + if (!level->isClientSide) + { + ((Sapling *) Tile::sapling)->growTree(level, x, y, z, level->random); + itemInstance->count--; + } + } + return true; + } + else if (tile == Tile::mushroom1_Id || tile == Tile::mushroom2_Id) + { + if(!bTestUseOnOnly) + { + if (!level->isClientSide) + { + if (((Mushroom *) Tile::tiles[tile])->growTree(level, x, y, z, level->random)) + { + itemInstance->count--; + } + } + } + return true; + } + else if (tile == Tile::melonStem_Id || tile == Tile::pumpkinStem_Id) + { + if (level->getData(x, y, z) == 7) return false; + if(!bTestUseOnOnly) + { + if (!level->isClientSide) + { + ((StemTile *) Tile::tiles[tile])->growCropsToMax(level, x, y, z); + itemInstance->count--; + } + } + return true; + } + else if (tile == Tile::carrots_Id || tile == Tile::potatoes_Id) + { + if (level->getData(x, y, z) == 7) return false; + if(!bTestUseOnOnly) + { + if (!level->isClientSide) + { + ((CropTile *) Tile::tiles[tile])->growCropsToMax(level, x, y, z); + itemInstance->count--; + } + } + return true; + } + else if (tile == Tile::crops_Id) + { + if (level->getData(x, y, z) == 7) return false; + if(!bTestUseOnOnly) + { + if (!level->isClientSide) + { + ((CropTile *) Tile::crops)->growCropsToMax(level, x, y, z); + itemInstance->count--; + } + } + return true; + } + else if (tile == Tile::cocoa_Id) + { + if(!bTestUseOnOnly) + { + if (!level->isClientSide) + { + level->setData(x, y, z, (2 << 2) | DirectionalTile::getDirection(level->getData(x, y, z))); + itemInstance->count--; + } + } + return true; + } + else if (tile == Tile::grass_Id) + { + if(!bTestUseOnOnly) + { + if (!level->isClientSide) + { + itemInstance->count--; + + for (int j = 0; j < 128; j++) + { + int xx = x; + int yy = y + 1; + int zz = z; + for (int i = 0; i < j / 16; i++) + { + xx += random->nextInt(3) - 1; + yy += (random->nextInt(3) - 1) * random->nextInt(3) / 2; + zz += random->nextInt(3) - 1; + if (level->getTile(xx, yy - 1, zz) != Tile::grass_Id || level->isSolidBlockingTile(xx, yy, zz)) + { + goto mainloop; + } + } + + if (level->getTile(xx, yy, zz) == 0) + { + if (random->nextInt(10) != 0) + { + if (Tile::tallgrass->canSurvive(level, xx, yy, zz)) level->setTileAndData(xx, yy, zz, Tile::tallgrass_Id, TallGrass::TALL_GRASS); + } + else if (random->nextInt(3) != 0) + { + if (Tile::flower->canSurvive(level, xx, yy, zz)) level->setTile(xx, yy, zz, Tile::flower_Id); + } + else + { + if (Tile::rose->canSurvive(level, xx, yy, zz)) level->setTile(xx, yy, zz, Tile::rose_Id); + } + } + + // 4J - Stops infinite loops. +mainloop: continue; + } + } + } + + return true; + } + } + else if (itemInstance->getAuxValue() == BROWN) + { + // plant cocoa + + int tile = level->getTile(x, y, z); + int data = level->getData(x, y, z); + + if (tile == Tile::treeTrunk_Id && TreeTile::getWoodType(data) == TreeTile::JUNGLE_TRUNK) + { + if (face == 0) return false; + if (face == 1) return false; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + + if(!bTestUseOnOnly) + { + if (level->isEmptyTile(x, y, z)) + { + int cocoaData = Tile::tiles[Tile::cocoa_Id]->getPlacedOnFaceDataValue(level, x, y, z, face, clickX, clickY, clickZ, 0); + level->setTileAndData(x, y, z, Tile::cocoa_Id, cocoaData); + if (!player->abilities.instabuild) + { + itemInstance->count--; + } + } + } + return true; + } + } + return false; +} + +bool DyePowderItem::interactEnemy(shared_ptr itemInstance, shared_ptr mob) +{ + if (dynamic_pointer_cast( mob ) != NULL) + { + shared_ptr sheep = dynamic_pointer_cast(mob); + // convert to tile-based color value (0 is white instead of black) + int newColor = ClothTile::getTileDataForItemAuxValue(itemInstance->getAuxValue()); + if (!sheep->isSheared() && sheep->getColor() != newColor) + { + sheep->setColor(newColor); + itemInstance->count--; + } + return true; + } + return false; +} + +void DyePowderItem::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon *[DYE_POWDER_ITEM_TEXTURE_COUNT]; + + for (int i = 0; i < DYE_POWDER_ITEM_TEXTURE_COUNT; i++) + { + icons[i] = iconRegister->registerIcon(COLOR_TEXTURES[i]); + } +} \ No newline at end of file diff --git a/Minecraft.World/DyePowderItem.h b/Minecraft.World/DyePowderItem.h new file mode 100644 index 00000000..b96b4087 --- /dev/null +++ b/Minecraft.World/DyePowderItem.h @@ -0,0 +1,50 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class Player; +class Mob; +class Level; + +class DyePowderItem : public Item +{ +public: + static const unsigned int COLOR_DESCS[]; + static const unsigned int COLOR_USE_DESCS[]; + static const wstring COLOR_TEXTURES[]; + static const int COLOR_RGB[]; + + static const int BLACK; + static const int RED; + static const int GREEN; + static const int BROWN; + static const int BLUE; + static const int PURPLE; + static const int CYAN; + static const int SILVER; + static const int GRAY; + static const int PINK; + static const int LIME; + static const int YELLOW; + static const int LIGHT_BLUE; + static const int MAGENTA; + static const int ORANGE; + static const int WHITE; + +private: + static const int DYE_POWDER_ITEM_TEXTURE_COUNT = 16; + Icon **icons; + +public: + DyePowderItem(int id); + + virtual Icon *getIcon(int itemAuxValue); + virtual unsigned int getDescriptionId(shared_ptr itemInstance); + virtual unsigned int getUseDescriptionId(shared_ptr itemInstance); + virtual bool useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + virtual bool interactEnemy(shared_ptr itemInstance, shared_ptr mob); + + //@Override + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/EatTileGoal.cpp b/Minecraft.World/EatTileGoal.cpp new file mode 100644 index 00000000..55bf2f35 --- /dev/null +++ b/Minecraft.World/EatTileGoal.cpp @@ -0,0 +1,73 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "EatTileGoal.h" + +EatTileGoal::EatTileGoal(Mob *mob) +{ + eatAnimationTick = 0; + + this->mob = mob; + this->level = mob->level; + setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag | Control::JumpControlFlag); +} + +bool EatTileGoal::canUse() +{ + if (mob->getRandom()->nextInt(mob->isBaby() ? 50 : 1000) != 0) return false; + + int xx = Mth::floor(mob->x); + int yy = Mth::floor(mob->y); + int zz = Mth::floor(mob->z); + if (level->getTile(xx, yy, zz) == Tile::tallgrass_Id && level->getData(xx, yy, zz) == TallGrass::TALL_GRASS) return true; + if (level->getTile(xx, yy - 1, zz) == Tile::grass_Id) return true; + return false; +} + +void EatTileGoal::start() +{ + eatAnimationTick = EAT_ANIMATION_TICKS; + level->broadcastEntityEvent(mob->shared_from_this(), EntityEvent::EAT_GRASS); + mob->getNavigation()->stop(); +} + +void EatTileGoal::stop() +{ + eatAnimationTick = 0; +} + +bool EatTileGoal::canContinueToUse() +{ + return eatAnimationTick > 0; +} + +int EatTileGoal::getEatAnimationTick() +{ + return eatAnimationTick; +} + +void EatTileGoal::tick() +{ + eatAnimationTick = max(0, eatAnimationTick - 1); + if (eatAnimationTick != 4) return; + + int xx = Mth::floor(mob->x); + int yy = Mth::floor(mob->y); + int zz = Mth::floor(mob->z); + + if (level->getTile(xx, yy, zz) == Tile::tallgrass_Id) + { + level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, xx, yy, zz, Tile::tallgrass_Id + (TallGrass::TALL_GRASS << Tile::TILE_NUM_SHIFT)); + level->setTile(xx, yy, zz, 0); + mob->ate(); + } + else if (level->getTile(xx, yy - 1, zz) == Tile::grass_Id) + { + level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, xx, yy - 1, zz, Tile::grass_Id); + level->setTile(xx, yy - 1, zz, Tile::dirt_Id); + mob->ate(); + } +} \ No newline at end of file diff --git a/Minecraft.World/EatTileGoal.h b/Minecraft.World/EatTileGoal.h new file mode 100644 index 00000000..00b607ae --- /dev/null +++ b/Minecraft.World/EatTileGoal.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Goal.h" +#include "SharedConstants.h" + +// note: Mob should implement handleEntityEvent for client state, also ate to take action upon eating +class EatTileGoal : public Goal +{ +private: + static const int EAT_ANIMATION_TICKS = SharedConstants::TICKS_PER_SECOND * 2; + + Mob *mob; // Owner of this goal + Level *level; + int eatAnimationTick; + +public: + EatTileGoal(Mob *mob); + + virtual bool canUse(); + virtual void start(); + virtual void stop(); + virtual bool canContinueToUse(); + virtual int getEatAnimationTick(); + virtual void tick(); + + // 4J Added override to update ai elements when loading entity from schematics + virtual void setLevel(Level *level) { this->level = level; } +}; \ No newline at end of file diff --git a/Minecraft.World/EggItem.cpp b/Minecraft.World/EggItem.cpp new file mode 100644 index 00000000..b85dbcd6 --- /dev/null +++ b/Minecraft.World/EggItem.cpp @@ -0,0 +1,31 @@ +using namespace std; + +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.global.h" +#include "net.minecraft.world.entity.projectile.h" +#include "EggItem.h" +#include "SoundTypes.h" + + +EggItem::EggItem(int id) : Item( id ) +{ + this->maxStackSize = 16; +} + +shared_ptr EggItem::use(shared_ptr instance, Level *level, shared_ptr player) +{ + if (!player->abilities.instabuild) + { + instance->count--; + } + level->playSound( dynamic_pointer_cast(player), eSoundType_RANDOM_BOW, 0.5f, 0.4f / (random->nextFloat() * 0.4f + 0.8f)); + if (!level->isClientSide) level->addEntity( shared_ptr( new ThrownEgg(level, dynamic_pointer_cast( player )) )); + return instance; +} diff --git a/Minecraft.World/EggItem.h b/Minecraft.World/EggItem.h new file mode 100644 index 00000000..83f09db5 --- /dev/null +++ b/Minecraft.World/EggItem.h @@ -0,0 +1,15 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class Player; +class Level; + +class EggItem : public Item +{ +public: + EggItem(int id); + + virtual shared_ptr use(shared_ptr instance, Level *level, shared_ptr player); +}; diff --git a/Minecraft.World/EggTile.cpp b/Minecraft.World/EggTile.cpp new file mode 100644 index 00000000..e6711a3f --- /dev/null +++ b/Minecraft.World/EggTile.cpp @@ -0,0 +1,173 @@ +#include "stdafx.h" +#include "EggTile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.entity.item.h" + +EggTile::EggTile(int id) : Tile(id, Material::egg, isSolidRender()) +{ +} + +void EggTile::onPlace(Level *level, int x, int y, int z) +{ + level->addToTickNextTick(x, y, z, id, getTickDelay()); +} + +void EggTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + level->addToTickNextTick(x, y, z, id, getTickDelay()); +} + +void EggTile::tick(Level *level, int x, int y, int z, Random *random) +{ + checkSlide(level, x, y, z); +} + +void EggTile::checkSlide(Level *level, int x, int y, int z) +{ + if (HeavyTile::isFree(level, x, y - 1, z) && y >= 0) + { + int r = 32; + if (HeavyTile::instaFall || !level->hasChunksAt(x - r, y - r, z - r, x + r, y + r, z + r)) + { + level->setTile(x, y, z, 0); + while (HeavyTile::isFree(level, x, y - 1, z) && y > 0) + y--; + if (y > 0) + { + level->setTile(x, y, z, id); + } + } + else + { + shared_ptr e = shared_ptr(new FallingTile(level, x + 0.5f, y + 0.5f, z + 0.5f, id)); + level->addEntity(e); + } + } +} + +bool EggTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if(soundOnly) return false; + + teleport(level, x, y, z); + return true; +} + +void EggTile::attack(Level *level, int x, int y, int z, shared_ptr player) +{ + teleport(level, x, y, z); +} + +void EggTile::teleport(Level *level, int x, int y, int z) +{ + if (level->getTile(x, y, z) != id) return; + + for (int i = 0; i < 1000; i++) + { + int xt = x + level->random->nextInt(16) - level->random->nextInt(16); + int yt = y + level->random->nextInt(8) - level->random->nextInt(8); + int zt = z + level->random->nextInt(16) - level->random->nextInt(16); + if (level->getTile(xt, yt, zt) == 0) + { + // Fix for TU9: Content: Art: Dragon egg teleport particle effect isn't present. + // Don't set tiles on client, and don't create particles on the server (matches later change in Java) + if(!level->isClientSide) + { + level->setTileAndData(xt, yt, zt, id, level->getData(x, y, z)); + level->setTile(x, y, z, 0); + + // 4J Stu - The PC version is wrong as the particles calculated on the client side will point towards a different + // location to the one where the egg has actually moved. As the deltas are all small we can pack them into an int + // See generateTeleportParticles for unpacking + char deltaX = x-xt; + char deltaY = y-yt; + char deltaZ = z-zt; + int deltas = 0|(deltaX&0xFF)|((deltaY&0xFF)<<8)|((deltaZ&0xFF)<<16); + + level->levelEvent(LevelEvent::END_EGG_TELEPORT,xt,yt,zt,deltas); + } + + // 4J Stu - This code will not work correctly on the client as it will show the particles going in the wrong direction + // and only for the player who attacks the egg + // else + // { + // int count = 128; + // for (int j = 0; j < count; j++) + // { + // double d = level->random->nextDouble(); // j < count / 2 ? 0 : + //// 1; + // float xa = (level->random->nextFloat() - 0.5f) * 0.2f; + // float ya = (level->random->nextFloat() - 0.5f) * 0.2f; + // float za = (level->random->nextFloat() - 0.5f) * 0.2f; + + // double _x = xt + (x - xt) * d + (level->random->nextDouble() - 0.5) * 1 + 0.5f; + // double _y = yt + (y - yt) * d + level->random->nextDouble() * 1 - 0.5f; + // double _z = zt + (z - zt) * d + (level->random->nextDouble() - 0.5) * 1 + 0.5f; + // level->addParticle(eParticleType_ender, _x, _y, _z, xa, ya, za); + // } + // } + return; + } + } +} + +int EggTile::getTickDelay() +{ + return 3; +} + +bool EggTile::blocksLight() +{ + return false; +} + +bool EggTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool EggTile::isCubeShaped() +{ + return false; +} + +int EggTile::getRenderShape() +{ + return Tile::SHAPE_EGG; +} + +int EggTile::cloneTileId(Level *level, int x, int y, int z) +{ + return 0; +} + +// 4J Added for Fix for #77475 - TU9: Content: Art: Dragon egg teleport particle effect isn't present. +void EggTile::generateTeleportParticles(Level *level,int xt,int yt, int zt,int deltas) +{ + int count = 128; + + // See above for packing + char deltaX = deltas&0xFF; + char deltaY = (deltas>>8)&0xFF; + char deltaZ = (deltas>>16)&0xFF; + + for (int j = 0; j < count; j++) + { + double d = level->random->nextDouble(); // j < count / 2 ? 0 : + // 1; + float xa = (level->random->nextFloat() - 0.5f) * 0.2f; + float ya = (level->random->nextFloat() - 0.5f) * 0.2f; + float za = (level->random->nextFloat() - 0.5f) * 0.2f; + + double _x = xt + deltaX * d + (level->random->nextDouble() - 0.5) * 1 + 0.5f; + double _y = yt + deltaY * d + level->random->nextDouble() * 1 - 0.5f; + double _z = zt + deltaZ * d + (level->random->nextDouble() - 0.5) * 1 + 0.5f; + level->addParticle(eParticleType_ender, _x, _y, _z, xa, ya, za); + } +} + +bool EggTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/EggTile.h b/Minecraft.World/EggTile.h new file mode 100644 index 00000000..c8929cbe --- /dev/null +++ b/Minecraft.World/EggTile.h @@ -0,0 +1,29 @@ +#pragma once +#include "Tile.h" + +class EggTile : public Tile +{ +public: + EggTile(int id); + virtual void onPlace(Level *level, int x, int y, int z); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual void tick(Level *level, int x, int y, int z, Random *random); +private: + void checkSlide(Level *level, int x, int y, int z); +public: + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + virtual void attack(Level *level, int x, int y, int z, shared_ptr player); +private: + void teleport(Level *level, int x, int y, int z); +public: + virtual int getTickDelay(); + virtual bool blocksLight(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + virtual int getRenderShape(); + virtual int cloneTileId(Level *level, int x, int y, int z); + + // 4J Added + static void generateTeleportParticles(Level *level,int xt,int yt, int zt,int deltas); +}; \ No newline at end of file diff --git a/Minecraft.World/Emboss.cpp b/Minecraft.World/Emboss.cpp new file mode 100644 index 00000000..ae49a0d3 --- /dev/null +++ b/Minecraft.World/Emboss.cpp @@ -0,0 +1,12 @@ +#include "stdafx.h" +#include "Emboss.h" + +Emboss::Emboss(Synth *synth) +{ + this->synth = synth; +} + +double Emboss::getValue(double x, double y) +{ + return synth->getValue(x, y) - synth->getValue(x + 1, y + 1); +} \ No newline at end of file diff --git a/Minecraft.World/Emboss.h b/Minecraft.World/Emboss.h new file mode 100644 index 00000000..2fbbf460 --- /dev/null +++ b/Minecraft.World/Emboss.h @@ -0,0 +1,13 @@ +#pragma once +#include "Synth.h" + +class Emboss: public Synth +{ +private: + Synth *synth; + +public: + Emboss(Synth *synth); + + virtual double getValue(double x, double y); +}; \ No newline at end of file diff --git a/Minecraft.World/EmptyLevelChunk.cpp b/Minecraft.World/EmptyLevelChunk.cpp new file mode 100644 index 00000000..80460b10 --- /dev/null +++ b/Minecraft.World/EmptyLevelChunk.cpp @@ -0,0 +1,222 @@ +#include "stdafx.h" +#include "Arrays.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "EmptyLevelChunk.h" + +EmptyLevelChunk::EmptyLevelChunk(Level *level, int x, int z) : LevelChunk(level,x,z) +{ + dontSave = true; + // Set this as fully post-processed, so we don't try and run post-processing on any edge chunks that will overlap into real chunks + terrainPopulated = LevelChunk::sTerrainPopulatedAllNeighbours | LevelChunk::sTerrainPostPostProcessed; +} + +EmptyLevelChunk::EmptyLevelChunk(Level *level, byteArray blocks, int x, int z): LevelChunk(level,blocks,x,z) +{ + dontSave = true; + delete [] blocks.data; + // Set this as fully post-processed, so we don't try and run post-processing on any edge chunks that will overlap into real chunks + terrainPopulated = LevelChunk::sTerrainPopulatedAllNeighbours | LevelChunk::sTerrainPostPostProcessed; +} + +bool EmptyLevelChunk::isAt(int x, int z) +{ + return x == this->x && z == this->z; +} + +int EmptyLevelChunk::getHeightmap(int x, int z) +{ + return 0; +} + +void EmptyLevelChunk::recalcBlockLights() +{ +} + +void EmptyLevelChunk::recalcHeightmapOnly() +{ +} + +void EmptyLevelChunk::recalcHeightmap() +{ +} + +void EmptyLevelChunk::lightLava() +{ +} + +int EmptyLevelChunk::getTile(int x, int y, int z) +{ + return 0; +} + +bool EmptyLevelChunk::setTileAndData(int x, int y, int z, int _tile, int _data) +{ + return true; +} + +bool EmptyLevelChunk::setTile(int x, int y, int z, int _tile) +{ + return true; +} + +int EmptyLevelChunk::getData(int x, int y, int z) +{ + return 0; +} + +bool EmptyLevelChunk::setData(int x, int y, int z, int val, int mask, bool *maskedBitsChanged) +{ + *maskedBitsChanged = true; + return false; +} + +int EmptyLevelChunk::getBrightness(LightLayer::variety layer, int x, int y, int z) +{ + return 0; +} + +// 4J added +void EmptyLevelChunk::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety layer, int x, int y, int z) +{ + for(int i = 0; i < 6; i++ ) + { + brightnesses[i] = 0; + } +} + +void EmptyLevelChunk::setBrightness(LightLayer::variety layer, int x, int y, int z, int brightness) +{ +} + +int EmptyLevelChunk::getRawBrightness(int x, int y, int z, int skyDampen) +{ + return 0; +} + +void EmptyLevelChunk::addEntity(shared_ptr e) +{ +} + +void EmptyLevelChunk::removeEntity(shared_ptr e) +{ +} + +void EmptyLevelChunk::removeEntity(shared_ptr e, int yc) +{ +} + +bool EmptyLevelChunk::isSkyLit(int x, int y, int z) +{ + return false; +} + +void EmptyLevelChunk::skyBrightnessChanged() +{ +} + +shared_ptr EmptyLevelChunk::getTileEntity(int x, int y, int z) +{ + return shared_ptr(); +} + +void EmptyLevelChunk::addTileEntity(shared_ptr te) +{ +} + +void EmptyLevelChunk::setTileEntity(int x, int y, int z, shared_ptr tileEntity) +{ +} + +void EmptyLevelChunk::removeTileEntity(int x, int y, int z) +{ +} + +void EmptyLevelChunk::load() +{ +} + +void EmptyLevelChunk::unload(bool unloadTileEntities) // 4J - added parameter +{ +} + +void EmptyLevelChunk::markUnsaved() +{ +} + +void EmptyLevelChunk::getEntities(shared_ptr except, AABB bb, vector > &es) +{ +} + +void EmptyLevelChunk::getEntitiesOfClass(const type_info& ec, AABB bb, vector > &es) +{ +} + +int EmptyLevelChunk::countEntities() +{ + return 0; +} + +bool EmptyLevelChunk::shouldSave(bool force) +{ + return false; +} + +void EmptyLevelChunk::setBlocks(byteArray newBlocks, int sub) +{ +} + +int EmptyLevelChunk::getBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p, bool includeLighting/* = true*/) +{ + int xs = x1 - x0; + int ys = y1 - y0; + int zs = z1 - z0; + + int s = xs * ys * zs; + int len; + if( includeLighting ) + { + len = s + s / 2 * 3; + } + else + { + len = s + s / 2; + } + + + Arrays::fill(data, p, p + len, (byte) 0); + return len; +} + +int EmptyLevelChunk::setBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p, bool includeLighting/* = true*/) +{ + int xs = x1 - x0; + int ys = y1 - y0; + int zs = z1 - z0; + + int s = xs * ys * zs; + if( includeLighting ) + { + return s + s / 2 * 3; + } + else + { + return s + s / 2; + } +} + +bool EmptyLevelChunk::testSetBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p) +{ + return false; +} + +Random *EmptyLevelChunk::getRandom(__int64 l) +{ + return new Random((level->getSeed() + x * x * 4987142 + x * 5947611 + z * z * 4392871l + z * 389711) ^ l); +} + +bool EmptyLevelChunk::isEmpty() +{ + return true; +} + diff --git a/Minecraft.World/EmptyLevelChunk.h b/Minecraft.World/EmptyLevelChunk.h new file mode 100644 index 00000000..02ffc176 --- /dev/null +++ b/Minecraft.World/EmptyLevelChunk.h @@ -0,0 +1,54 @@ +#pragma once +#include "LevelChunk.h" +#include "Definitions.h" + +class Level; + +class EmptyLevelChunk: public LevelChunk +{ +public: + using LevelChunk::getBlocksAndData; + using LevelChunk::getEntities; + using LevelChunk::getEntitiesOfClass; + + EmptyLevelChunk(Level *level, int x, int z); + EmptyLevelChunk(Level *level, byteArray blocks, int x, int z); + bool isAt(int x, int z); + int getHeightmap(int x, int z); + void recalcBlockLights(); + void recalcHeightmapOnly(); + void recalcHeightmap(); + void lightLava(); + int getTile(int x, int y, int z); + bool setTileAndData(int x, int y, int z, int _tile, int _data); + bool setTile(int x, int y, int z, int _tile); + int getData(int x, int y, int z); + bool setData(int x, int y, int z, int val, int mask, bool *maskedBitsChanged); // 4J added mask + int getBrightness(LightLayer::variety layer, int x, int y, int z); + void getNeighbourBrightnesses(int *brightnesses, LightLayer::variety layer, int x, int y, int z); // 4J added + void setBrightness(LightLayer::variety layer, int x, int y, int z, int brightness); + int getRawBrightness(int x, int y, int z, int skyDampen); + void addEntity(shared_ptr e); + void removeEntity(shared_ptr e); + void removeEntity(shared_ptr e, int yc); + bool isSkyLit(int x, int y, int z); + void skyBrightnessChanged(); + shared_ptr getTileEntity(int x, int y, int z); + void addTileEntity(shared_ptr te); + void setTileEntity(int x, int y, int z, shared_ptr tileEntity); + void removeTileEntity(int x, int y, int z); + void load(); + void unload(bool unloadTileEntities) ; // 4J - added parameter + void markUnsaved(); + void getEntities(shared_ptr except, AABB bb, vector > &es); + void getEntitiesOfClass(const type_info& ec, AABB bb, vector > &es); + int countEntities(); + bool shouldSave(bool force); + void setBlocks(byteArray newBlocks, int sub); + int getBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p, bool includeLighting = true); // 4J - added includeLighting parameter + int setBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p, bool includeLighting = true); // 4J - added includeLighting parameter + bool testSetBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p); // 4J added + Random *getRandom(__int64 l); + bool isEmpty(); + virtual void reSyncLighting() {}; // 4J added +}; diff --git a/Minecraft.World/EnchantItemCommand.cpp b/Minecraft.World/EnchantItemCommand.cpp new file mode 100644 index 00000000..aa02b533 --- /dev/null +++ b/Minecraft.World/EnchantItemCommand.cpp @@ -0,0 +1,85 @@ +#include "stdafx.h" +#include "net.minecraft.network.packet.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "..\Minecraft.Client\ServerPlayer.h" +#include "EnchantItemCommand.h" + +EGameCommand EnchantItemCommand::getId() +{ + return eGameCommand_EnchantItem; +} + +int EnchantItemCommand::getPermissionLevel() +{ + return 0; //aLEVEL_GAMEMASTERS; +} + +void EnchantItemCommand::execute(shared_ptr source, byteArray commandData) +{ + ByteArrayInputStream bais(commandData); + DataInputStream dis(&bais); + + PlayerUID uid = dis.readPlayerUID(); + int enchantmentId = dis.readInt(); + int enchantmentLevel = dis.readInt(); + + bais.reset(); + + shared_ptr player = getPlayer(uid); + + if(player == NULL) return; + + shared_ptr selectedItem = player->getSelectedItem(); + + if(selectedItem == NULL) return; + + Enchantment *e = Enchantment::enchantments[enchantmentId]; + + if(e == NULL) return; + if(!e->canEnchant(selectedItem)) return; + + if(enchantmentLevel < e->getMinLevel()) enchantmentLevel = e->getMinLevel(); + if(enchantmentLevel > e->getMaxLevel()) enchantmentLevel = e->getMaxLevel(); + + if (selectedItem->hasTag()) + { + ListTag *enchantmentTags = selectedItem->getEnchantmentTags(); + if (enchantmentTags != NULL) + { + for (int i = 0; i < enchantmentTags->size(); i++) + { + int type = enchantmentTags->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_ID); + + if (Enchantment::enchantments[type] != NULL) + { + Enchantment *other = Enchantment::enchantments[type]; + if (!other->isCompatibleWith(e)) + { + return; + //throw new CommandException("commands.enchant.cantCombine", e.getFullname(level), other.getFullname(enchantmentTags.get(i).getShort(ItemInstance.TAG_ENCH_LEVEL))); + } + } + } + } + } + + selectedItem->enchant(e, enchantmentLevel); + + //logAdminAction(source, "commands.enchant.success"); + logAdminAction(source, ChatPacket::e_ChatCustom, L"commands.enchant.success"); +} + +shared_ptr EnchantItemCommand::preparePacket(shared_ptr player, int enchantmentId, int enchantmentLevel) +{ + if(player == NULL) return nullptr; + + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + dos.writePlayerUID(player->getXuid()); + dos.writeInt(enchantmentId); + dos.writeInt(enchantmentLevel); + + return shared_ptr( new GameCommandPacket(eGameCommand_EnchantItem, baos.toByteArray() )); +} \ No newline at end of file diff --git a/Minecraft.World/EnchantItemCommand.h b/Minecraft.World/EnchantItemCommand.h new file mode 100644 index 00000000..5fc6c648 --- /dev/null +++ b/Minecraft.World/EnchantItemCommand.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Command.h" + +class GameCommandPacket; + +class EnchantItemCommand : public Command +{ +public: + virtual EGameCommand getId(); + int getPermissionLevel(); + virtual void execute(shared_ptr source, byteArray commandData); + + static shared_ptr preparePacket(shared_ptr player, int enchantmentId, int enchantmentLevel = 1); +}; \ No newline at end of file diff --git a/Minecraft.World/EnchantedBookItem.cpp b/Minecraft.World/EnchantedBookItem.cpp new file mode 100644 index 00000000..59e7156b --- /dev/null +++ b/Minecraft.World/EnchantedBookItem.cpp @@ -0,0 +1,144 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.item.h" +#include "WeighedTreasure.h" +#include "EnchantedBookItem.h" + +const wstring EnchantedBookItem::TAG_STORED_ENCHANTMENTS = L"StoredEnchantments"; + +EnchantedBookItem::EnchantedBookItem(int id) : Item(id) +{ +} + +bool EnchantedBookItem::isFoil(shared_ptr itemInstance) +{ + return true; +} + +bool EnchantedBookItem::isEnchantable(shared_ptr itemInstance) +{ + return false; +} + +const Rarity *EnchantedBookItem::getRarity(shared_ptr itemInstance) +{ + ListTag *enchantments = getEnchantments(itemInstance); + if (enchantments && enchantments->size() > 0) + { + return Rarity::uncommon; + } + else + { + return Item::getRarity(itemInstance); + } +} + +ListTag *EnchantedBookItem::getEnchantments(shared_ptr item) +{ + if (item->tag == NULL || !item->tag->contains((wchar_t *)TAG_STORED_ENCHANTMENTS.c_str())) + { + return new ListTag(); + } + + return (ListTag *) item->tag->get((wchar_t *)TAG_STORED_ENCHANTMENTS.c_str()); +} + +void EnchantedBookItem::appendHoverText(shared_ptr itemInstance, shared_ptr player, vector *lines, bool advanced, vector &unformattedStrings) +{ + Item::appendHoverText(itemInstance, player, lines, advanced, unformattedStrings); + + ListTag *list = getEnchantments(itemInstance); + + if (list != NULL) + { + wstring unformatted = L""; + for (int i = 0; i < list->size(); i++) + { + int type = list->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_ID); + int level = list->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_LEVEL); + + if (Enchantment::enchantments[type] != NULL) + { + lines->push_back(Enchantment::enchantments[type]->getFullname(level, unformatted)); + unformattedStrings.push_back(unformatted); + } + } + } +} + +void EnchantedBookItem::addEnchantment(shared_ptr item, EnchantmentInstance *enchantment) +{ + ListTag *enchantments = getEnchantments(item); + bool add = true; + + for (int i = 0; i < enchantments->size(); i++) + { + CompoundTag *tag = enchantments->get(i); + + if (tag->getShort((wchar_t *)ItemInstance::TAG_ENCH_ID) == enchantment->enchantment->id) + { + if (tag->getShort((wchar_t *)ItemInstance::TAG_ENCH_LEVEL) < enchantment->level) + { + tag->putShort((wchar_t *)ItemInstance::TAG_ENCH_LEVEL, (short) enchantment->level); + } + + add = false; + break; + } + } + + if (add) + { + CompoundTag *tag = new CompoundTag(); + + tag->putShort((wchar_t *)ItemInstance::TAG_ENCH_ID, (short) enchantment->enchantment->id); + tag->putShort((wchar_t *)ItemInstance::TAG_ENCH_LEVEL, (short) enchantment->level); + + enchantments->add(tag); + } + + if (!item->hasTag()) item->setTag(new CompoundTag()); + item->getTag()->put((wchar_t *)TAG_STORED_ENCHANTMENTS.c_str(), enchantments); +} + +shared_ptr EnchantedBookItem::createForEnchantment(EnchantmentInstance *enchant) +{ + shared_ptr item = shared_ptr(new ItemInstance(this)); + addEnchantment(item, enchant); + return item; +} + +void EnchantedBookItem::createForEnchantment(Enchantment *enchant, vector > *items) +{ + for (int i = enchant->getMinLevel(); i <= enchant->getMaxLevel(); i++) + { + items->push_back(createForEnchantment(new EnchantmentInstance(enchant, i))); + } +} + +shared_ptr EnchantedBookItem::createForRandomLoot(Random *random) +{ + Enchantment *enchantment = Enchantment::validEnchantments[random->nextInt(Enchantment::validEnchantments.size())]; + shared_ptr book = shared_ptr(new ItemInstance(id, 1, 0)); + int level = Mth::nextInt(random, enchantment->getMinLevel(), enchantment->getMaxLevel()); + + addEnchantment(book, new EnchantmentInstance(enchantment, level)); + + return book; +} + +WeighedTreasure *EnchantedBookItem::createForRandomTreasure(Random *random) +{ + return createForRandomTreasure(random, 1, 1, 1); +} + +WeighedTreasure *EnchantedBookItem::createForRandomTreasure(Random *random, int minCount, int maxCount, int weight) +{ + Enchantment *enchantment = Enchantment::validEnchantments[random->nextInt(Enchantment::validEnchantments.size())]; + shared_ptr book = shared_ptr(new ItemInstance(id, 1, 0)); + int level = Mth::nextInt(random, enchantment->getMinLevel(), enchantment->getMaxLevel()); + + addEnchantment(book, new EnchantmentInstance(enchantment, level)); + + return new WeighedTreasure(book, minCount, maxCount, weight); +} \ No newline at end of file diff --git a/Minecraft.World/EnchantedBookItem.h b/Minecraft.World/EnchantedBookItem.h new file mode 100644 index 00000000..c67f208f --- /dev/null +++ b/Minecraft.World/EnchantedBookItem.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Item.h" + +class EnchantmentInstance; + +class EnchantedBookItem : public Item +{ +public: + static const wstring TAG_STORED_ENCHANTMENTS; + + EnchantedBookItem(int id); + + bool isFoil(shared_ptr itemInstance); + bool isEnchantable(shared_ptr itemInstance); + const Rarity *getRarity(shared_ptr itemInstance); + ListTag *getEnchantments(shared_ptr item); + void appendHoverText(shared_ptr itemInstance, shared_ptr player, vector *lines, bool advanced, vector &unformattedStrings); + void addEnchantment(shared_ptr item, EnchantmentInstance *enchantment); + shared_ptr createForEnchantment(EnchantmentInstance *enchant); + void createForEnchantment(Enchantment *enchant, vector > *items); + shared_ptr createForRandomLoot(Random *random); + WeighedTreasure *createForRandomTreasure(Random *random); + WeighedTreasure *createForRandomTreasure(Random *random, int minCount, int maxCount, int weight); +}; \ No newline at end of file diff --git a/Minecraft.World/Enchantment.cpp b/Minecraft.World/Enchantment.cpp new file mode 100644 index 00000000..2ce1441a --- /dev/null +++ b/Minecraft.World/Enchantment.cpp @@ -0,0 +1,202 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.enchantment.h" +#include "Enchantment.h" + +//Enchantment *Enchantment::enchantments[256]; +EnchantmentArray Enchantment::enchantments = EnchantmentArray( 256 ); +vector Enchantment::validEnchantments; + +Enchantment *Enchantment::allDamageProtection = NULL; +Enchantment *Enchantment::fireProtection = NULL; +Enchantment *Enchantment::fallProtection = NULL; +Enchantment *Enchantment::explosionProtection = NULL; +Enchantment *Enchantment::projectileProtection = NULL; +Enchantment *Enchantment::drownProtection = NULL; +Enchantment *Enchantment::waterWorker = NULL; +Enchantment *Enchantment::thorns = NULL; + +// weapon +Enchantment *Enchantment::damageBonus = NULL; +Enchantment *Enchantment::damageBonusUndead = NULL; +Enchantment *Enchantment::damageBonusArthropods = NULL; +Enchantment *Enchantment::knockback = NULL; +Enchantment *Enchantment::fireAspect = NULL; +Enchantment *Enchantment::lootBonus = NULL; + +// digger +Enchantment *Enchantment::diggingBonus = NULL; +Enchantment *Enchantment::untouching = NULL; +Enchantment *Enchantment::digDurability = NULL; +Enchantment *Enchantment::resourceBonus = NULL; + +// bows +Enchantment *Enchantment::arrowBonus = NULL; +Enchantment *Enchantment::arrowKnockback = NULL; +Enchantment *Enchantment::arrowFire = NULL; +Enchantment *Enchantment::arrowInfinite = NULL; + +void Enchantment::staticCtor() +{ + allDamageProtection = new ProtectionEnchantment(0, FREQ_COMMON, ProtectionEnchantment::ALL); + fireProtection = new ProtectionEnchantment(1, FREQ_UNCOMMON, ProtectionEnchantment::FIRE); + fallProtection = new ProtectionEnchantment(2, FREQ_UNCOMMON, ProtectionEnchantment::FALL); + explosionProtection = new ProtectionEnchantment(3, FREQ_RARE, ProtectionEnchantment::EXPLOSION); + projectileProtection = new ProtectionEnchantment(4, FREQ_UNCOMMON, ProtectionEnchantment::PROJECTILE); + drownProtection = new OxygenEnchantment(5, FREQ_RARE); + waterWorker = new WaterWorkerEnchantment(6, FREQ_RARE); + thorns = new ThornsEnchantment(7, FREQ_VERY_RARE); + + // weapon + damageBonus = new DamageEnchantment(16, FREQ_COMMON, DamageEnchantment::ALL); + damageBonusUndead = new DamageEnchantment(17, FREQ_UNCOMMON, DamageEnchantment::UNDEAD); + damageBonusArthropods = new DamageEnchantment(18, FREQ_UNCOMMON, DamageEnchantment::ARTHROPODS); + knockback = new KnockbackEnchantment(19, FREQ_UNCOMMON); + fireAspect = new FireAspectEnchantment(20, FREQ_RARE); + lootBonus = new LootBonusEnchantment(21, FREQ_RARE, EnchantmentCategory::weapon); + + // digger + diggingBonus = new DiggingEnchantment(32, FREQ_COMMON); + untouching = new UntouchingEnchantment(33, FREQ_VERY_RARE); + digDurability = new DigDurabilityEnchantment(34, FREQ_UNCOMMON); + resourceBonus = new LootBonusEnchantment(35, FREQ_RARE, EnchantmentCategory::digger); + + // bows + arrowBonus = new ArrowDamageEnchantment(48, FREQ_COMMON); + arrowKnockback = new ArrowKnockbackEnchantment(49, FREQ_RARE); + arrowFire = new ArrowFireEnchantment(50, FREQ_RARE); + arrowInfinite = new ArrowInfiniteEnchantment(51, FREQ_VERY_RARE); + + for(unsigned int i = 0; i < 256; ++i) + { + Enchantment *enchantment = enchantments[i]; + if (enchantment != NULL) + { + validEnchantments.push_back(enchantment); + } + } +} + +void Enchantment::_init(int id) +{ + if (enchantments[id] != NULL) + { + app.DebugPrintf("Duplicate enchantment id!"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + //throw new IllegalArgumentException("Duplicate enchantment id!"); + } + enchantments[id] = this; +} + +Enchantment::Enchantment(int id, int frequency, const EnchantmentCategory *category) : id(id), frequency(frequency), category(category) +{ + _init(id); +} + +Enchantment::Enchantment(int id) : id(id), frequency(FREQ_COMMON), category(EnchantmentCategory::all) +{ + _init(id); +} + +int Enchantment::getFrequency() +{ + return frequency; +} + +int Enchantment::getMinLevel() +{ + return 1; +} + +int Enchantment::getMaxLevel() +{ + return 1; +} + +int Enchantment::getMinCost(int level) +{ + return 1 + level * 10; +} + +int Enchantment::getMaxCost(int level) +{ + return getMinCost(level) + 5; +} + +int Enchantment::getDamageProtection(int level, DamageSource *source) +{ + return 0; +} + +int Enchantment::getDamageBonus(int level, shared_ptr target) +{ + return 0; +} + +bool Enchantment::isCompatibleWith(Enchantment *other) const +{ + return this != other; +} + +Enchantment *Enchantment::setDescriptionId(int id) +{ + descriptionId = id; + return this; +} + +int Enchantment::getDescriptionId() +{ + return descriptionId; +} + +wstring Enchantment::getFullname(int level,wstring &unformatted) +{ + wchar_t formatted[256]; + swprintf(formatted,256,L"%ls %ls",app.GetString( getDescriptionId() ), getLevelString(level).c_str()); + unformatted = formatted; + swprintf(formatted,256,L"%ls",app.GetHTMLColour(eHTMLColor_f),unformatted.c_str()); + return formatted; +} + +bool Enchantment::canEnchant(shared_ptr item) +{ + return category->canEnchant(item->getItem()); +} + +// 4J Added +wstring Enchantment::getLevelString(int level) +{ + int stringId = IDS_ENCHANTMENT_LEVEL_1; + switch(level) + { + case 2: + stringId = IDS_ENCHANTMENT_LEVEL_2; + break; + case 3: + stringId = IDS_ENCHANTMENT_LEVEL_3; + break; + case 4: + stringId = IDS_ENCHANTMENT_LEVEL_4; + break; + case 5: + stringId = IDS_ENCHANTMENT_LEVEL_5; + break; + case 6: + stringId = IDS_ENCHANTMENT_LEVEL_6; + break; + case 7: + stringId = IDS_ENCHANTMENT_LEVEL_7; + break; + case 8: + stringId = IDS_ENCHANTMENT_LEVEL_8; + break; + case 9: + stringId = IDS_ENCHANTMENT_LEVEL_9; + break; + case 10: + stringId = IDS_ENCHANTMENT_LEVEL_10; + break; + }; + return app.GetString(stringId); //I18n.get("enchantment.level." + level); +} \ No newline at end of file diff --git a/Minecraft.World/Enchantment.h b/Minecraft.World/Enchantment.h new file mode 100644 index 00000000..794edf85 --- /dev/null +++ b/Minecraft.World/Enchantment.h @@ -0,0 +1,87 @@ +#pragma once + +#include "EnchantmentCategory.h" + +class DamageSource; +class Mob; + +class Enchantment //implements Descriptive { +{ +public : + //static Enchantment *enchantments[256]; + static EnchantmentArray enchantments; + static vector validEnchantments; + + static const int FREQ_COMMON = 10; + static const int FREQ_UNCOMMON = 5; + static const int FREQ_RARE = 2; + static const int FREQ_VERY_RARE = 1; + + // armor + static Enchantment *allDamageProtection; + static Enchantment *fireProtection; + static Enchantment *fallProtection; + static Enchantment *explosionProtection; + static Enchantment *projectileProtection; + static Enchantment *drownProtection; + static Enchantment *waterWorker; + static Enchantment *thorns; + + // weapon + static Enchantment *damageBonus; + static Enchantment *damageBonusUndead; + static Enchantment *damageBonusArthropods; + static Enchantment *knockback; + static Enchantment *fireAspect; + static Enchantment *lootBonus; + + // digger + static Enchantment *diggingBonus; + static Enchantment *untouching; + static Enchantment *digDurability; + static Enchantment *resourceBonus; + + // bows + static Enchantment *arrowBonus; + static Enchantment *arrowKnockback; + static Enchantment *arrowFire; + static Enchantment *arrowInfinite; + + const int id; + + static void staticCtor(); + +private: + const int frequency; + +public: + const EnchantmentCategory *category; + +protected: + int descriptionId; + +private: + void _init(int id); + +protected: + Enchantment(int id, int frequency, const EnchantmentCategory *category); + Enchantment(int id); + +public: + virtual int getFrequency(); + virtual int getMinLevel(); + virtual int getMaxLevel(); + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getDamageProtection(int level, DamageSource *source); + virtual int getDamageBonus(int level, shared_ptr target); + virtual bool isCompatibleWith(Enchantment *other) const; + virtual Enchantment *setDescriptionId(int id); + virtual int getDescriptionId(); + virtual wstring getFullname(int level,wstring &unformatted); // 4J Stu added unformatted + virtual bool canEnchant(shared_ptr item); + +private: + // 4J Added + wstring getLevelString(int level); +}; \ No newline at end of file diff --git a/Minecraft.World/EnchantmentCategory.cpp b/Minecraft.World/EnchantmentCategory.cpp new file mode 100644 index 00000000..7fb44481 --- /dev/null +++ b/Minecraft.World/EnchantmentCategory.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "EnchantmentCategory.h" + +const EnchantmentCategory *EnchantmentCategory::all = new EnchantmentCategory(); +const EnchantmentCategory *EnchantmentCategory::armor = new EnchantmentCategory(); +const EnchantmentCategory *EnchantmentCategory::armor_feet = new EnchantmentCategory(); +const EnchantmentCategory *EnchantmentCategory::armor_legs = new EnchantmentCategory(); +const EnchantmentCategory *EnchantmentCategory::armor_torso = new EnchantmentCategory(); +const EnchantmentCategory *EnchantmentCategory::armor_head = new EnchantmentCategory(); +const EnchantmentCategory *EnchantmentCategory::weapon = new EnchantmentCategory(); +const EnchantmentCategory *EnchantmentCategory::digger = new EnchantmentCategory(); +const EnchantmentCategory *EnchantmentCategory::bow = new EnchantmentCategory(); + +bool EnchantmentCategory::canEnchant(Item *item) const +{ + if (this == all) return true; + + if (dynamic_cast( item ) != NULL) + { + if (this == armor) return true; + ArmorItem *ai = (ArmorItem *) item; + if (ai->slot == ArmorItem::SLOT_HEAD) return this == armor_head; + if (ai->slot == ArmorItem::SLOT_LEGS) return this == armor_legs; + if (ai->slot == ArmorItem::SLOT_TORSO) return this == armor_torso; + if (ai->slot == ArmorItem::SLOT_FEET) return this == armor_feet; + return false; + } + else if (dynamic_cast(item) != NULL) + { + return this == weapon; + } + else if (dynamic_cast(item) != NULL) + { + return this == digger; + } + else if (dynamic_cast(item) != NULL) + { + return this == bow; + } + return false; +} \ No newline at end of file diff --git a/Minecraft.World/EnchantmentCategory.h b/Minecraft.World/EnchantmentCategory.h new file mode 100644 index 00000000..b5976307 --- /dev/null +++ b/Minecraft.World/EnchantmentCategory.h @@ -0,0 +1,19 @@ +#pragma once + +class Item; + +class EnchantmentCategory +{ +public: + static const EnchantmentCategory *all; + static const EnchantmentCategory *armor; + static const EnchantmentCategory *armor_feet; + static const EnchantmentCategory *armor_legs; + static const EnchantmentCategory *armor_torso; + static const EnchantmentCategory *armor_head; + static const EnchantmentCategory *weapon; + static const EnchantmentCategory *digger; + static const EnchantmentCategory *bow; + + bool canEnchant(Item *item) const; +}; \ No newline at end of file diff --git a/Minecraft.World/EnchantmentContainer.cpp b/Minecraft.World/EnchantmentContainer.cpp new file mode 100644 index 00000000..6e5541fb --- /dev/null +++ b/Minecraft.World/EnchantmentContainer.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" +#include "net.minecraft.world.inventory.h" +#include "EnchantmentContainer.h" + +EnchantmentContainer::EnchantmentContainer(EnchantmentMenu *menu) : SimpleContainer(IDS_ENCHANT, 1), m_menu( menu ) +{ +} + +int EnchantmentContainer::getMaxStackSize() +{ + return 1; +} + +void EnchantmentContainer::setChanged() +{ + SimpleContainer::setChanged(); + m_menu->slotsChanged(); // Remove this param as it's not needed +} \ No newline at end of file diff --git a/Minecraft.World/EnchantmentContainer.h b/Minecraft.World/EnchantmentContainer.h new file mode 100644 index 00000000..48d8687f --- /dev/null +++ b/Minecraft.World/EnchantmentContainer.h @@ -0,0 +1,18 @@ +#pragma once +// 4J Stu Added +// In EnchantmentMenu.java they create an anoymous class while creating the container. I have moved the content +// of that anonymous class to here + +#include "SimpleContainer.h" + +class EnchantmentMenu; + +class EnchantmentContainer : public SimpleContainer +{ +private: + EnchantmentMenu *m_menu; +public: + EnchantmentContainer(EnchantmentMenu *menu); + virtual int getMaxStackSize(); + virtual void setChanged(); +}; \ No newline at end of file diff --git a/Minecraft.World/EnchantmentHelper.cpp b/Minecraft.World/EnchantmentHelper.cpp new file mode 100644 index 00000000..f43ced01 --- /dev/null +++ b/Minecraft.World/EnchantmentHelper.cpp @@ -0,0 +1,477 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.damagesource.h" +#include "WeighedRandom.h" +#include "EnchantmentHelper.h" + +Random EnchantmentHelper::random; + +int EnchantmentHelper::getEnchantmentLevel(int enchantmentId, shared_ptr piece) +{ + if (piece == NULL) + { + return 0; + } + ListTag *enchantmentTags = piece->getEnchantmentTags(); + if (enchantmentTags == NULL) + { + return 0; + } + for (int i = 0; i < enchantmentTags->size(); i++) + { + int type = enchantmentTags->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_ID); + int level = enchantmentTags->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_LEVEL); + + if (type == enchantmentId) + { + return level; + } + } + return 0; +} + +unordered_map *EnchantmentHelper::getEnchantments(shared_ptr item) +{ + unordered_map *result = new unordered_map(); + ListTag *list = item->id == Item::enchantedBook_Id ? Item::enchantedBook->getEnchantments(item) : item->getEnchantmentTags(); + + if (list != NULL) + { + for (int i = 0; i < list->size(); i++) + { + int type = list->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_ID); + int level = list->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_LEVEL); + + result->insert( unordered_map::value_type(type, level)); + } + } + + return result; +} + +void EnchantmentHelper::setEnchantments(unordered_map *enchantments, shared_ptr item) +{ + ListTag *list = new ListTag(); + + //for (int id : enchantments.keySet()) + for(AUTO_VAR(it, enchantments->begin()); it != enchantments->end(); ++it) + { + int id = it->first; + CompoundTag *tag = new CompoundTag(); + + tag->putShort((wchar_t *)ItemInstance::TAG_ENCH_ID, (short) id); + tag->putShort((wchar_t *)ItemInstance::TAG_ENCH_LEVEL, (short)(int)it->second); + + list->add(tag); + + if (item->id == Item::enchantedBook_Id) + { + Item::enchantedBook->addEnchantment(item, new EnchantmentInstance(id, it->second)); + } + } + + if (list->size() > 0) + { + if (item->id != Item::enchantedBook_Id) + { + item->addTagElement(L"ench", list); + } + } + else if (item->hasTag()) + { + item->getTag()->remove(L"ench"); + } +} + +int EnchantmentHelper::getEnchantmentLevel(int enchantmentId, ItemInstanceArray inventory) +{ + int bestLevel = 0; + //for (ItemInstance piece : inventory) + for(unsigned int i = 0; i < inventory.length; ++i) + { + int newLevel = getEnchantmentLevel(enchantmentId, inventory[i]); + if (newLevel > bestLevel) + { + bestLevel = newLevel; + } + } + return bestLevel; +} + +void EnchantmentHelper::runIterationOnItem(EnchantmentIterationMethod &method, shared_ptr piece) +{ + if (piece == NULL) + { + return; + } + ListTag *enchantmentTags = piece->getEnchantmentTags(); + if (enchantmentTags == NULL) + { + return; + } + for (int i = 0; i < enchantmentTags->size(); i++) + { + int type = enchantmentTags->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_ID); + int level = enchantmentTags->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_LEVEL); + + if (Enchantment::enchantments[type] != NULL) + { + method.doEnchantment(Enchantment::enchantments[type], level); + } + } +} + +void EnchantmentHelper::runIterationOnInventory(EnchantmentIterationMethod &method, ItemInstanceArray inventory) +{ + //for (ItemInstance piece : inventory) + for(unsigned int i = 0; i < inventory.length; ++i) + { + runIterationOnItem(method, inventory[i]); + } +} + +void EnchantmentHelper::GetDamageProtectionIteration::doEnchantment(Enchantment *enchantment, int level) +{ + sum += enchantment->getDamageProtection(level, source); +} + +EnchantmentHelper::GetDamageProtectionIteration EnchantmentHelper::getDamageProtectionIteration; + +/** +* Fetches the protection value for enchanted items. +* +* @param inventory +* @param source +* @return +*/ +int EnchantmentHelper::getDamageProtection(shared_ptr inventory, DamageSource *source) +{ + getDamageProtectionIteration.sum = 0; + getDamageProtectionIteration.source = source; + + runIterationOnInventory(getDamageProtectionIteration, inventory->armor); + + if (getDamageProtectionIteration.sum > 25) + { + getDamageProtectionIteration.sum = 25; + } + // enchantment protection is on the scale of 0 to 25, where 20 or more + // will nullify nearly all damage (there will be damage spill) + return ((getDamageProtectionIteration.sum + 1) >> 1) + random.nextInt((getDamageProtectionIteration.sum >> 1) + 1); +} + +void EnchantmentHelper::GetDamageBonusIteration::doEnchantment(Enchantment *enchantment, int level) +{ + sum += enchantment->getDamageBonus(level, target); +} + + +EnchantmentHelper::GetDamageBonusIteration EnchantmentHelper::getDamageBonusIteration; + +/** +* +* @param inventory +* @param target +* @return +*/ +int EnchantmentHelper::getDamageBonus(shared_ptr inventory, shared_ptr target) +{ + + getDamageBonusIteration.sum = 0; + getDamageBonusIteration.target = target; + + runIterationOnItem(getDamageBonusIteration, inventory->getSelected()); + + if (getDamageBonusIteration.sum > 0) + { + return 1 + random.nextInt(getDamageBonusIteration.sum); + } + return 0; +} + +int EnchantmentHelper::getKnockbackBonus(shared_ptr inventory, shared_ptr target) +{ + return getEnchantmentLevel(Enchantment::knockback->id, inventory->getSelected()); +} + +int EnchantmentHelper::getFireAspect(shared_ptr source) +{ + return getEnchantmentLevel(Enchantment::fireAspect->id, source->getCarriedItem()); +} + +int EnchantmentHelper::getOxygenBonus(shared_ptr inventory) +{ + return getEnchantmentLevel(Enchantment::drownProtection->id, inventory->armor); +} + +int EnchantmentHelper::getDiggingBonus(shared_ptr inventory) +{ + return getEnchantmentLevel(Enchantment::diggingBonus->id, inventory->getSelected()); +} + +int EnchantmentHelper::getDigDurability(shared_ptr inventory) +{ + return getEnchantmentLevel(Enchantment::digDurability->id, inventory->getSelected()); +} + +bool EnchantmentHelper::hasSilkTouch(shared_ptr inventory) +{ + return getEnchantmentLevel(Enchantment::untouching->id, inventory->getSelected()) > 0; +} + +int EnchantmentHelper::getDiggingLootBonus(shared_ptr inventory) +{ + return getEnchantmentLevel(Enchantment::resourceBonus->id, inventory->getSelected()); +} + +int EnchantmentHelper::getKillingLootBonus(shared_ptr inventory) +{ + return getEnchantmentLevel(Enchantment::lootBonus->id, inventory->getSelected()); +} + +bool EnchantmentHelper::hasWaterWorkerBonus(shared_ptr inventory) +{ + return getEnchantmentLevel(Enchantment::waterWorker->id, inventory->armor) > 0; +} + +int EnchantmentHelper::getArmorThorns(shared_ptr source) +{ + return getEnchantmentLevel(Enchantment::thorns->id, source->getEquipmentSlots()); +} + +shared_ptr EnchantmentHelper::getRandomItemWith(Enchantment *enchantment, shared_ptr source) +{ + ItemInstanceArray items = source->getEquipmentSlots(); + for(unsigned int i = 0; i < items.length; ++i) + { + shared_ptr item = items[i]; + if (item != NULL && getEnchantmentLevel(enchantment->id, item) > 0) + { + return item; + } + } + + return nullptr; +} + +/** +* +* @param random +* @param slot +* The table slot, 0-2 +* @param bookcases +* How many book cases that are found around the table. +* @param itemInstance +* Which item that is being enchanted. +* @return The enchantment cost, 0 means unchantable, 50 is max. +*/ +int EnchantmentHelper::getEnchantmentCost(Random *random, int slot, int bookcases, shared_ptr itemInstance) +{ + Item *item = itemInstance->getItem(); + int itemValue = item->getEnchantmentValue(); + + if (itemValue <= 0) + { + // not enchantable + return 0; + } + + // 4J Stu - Updated function to 1.3 version for TU7 + if (bookcases > 15) + { + bookcases = 15; + } + + int selected = random->nextInt(8) + 1 + (bookcases >> 1) + random->nextInt(bookcases + 1); + if (slot == 0) + { + return max((selected / 3), 1); + } + if (slot == 1) + { + return max(selected, bookcases * 2); + } + return selected; +} + +shared_ptr EnchantmentHelper::enchantItem(Random *random, shared_ptr itemInstance, int enchantmentCost) +{ + vector *newEnchantment = EnchantmentHelper::selectEnchantment(random, itemInstance, enchantmentCost); + bool isBook = itemInstance->id == Item::book_Id; + + if (isBook) itemInstance->id = Item::enchantedBook_Id; + + if (newEnchantment != NULL) + { + for(AUTO_VAR(it, newEnchantment->begin()); it != newEnchantment->end(); ++it) + { + EnchantmentInstance *e = *it; + if (isBook) + { + Item::enchantedBook->addEnchantment(itemInstance, e); + } + else + { + itemInstance->enchant(e->enchantment, e->level); + } + delete e; + } + delete newEnchantment; + } + return itemInstance; +} + +/** +* +* @param random +* @param itemInstance +* @param enchantmentCost +* @return +*/ +vector *EnchantmentHelper::selectEnchantment(Random *random, shared_ptr itemInstance, int enchantmentCost) +{ + // withdraw bonus from item + Item *item = itemInstance->getItem(); + int itemBonus = item->getEnchantmentValue(); + + if (itemBonus <= 0) + { + return NULL; + } + // 4J Stu - Update function to 1.3 version for TU7 + itemBonus /= 2; + itemBonus = 1 + random->nextInt((itemBonus >> 1) + 1) + random->nextInt((itemBonus >> 1) + 1); + + int enchantmentValue = itemBonus + enchantmentCost; + + // the final enchantment cost will have another random span of +- 15% + float deviation = (random->nextFloat() + random->nextFloat() - 1.0f) * .15f; + int realValue = (int) ((float) enchantmentValue * (1.0f + deviation) + .5f); + if (realValue < 1) + { + realValue = 1; + } + + vector *results = NULL; + + unordered_map *availableEnchantments = getAvailableEnchantmentResults(realValue, itemInstance); + if (availableEnchantments != NULL && !availableEnchantments->empty()) + { + vector values; + for(AUTO_VAR(it, availableEnchantments->begin()); it != availableEnchantments->end(); ++it) + { + values.push_back(it->second); + } + EnchantmentInstance *instance = (EnchantmentInstance *) WeighedRandom::getRandomItem(random, &values); + values.clear(); + + if (instance != NULL) + { + results = new vector(); + results->push_back( instance->copy() ); // 4J Stu - Inserting a copy so we can clear memory from the availableEnchantments collection + + int bonusChance = realValue; + while (random->nextInt(50) <= bonusChance) + { + + // remove incompatible enchantments from previous result + //final Iterator mapIter = availableEnchantments.keySet().iterator(); + //while (mapIter.hasNext()) + for(AUTO_VAR(it, availableEnchantments->begin()); it != availableEnchantments->end();) + { + int nextEnchantment = it->first;//mapIter.next(); + bool valid = true; + //for (EnchantmentInstance *current : results) + for(AUTO_VAR(resIt, results->begin()); resIt != results->end(); ++resIt) + { + EnchantmentInstance *current = *resIt; + if (!current->enchantment->isCompatibleWith(Enchantment::enchantments[nextEnchantment])) + { + valid = false; + break; + } + } + if (!valid) + { + //mapIter.remove(); + delete it->second; + it = availableEnchantments->erase(it); + } + else + { + ++it; + } + } + + if (!availableEnchantments->empty()) + { + for(AUTO_VAR(it, availableEnchantments->begin()); it != availableEnchantments->end(); ++it) + { + values.push_back(it->second); + } + EnchantmentInstance *nextInstance = (EnchantmentInstance *) WeighedRandom::getRandomItem(random, &values); + values.clear(); + results->push_back( nextInstance->copy() ); // 4J Stu - Inserting a copy so we can clear memory from the availableEnchantments collection + } + + bonusChance >>= 1; + } + } + } + if(availableEnchantments != NULL) + { + for(AUTO_VAR(it, availableEnchantments->begin()); it != availableEnchantments->end(); ++it) + { + delete it->second; + } + delete availableEnchantments; + } + + return results; +} + +unordered_map *EnchantmentHelper::getAvailableEnchantmentResults(int value, shared_ptr itemInstance) +{ + Item *item = itemInstance->getItem(); + unordered_map *results = NULL; + + bool isBook = itemInstance->id == Item::book_Id; + + //for (Enchantment e : Enchantment.enchantments) + for(unsigned int i = 0; i < Enchantment::enchantments.length; ++i) + { + Enchantment *e = Enchantment::enchantments[i]; + if (e == NULL) + { + continue; + } + + // Only picks "normal" enchantments, no specialcases + if (!e->category->canEnchant(item) && !isBook) + { + continue; + } + + for (int level = e->getMinLevel(); level <= e->getMaxLevel(); level++) + { + if (value >= e->getMinCost(level) && value <= e->getMaxCost(level)) + { + if (results == NULL) + { + results = new unordered_map(); + } + AUTO_VAR(it, results->find(e->id)); + if(it != results->end()) + { + delete it->second; + } + (*results)[e->id] = new EnchantmentInstance(e, level); + } + } + } + + return results; +} \ No newline at end of file diff --git a/Minecraft.World/EnchantmentHelper.h b/Minecraft.World/EnchantmentHelper.h new file mode 100644 index 00000000..9f7de5c5 --- /dev/null +++ b/Minecraft.World/EnchantmentHelper.h @@ -0,0 +1,110 @@ +#pragma once + +class ItemInstance; +class Inventory; +class DamageSource; +class Enchantment; +class EnchantmentInstance; + +class EnchantmentHelper +{ +private: + static Random random; + +public: + static int getEnchantmentLevel(int enchantmentId, shared_ptr piece); + static unordered_map *getEnchantments(shared_ptr item); + static void setEnchantments(unordered_map *enchantments, shared_ptr item); + + static int getEnchantmentLevel(int enchantmentId, ItemInstanceArray inventory); + +private: + + + class EnchantmentIterationMethod + { + public: + virtual void doEnchantment(Enchantment *enchantment, int level) = 0; + }; + + static void runIterationOnItem(EnchantmentIterationMethod &method, shared_ptr piece); + static void runIterationOnInventory(EnchantmentIterationMethod &method, ItemInstanceArray inventory); + + class GetDamageProtectionIteration : public EnchantmentIterationMethod + { + public: + int sum; + DamageSource *source; + + virtual void doEnchantment(Enchantment *enchantment, int level); + }; + + static GetDamageProtectionIteration getDamageProtectionIteration; + + /** + * Fetches the protection value for enchanted items. + * + * @param inventory + * @param source + * @return + */ +public: + static int getDamageProtection(shared_ptr inventory, DamageSource *source); + +private: + class GetDamageBonusIteration : public EnchantmentIterationMethod + { + public: + int sum; + shared_ptr target; + + virtual void doEnchantment(Enchantment *enchantment, int level); + }; + + static GetDamageBonusIteration getDamageBonusIteration; + + /** + * + * @param inventory + * @param target + * @return + */ +public: + static int getDamageBonus(shared_ptr inventory, shared_ptr target); + static int getKnockbackBonus(shared_ptr inventory, shared_ptr target); + static int getFireAspect(shared_ptr source); + static int getOxygenBonus(shared_ptr inventory); + static int getDiggingBonus(shared_ptr inventory); + static int getDigDurability(shared_ptr inventory); + static bool hasSilkTouch(shared_ptr inventory); + static int getDiggingLootBonus(shared_ptr inventory); + static int getKillingLootBonus(shared_ptr inventory); + static bool hasWaterWorkerBonus(shared_ptr inventory); + static int getArmorThorns(shared_ptr source); + static shared_ptr getRandomItemWith(Enchantment *enchantment, shared_ptr source); + + /** + * + * @param random + * @param slot + * The table slot, 0-2 + * @param bookcases + * How many book cases that are found around the table. + * @param itemInstance + * Which item that is being enchanted. + * @return The enchantment cost, 0 means unchantable, 50 is max. + */ + static int getEnchantmentCost(Random *random, int slot, int bookcases, shared_ptr itemInstance); + + static shared_ptr enchantItem(Random *random, shared_ptr itemInstance, int enchantmentCost); + + /** + * + * @param random + * @param itemInstance + * @param enchantmentCost + * @return + */ + static vector *selectEnchantment(Random *random, shared_ptr itemInstance, int enchantmentCost); + static unordered_map *getAvailableEnchantmentResults(int value, shared_ptr itemInstance); +}; \ No newline at end of file diff --git a/Minecraft.World/EnchantmentInstance.cpp b/Minecraft.World/EnchantmentInstance.cpp new file mode 100644 index 00000000..973cd220 --- /dev/null +++ b/Minecraft.World/EnchantmentInstance.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.enchantment.h" +#include "EnchantmentInstance.h" + +EnchantmentInstance::EnchantmentInstance(Enchantment *enchantment, int level) : WeighedRandomItem(enchantment->getFrequency()), enchantment(enchantment), level(level) +{ +} + +EnchantmentInstance::EnchantmentInstance(int id, int level) : WeighedRandomItem(Enchantment::enchantments[id]->getFrequency()), enchantment(Enchantment::enchantments[id]), level(level) +{ +} + +// 4J Added +EnchantmentInstance *EnchantmentInstance::copy() +{ + return new EnchantmentInstance((Enchantment *)enchantment, (int)level); +} \ No newline at end of file diff --git a/Minecraft.World/EnchantmentInstance.h b/Minecraft.World/EnchantmentInstance.h new file mode 100644 index 00000000..eb55751b --- /dev/null +++ b/Minecraft.World/EnchantmentInstance.h @@ -0,0 +1,16 @@ +#pragma once + +#include "WeighedRandom.h" + +class EnchantmentInstance : public WeighedRandomItem +{ +public: + const Enchantment *enchantment; + const int level; + + EnchantmentInstance(Enchantment *enchantment, int level); + EnchantmentInstance(int id, int level); + + // 4J Added + EnchantmentInstance *copy(); +}; \ No newline at end of file diff --git a/Minecraft.World/EnchantmentMenu.cpp b/Minecraft.World/EnchantmentMenu.cpp new file mode 100644 index 00000000..23b366ce --- /dev/null +++ b/Minecraft.World/EnchantmentMenu.cpp @@ -0,0 +1,299 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.inventory.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "EnchantmentMenu.h" + +EnchantmentMenu::EnchantmentMenu(shared_ptr inventory, Level *level, int xt, int yt, int zt) +{ + enchantSlots = shared_ptr( new EnchantmentContainer(this) ); + + for(int i = 0; i < 3; ++i) + { + costs[i] = 0; + } + + this->level = level; + this->x = xt; + this->y = yt; + this->z = zt; + addSlot(new EnchantmentSlot(enchantSlots, 0, 21 + 4, 43 + 4)); + + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x + y * 9 + 9, 8 + x * 18, 84 + y * 18)); + } + } + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x, 8 + x * 18, 142)); + } + + m_costsChanged = false; +} + +void EnchantmentMenu::addSlotListener(ContainerListener *listener) +{ + AbstractContainerMenu::addSlotListener(listener); + + listener->setContainerData(this, 0, costs[0]); + listener->setContainerData(this, 1, costs[1]); + listener->setContainerData(this, 2, costs[2]); +} + +void EnchantmentMenu::broadcastChanges() +{ + AbstractContainerMenu::broadcastChanges(); + + // 4J Added m_costsChanged to stop continually sending update packets even when no changes have been made + if(m_costsChanged) + { + for (int i = 0; i < containerListeners->size(); i++) + { + ContainerListener *listener = containerListeners->at(i); + listener->setContainerData(this, 0, costs[0]); + listener->setContainerData(this, 1, costs[1]); + listener->setContainerData(this, 2, costs[2]); + } + m_costsChanged = false; + } +} + +void EnchantmentMenu::setData(int id, int value) +{ + if (id >= 0 && id <= 2) + { + costs[id] = value; + m_costsChanged = true; + } + else + { + AbstractContainerMenu::setData(id, value); + } +} + +void EnchantmentMenu::slotsChanged() // 4J used to take a shared_ptr container but wasn't using it, so removed to simplify things +{ + shared_ptr item = enchantSlots->getItem(0); + + if (item == NULL || !item->isEnchantable()) + { + for (int i = 0; i < 3; i++) + { + costs[i] = 0; + } + m_costsChanged = true; + } + else + { + nameSeed = random.nextLong(); + + if (!level->isClientSide) + { + // find book cases + int bookcases = 0; + for (int oz = -1; oz <= 1; oz++) + { + for (int ox = -1; ox <= 1; ox++) + { + if (oz == 0 && ox == 0) + { + continue; + } + + if (level->isEmptyTile(x + ox, y, z + oz) && level->isEmptyTile(x + ox, y + 1, z + oz)) + { + if (level->getTile(x + ox * 2, y, z + oz * 2) == Tile::bookshelf_Id) + { + bookcases++; + } + if (level->getTile(x + ox * 2, y + 1, z + oz * 2) == Tile::bookshelf_Id) + { + bookcases++; + } + // corners + if (ox != 0 && oz != 0) + { + if (level->getTile(x + ox * 2, y, z + oz) == Tile::bookshelf_Id) + { + bookcases++; + } + if (level->getTile(x + ox * 2, y + 1, z + oz) == Tile::bookshelf_Id) + { + bookcases++; + } + if (level->getTile(x + ox, y, z + oz * 2) == Tile::bookshelf_Id) + { + bookcases++; + } + if (level->getTile(x + ox, y + 1, z + oz * 2) == Tile::bookshelf_Id) + { + bookcases++; + } + } + } + } + } + + for (int i = 0; i < 3; i++) + { + costs[i] = EnchantmentHelper::getEnchantmentCost(&random, i, bookcases, item); + } + m_costsChanged = true; + broadcastChanges(); + } + } +} + +bool EnchantmentMenu::clickMenuButton(shared_ptr player, int i) +{ + shared_ptr item = enchantSlots->getItem(0); + if (costs[i] > 0 && item != NULL && (player->experienceLevel >= costs[i] || player->abilities.instabuild) ) + { + if (!level->isClientSide) + { + bool isBook = item->id == Item::book_Id; + + vector *newEnchantment = EnchantmentHelper::selectEnchantment(&random, item, costs[i]); + if (newEnchantment != NULL) + { + player->withdrawExperienceLevels(costs[i]); + if (isBook) item->id = Item::enchantedBook_Id; + int randomIndex = isBook ? random.nextInt(newEnchantment->size()) : -1; + //for (EnchantmentInstance e : newEnchantment) + for (int index = 0; index < newEnchantment->size(); index++) + { + EnchantmentInstance *e = newEnchantment->at(index); + if (isBook && index != randomIndex) + {} + else + { + if (isBook) + { + Item::enchantedBook->addEnchantment(item, e); + } + else + { + item->enchant(e->enchantment, e->level); + } + } + delete e; + } + delete newEnchantment; + slotsChanged();// Removed enchantSlots parameter as the function can reference it directly + } + } + return true; + } + return false; +} + + +void EnchantmentMenu::removed(shared_ptr player) +{ + AbstractContainerMenu::removed(player); + if (level->isClientSide) return; + + shared_ptr item = enchantSlots->removeItemNoUpdate(0); + if (item != NULL) + { + player->drop(item); + } +} + +bool EnchantmentMenu::stillValid(shared_ptr player) +{ + if (level->getTile(x, y, z) != Tile::enchantTable_Id) return false; + if (player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) > 8 * 8) return false; + return true; +} + +shared_ptr EnchantmentMenu::quickMoveStack(shared_ptr player, int slotIndex) +{ + shared_ptr clicked = nullptr; + Slot *slot = slots->at(slotIndex); + Slot *IngredientSlot = slots->at(INGREDIENT_SLOT); + + if (slot != NULL && slot->hasItem()) + { + shared_ptr stack = slot->getItem(); + clicked = stack->copy(); + + if (slotIndex == INGREDIENT_SLOT) + { + if (!moveItemStackTo(stack, INV_SLOT_START, INV_SLOT_END, true)) + { + if (!moveItemStackTo(stack, USE_ROW_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + + } + } + else if (slotIndex >= INV_SLOT_START && slotIndex < INV_SLOT_END) + { + // if the item is an enchantable tool + + if(stack->isEnchantable() && (!IngredientSlot->hasItem() ) ) + { + if(!moveItemStackTo(stack, INGREDIENT_SLOT, INGREDIENT_SLOT+1, false)) + { + return nullptr; + } + } + else + { + if(!moveItemStackTo(stack, USE_ROW_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + } + } + else if (slotIndex >= USE_ROW_SLOT_START && slotIndex < USE_ROW_SLOT_END) + { + // if the item is an enchantable tool + + if(stack->isEnchantable() && (!IngredientSlot->hasItem() ) ) + { + if(!moveItemStackTo(stack, INGREDIENT_SLOT, INGREDIENT_SLOT+1, false)) + { + return nullptr; + } + } + else + { + if(!moveItemStackTo(stack, INV_SLOT_START, INV_SLOT_END, false)) + { + return nullptr; + } + } + } + else + { + return nullptr; + } + + if (stack->count == 0) + { + slot->set(nullptr); + } + else + { + slot->setChanged(); + } + if (stack->count == clicked->count) + { + return nullptr; + } + else + { + slot->onTake(player, stack); + } + } + return clicked; +} \ No newline at end of file diff --git a/Minecraft.World/EnchantmentMenu.h b/Minecraft.World/EnchantmentMenu.h new file mode 100644 index 00000000..e09b94c6 --- /dev/null +++ b/Minecraft.World/EnchantmentMenu.h @@ -0,0 +1,41 @@ +#pragma once +#include "AbstractContainerMenu.h" +#include "Random.h" + +class EnchantmentMenu : public AbstractContainerMenu +{ + // 4J Stu Made these public for UI menus, perhaps should make friend class? +public: + static const int INGREDIENT_SLOT = 0; + static const int INV_SLOT_START = EnchantmentMenu::INGREDIENT_SLOT + 1; + static const int INV_SLOT_END = EnchantmentMenu::INV_SLOT_START + 9 * 3; + static const int USE_ROW_SLOT_START = EnchantmentMenu::INV_SLOT_END; + static const int USE_ROW_SLOT_END = EnchantmentMenu::USE_ROW_SLOT_START + 9; + +public: + shared_ptr enchantSlots; + +private: + Level *level; + int x, y, z; + Random random; + + bool m_costsChanged; // 4J Added + +public: + __int64 nameSeed; + +public: + int costs[3]; + + EnchantmentMenu(shared_ptr inventory, Level *level, int xt, int yt, int zt); + + virtual void addSlotListener(ContainerListener *listener); + virtual void broadcastChanges(); + virtual void setData(int id, int value); + virtual void slotsChanged();// 4J used to take a shared_ptr container but wasn't using it, so removed to simplify things + virtual bool clickMenuButton(shared_ptr player, int i); + void removed(shared_ptr player); + virtual bool stillValid(shared_ptr player); + virtual shared_ptr quickMoveStack(shared_ptr player, int slotIndex); +}; \ No newline at end of file diff --git a/Minecraft.World/EnchantmentSlot.h b/Minecraft.World/EnchantmentSlot.h new file mode 100644 index 00000000..590d9e31 --- /dev/null +++ b/Minecraft.World/EnchantmentSlot.h @@ -0,0 +1,16 @@ +#pragma once +// 4J Stu Added +// In EnchantmentMenu.java they create an anoymous class while creating some slot. I have moved the content +// of that anonymous class to here + +#include "Slot.h" + +class Container; + +class EnchantmentSlot : public Slot +{ +public: + EnchantmentSlot(shared_ptr container, int id, int x, int y) : Slot(container,id, x, y) {} + virtual bool mayPlace(shared_ptr item) {return true;} + virtual bool mayCombine(shared_ptr item) {return false;} // 4J Added +}; \ No newline at end of file diff --git a/Minecraft.World/EnchantmentTableEntity.cpp b/Minecraft.World/EnchantmentTableEntity.cpp new file mode 100644 index 00000000..31da3c49 --- /dev/null +++ b/Minecraft.World/EnchantmentTableEntity.cpp @@ -0,0 +1,109 @@ +#include "stdafx.h" +#include "EnchantmentTableEntity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" + + + +EnchantmentTableEntity::EnchantmentTableEntity() +{ + random = new Random(); + + time = 0; + flip = 0.0f; + oFlip = 0.0f; + flipT = 0.0f; + flipA = 0.0f; + open = 0.0f; + oOpen = 0.0f; + rot = 0.0f; + oRot = 0.0f; + tRot = 0.0f; +} + +EnchantmentTableEntity::~EnchantmentTableEntity() +{ + delete random; +} + +void EnchantmentTableEntity::tick() +{ + TileEntity::tick(); + oOpen = open; + oRot = rot; + + shared_ptr player = level->getNearestPlayer(x + 0.5f, y + 0.5f, z + 0.5f, 3); + if (player != NULL) + { + double xd = player->x - (x + 0.5f); + double zd = player->z - (z + 0.5f); + + tRot = (float) atan2(zd, xd); + + open += 0.1f; + + if (open < 0.5f || random->nextInt(40) == 0) + { + float old = flipT; + do + { + flipT += random->nextInt(4) - random->nextInt(4); + } while (old == flipT); + } + + } + else + { + tRot += 0.02f; + open -= 0.1f; + } + + while (rot >= PI) + rot -= PI * 2; + while (rot < -PI) + rot += PI * 2; + while (tRot >= PI) + tRot -= PI * 2; + while (tRot < -PI) + tRot += PI * 2; + float rotDir = tRot - rot; + while (rotDir >= PI) + rotDir -= PI * 2; + while (rotDir < -PI) + rotDir += PI * 2; + + rot += rotDir * 0.4f; + + if (open < 0) open = 0; + if (open > 1) open = 1; + + time++; + oFlip = flip; + + float diff = (flipT - flip) * 0.4f; + float max = 0.2f; + if (diff < -max) diff = -max; + if (diff > +max) diff = +max; + flipA += (diff - flipA) * 0.9f; + + flip = flip + flipA; +} + +shared_ptr EnchantmentTableEntity::clone() +{ + shared_ptr result = shared_ptr( new EnchantmentTableEntity() ); + TileEntity::clone(result); + + result->time = time; + result->flip = flip; + result->oFlip = oFlip; + result->flipT = flipT; + result->flipA = flipA; + result->open = open; + result->oOpen = oOpen; + result->rot = rot; + result->oRot = oRot; + result->tRot = tRot; + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/EnchantmentTableEntity.h b/Minecraft.World/EnchantmentTableEntity.h new file mode 100644 index 00000000..aa54c812 --- /dev/null +++ b/Minecraft.World/EnchantmentTableEntity.h @@ -0,0 +1,25 @@ +#pragma once +#include "TileEntity.h" +class Random; + +class EnchantmentTableEntity : public TileEntity +{ +public: + eINSTANCEOF GetType() { return eTYPE_ENCHANTMENTTABLEENTITY; } + static TileEntity *create() { return new EnchantmentTableEntity(); } + +public: + int time; + float flip, oFlip, flipT, flipA; + float open, oOpen; + float rot, oRot, tRot; +private: + Random *random; +public: + EnchantmentTableEntity(); + ~EnchantmentTableEntity(); + virtual void tick(); + + // 4J Added + virtual shared_ptr clone(); +}; \ No newline at end of file diff --git a/Minecraft.World/EnchantmentTableTile.cpp b/Minecraft.World/EnchantmentTableTile.cpp new file mode 100644 index 00000000..70c127ba --- /dev/null +++ b/Minecraft.World/EnchantmentTableTile.cpp @@ -0,0 +1,92 @@ +#include "stdafx.h" +#include "EnchantmentTableTile.h" +#include "EnchantmentTableEntity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.h" +#include "net.minecraft.world.h" + +const wstring EnchantmentTableTile::TEXTURE_SIDE = L"enchantment_side"; +const wstring EnchantmentTableTile::TEXTURE_TOP = L"enchantment_top"; +const wstring EnchantmentTableTile::TEXTURE_BOTTOM = L"enchantment_bottom"; + +EnchantmentTableTile::EnchantmentTableTile(int id) : EntityTile(id, Material::stone, isSolidRender()) +{ + updateDefaultShape(); + setLightBlock(0); + + iconTop = NULL; + iconBottom = NULL; +} + +// 4J Added override +void EnchantmentTableTile::updateDefaultShape() +{ + setShape(0, 0, 0, 1, 12 / 16.0f, 1); +} + +bool EnchantmentTableTile::isCubeShaped() +{ + return false; +} + +void EnchantmentTableTile::animateTick(Level *level, int x, int y, int z, Random *random) +{ + EntityTile::animateTick(level, x, y, z, random); + + for (int xx = x - 2; xx <= x + 2; xx++) + { + for (int zz = z - 2; zz <= z + 2; zz++) + { + if (xx > x - 2 && xx < x + 2 && zz == z - 1) + { + zz = z + 2; + } + if (random->nextInt(16) != 0) continue; + for (int yy = y; yy <= y + 1; yy++) + { + if (level->getTile(xx, yy, zz) == Tile::bookshelf_Id) + { + if (!level->isEmptyTile((xx - x) / 2 + x, yy, (zz - z) / 2 + z)) break; + + level->addParticle(eParticleType_enchantmenttable, x + 0.5, y + 2.0, z + 0.5, xx - x + random->nextFloat() - 0.5, yy - y - random->nextFloat() - 1, zz - z + random->nextFloat() - 0.5); + } + } + } + } +} + +bool EnchantmentTableTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +Icon *EnchantmentTableTile::getTexture(int face, int data) +{ + if (face == Facing::DOWN) return iconBottom; + if (face == Facing::UP) return iconTop; + return icon; +} + +shared_ptr EnchantmentTableTile::newTileEntity(Level *level) +{ + return shared_ptr(new EnchantmentTableEntity()); +} + +bool EnchantmentTableTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if(soundOnly) return false; + + if (level->isClientSide) + { + return true; + } + player->startEnchanting(x, y, z); + return true; +} + +void EnchantmentTableTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(TEXTURE_SIDE); + iconTop = iconRegister->registerIcon(TEXTURE_TOP); + iconBottom = iconRegister->registerIcon(TEXTURE_BOTTOM); +} diff --git a/Minecraft.World/EnchantmentTableTile.h b/Minecraft.World/EnchantmentTableTile.h new file mode 100644 index 00000000..43816cec --- /dev/null +++ b/Minecraft.World/EnchantmentTableTile.h @@ -0,0 +1,29 @@ +#pragma once +#include "EntityTile.h" + +class ChunkRebuildData; +class EnchantmentTableTile : public EntityTile +{ + friend class ChunkRebuildData; +public: + static const wstring TEXTURE_SIDE; + static const wstring TEXTURE_TOP; + static const wstring TEXTURE_BOTTOM; + +private: + Icon *iconTop; + Icon *iconBottom; + +public: + EnchantmentTableTile(int id); + + virtual void updateDefaultShape(); // 4J Added override + bool isCubeShaped(); + void animateTick(Level *level, int x, int y, int z, Random *random); + bool isSolidRender(bool isServerLevel = false); + Icon *getTexture(int face, int data); + shared_ptr newTileEntity(Level *level); + bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + //@Override + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/EndPodiumFeature.cpp b/Minecraft.World/EndPodiumFeature.cpp new file mode 100644 index 00000000..261b6446 --- /dev/null +++ b/Minecraft.World/EndPodiumFeature.cpp @@ -0,0 +1,86 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.boss.enderdragon.h" +#include "net.minecraft.world.level.tile.h" +#include "EndPodiumFeature.h" + +EndPodiumFeature::EndPodiumFeature(int tile) +{ + this->tile = tile; + //m_iIndex=0; +} + +bool EndPodiumFeature::place(Level *level, Random *random, int x, int y, int z) +{ + // spawn Exit portal + int r = 4; + + for (int yy = y - 1; yy <= y + 32; yy++) + { + for (int xx = x - r; xx <= x + r; xx++) + { + for (int zz = z - r; zz <= z + r; zz++) + { + double xd = xx - x; + double zd = zz - z; + double d = sqrt(xd * xd + zd * zd); + if (d <= r - 0.5) + { + if (yy < y) + { + if (d > r - 1 - 0.5) + { + } + else + { + //level->setTile(xx, yy, zz, Tile::unbreakable_Id); + placeBlock(level, xx, yy, zz, Tile::unbreakable_Id, 0); + } + } + else if (yy > y) + { + //level->setTile(xx, yy, zz, 0); + placeBlock(level, xx, yy, zz, 0, 0); + } + else + { + if (d > r - 1 - 0.5) + { + //level->setTile(xx, yy, zz, Tile::unbreakable_Id); + placeBlock(level, xx, yy, zz, Tile::unbreakable_Id, 0); + } + } + } + } + } + } + + placeBlock(level,x, y + 0, z, Tile::unbreakable_Id, 0); + placeBlock(level,x, y + 1, z, Tile::unbreakable_Id, 0); + placeBlock(level,x, y + 2, z, Tile::unbreakable_Id, 0); + placeBlock(level,x - 1, y + 2, z, Tile::torch_Id, 0); + placeBlock(level,x + 1, y + 2, z, Tile::torch_Id, 0); + placeBlock(level,x, y + 2, z - 1, Tile::torch_Id, 0); + placeBlock(level,x, y + 2, z + 1, Tile::torch_Id, 0); + placeBlock(level,x, y + 3, z, Tile::unbreakable_Id, 0); + //placeBlock(level,x, y + 4, z, Tile::dragonEgg_Id, 0); + + // 4J-PB - The podium can be floating with nothing under it, so put some whiteStone under it if this is the case + for (int yy = y - 5; yy < y - 1; yy++) + { + for (int xx = x - (r - 1); xx <= x + (r - 1); xx++) + { + for (int zz = z - (r - 1); zz <= z + (r - 1); zz++) + { + if(level->isEmptyTile(xx,yy,zz)) + { + placeBlock(level, xx, yy, zz, Tile::whiteStone_Id, 0); + } + } + } + } + + + return true; +} + diff --git a/Minecraft.World/EndPodiumFeature.h b/Minecraft.World/EndPodiumFeature.h new file mode 100644 index 00000000..fd231d32 --- /dev/null +++ b/Minecraft.World/EndPodiumFeature.h @@ -0,0 +1,14 @@ +#pragma once +#include "Feature.h" + +class EndPodiumFeature : public Feature +{ +private: + int tile; + //int m_iIndex; + +public: + EndPodiumFeature(int tile); + virtual bool place(Level *level, Random *random, int x, int y, int z); + +}; diff --git a/Minecraft.World/EndTag.h b/Minecraft.World/EndTag.h new file mode 100644 index 00000000..684ee6ce --- /dev/null +++ b/Minecraft.World/EndTag.h @@ -0,0 +1,25 @@ +#pragma once +#include "Tag.h" + +class EndTag : public Tag +{ +public: + EndTag() : Tag(L"") {} + EndTag(const wstring &name) : Tag(name) {} + + void load(DataInput *dis) {}; + void write(DataOutput *dos) {}; + + byte getId() { return TAG_End; } + wstring toString() { return wstring( L"END" ); } + + Tag *copy() + { + return new EndTag(); + } + + bool equals(Tag *obj) + { + return Tag::equals(obj); + } +}; \ No newline at end of file diff --git a/Minecraft.World/EnderChestTile.cpp b/Minecraft.World/EnderChestTile.cpp new file mode 100644 index 00000000..7e50a933 --- /dev/null +++ b/Minecraft.World/EnderChestTile.cpp @@ -0,0 +1,117 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.h" +#include "net.minecraft.h" +#include "EnderChestTile.h" + +EnderChestTile::EnderChestTile(int id) : EntityTile(id, Material::stone, isSolidRender()) +{ + updateDefaultShape(); +} + +// 4J Added override +void EnderChestTile::updateDefaultShape() +{ + setShape(1 / 16.0f, 0, 1 / 16.0f, 15 / 16.0f, 14 / 16.0f, 15 / 16.0f); +} + +bool EnderChestTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool EnderChestTile::isCubeShaped() +{ + return false; +} + +int EnderChestTile::getRenderShape() +{ + return Tile::SHAPE_ENTITYTILE_ANIMATED; +} + +int EnderChestTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::obsidian_Id; +} + +int EnderChestTile::getResourceCount(Random *random) +{ + return 8; +} + +bool EnderChestTile::isSilkTouchable() +{ + return true; +} + +void EnderChestTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int facing = 0; + int dir = (Mth::floor(by->yRot * 4 / (360) + 0.5f)) & 3; + + if (dir == 0) facing = Facing::NORTH; + if (dir == 1) facing = Facing::EAST; + if (dir == 2) facing = Facing::SOUTH; + if (dir == 3) facing = Facing::WEST; + + level->setData(x, y, z, facing); +} + +bool EnderChestTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly) +{ + shared_ptr container = player->getEnderChestInventory(); + shared_ptr enderChest = dynamic_pointer_cast(level->getTileEntity(x, y, z)); + if (container == NULL || enderChest == NULL) return true; + + if (level->isSolidBlockingTile(x, y + 1, z)) return true; + + if (level->isClientSide) + { + return true; + } + + container->setActiveChest(enderChest); + player->openContainer(container); + + return true; +} + +shared_ptr EnderChestTile::newTileEntity(Level *level) +{ + return shared_ptr(new EnderChestTileEntity()); +} + +void EnderChestTile::animateTick(Level *level, int xt, int yt, int zt, Random *random) +{ + for (int i = 0; i < 3; i++) + { + double x = xt + random->nextFloat(); + double y = yt + random->nextFloat(); + double z = zt + random->nextFloat(); + double xa = 0; + double ya = 0; + double za = 0; + int flipX = random->nextInt(2) * 2 - 1; + int flipZ = random->nextInt(2) * 2 - 1; + xa = (random->nextFloat() - 0.5) * 0.125; + ya = (random->nextFloat() - 0.5) * 0.125; + za = (random->nextFloat() - 0.5) * 0.125; + z = zt + 0.5 + (0.25) * flipZ; + za = (random->nextFloat() * 1) * flipZ; + x = xt + 0.5 + (0.25) * flipX; + xa = (random->nextFloat() * 1) * flipX; + + level->addParticle(eParticleType_ender, x, y, z, xa, ya, za); + } +} + +void EnderChestTile::registerIcons(IconRegister *iconRegister) +{ + // Register obsidian as the chest's icon, because it's used by the + // particles when destroying the chest + icon = iconRegister->registerIcon(L"obsidian"); +} diff --git a/Minecraft.World/EnderChestTile.h b/Minecraft.World/EnderChestTile.h new file mode 100644 index 00000000..96173f63 --- /dev/null +++ b/Minecraft.World/EnderChestTile.h @@ -0,0 +1,29 @@ +#pragma once + +#include "EntityTile.h" +#include "ChestTile.h" + +class EnderChestTile : public EntityTile +{ +public: + static const int EVENT_SET_OPEN_COUNT = ChestTile::EVENT_SET_OPEN_COUNT; + + EnderChestTile(int id); + virtual void updateDefaultShape(); // 4J Added override + + bool isSolidRender(bool isServerLevel = false); + bool isCubeShaped(); + int getRenderShape(); + int getResource(int data, Random *random, int playerBonusLevel); + int getResourceCount(Random *random); + +protected: + bool isSilkTouchable(); + +public: + void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); + shared_ptr newTileEntity(Level *level); + void animateTick(Level *level, int xt, int yt, int zt, Random *random); + virtual void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/EnderChestTileEntity.cpp b/Minecraft.World/EnderChestTileEntity.cpp new file mode 100644 index 00000000..dffa2bd5 --- /dev/null +++ b/Minecraft.World/EnderChestTileEntity.cpp @@ -0,0 +1,99 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "EnderChestTileEntity.h" + +EnderChestTileEntity::EnderChestTileEntity() +{ + openness = oOpenness = 0.0f; + openCount = 0; + tickInterval = 0; +} + +void EnderChestTileEntity::tick() +{ + TileEntity::tick(); + + if (++tickInterval % 20 * 4 == 0) + { + level->tileEvent(x, y, z, Tile::enderChest_Id, ChestTile::EVENT_SET_OPEN_COUNT, openCount); + } + + oOpenness = openness; + + float speed = 0.10f; + if (openCount > 0 && openness == 0) + { + double xc = x + 0.5; + double zc = z + 0.5; + + // 4J-PB - Seems the chest open volume is much louder than other sounds from user reports. We'll tone it down a bit + level->playSound(xc, y + 0.5, zc, eSoundType_RANDOM_CHEST_OPEN, 0.2f, level->random->nextFloat() * 0.1f + 0.9f); + } + if ((openCount == 0 && openness > 0) || (openCount > 0 && openness < 1)) + { + float oldOpen = openness; + if (openCount > 0) openness += speed; + else openness -= speed; + if (openness > 1) + { + openness = 1; + } + float lim = 0.5f; + if (openness < lim && oldOpen >= lim) + { + double xc = x + 0.5; + double zc = z + 0.5; + + // 4J-PB - Seems the chest open volume is much louder than other sounds from user reports. We'll tone it down a bit + level->playSound(xc, y + 0.5, zc, eSoundType_RANDOM_CHEST_CLOSE, 0.2f, level->random->nextFloat() * 0.1f + 0.9f); + } + if (openness < 0) + { + openness = 0; + } + } +} + +void EnderChestTileEntity::triggerEvent(int b0, int b1) +{ + if (b0 == ChestTile::EVENT_SET_OPEN_COUNT) + { + openCount = b1; + } +} + +void EnderChestTileEntity::setRemoved() +{ + clearCache(); + TileEntity::setRemoved(); +} + +void EnderChestTileEntity::startOpen() +{ + openCount++; + level->tileEvent(x, y, z, Tile::enderChest_Id, ChestTile::EVENT_SET_OPEN_COUNT, openCount); +} + +void EnderChestTileEntity::stopOpen() +{ + openCount--; + level->tileEvent(x, y, z, Tile::enderChest_Id, ChestTile::EVENT_SET_OPEN_COUNT, openCount); +} + +bool EnderChestTileEntity::stillValid(shared_ptr player) +{ + if (level->getTileEntity(x, y, z) != shared_from_this()) return false; + if (player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) > 8 * 8) return false; + + return true; +} + +// 4J Added +shared_ptr EnderChestTileEntity::clone() +{ + shared_ptr result = shared_ptr( new EnderChestTileEntity() ); + TileEntity::clone(result); + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/EnderChestTileEntity.h b/Minecraft.World/EnderChestTileEntity.h new file mode 100644 index 00000000..f6882980 --- /dev/null +++ b/Minecraft.World/EnderChestTileEntity.h @@ -0,0 +1,30 @@ +#pragma once + +#include "TileEntity.h" + +class EnderChestTileEntity : public TileEntity +{ +public: + eINSTANCEOF GetType() { return eTYPE_ENDERCHESTTILEENTITY; } + static TileEntity *create() { return new EnderChestTileEntity(); } + +public: + float openness, oOpenness; + int openCount; + +private: + int tickInterval; + +public: + EnderChestTileEntity(); + + void tick(); + void triggerEvent(int b0, int b1); + void setRemoved(); + void startOpen(); + void stopOpen(); + bool stillValid(shared_ptr player); + + // 4J Added + virtual shared_ptr clone(); +}; \ No newline at end of file diff --git a/Minecraft.World/EnderCrystal.cpp b/Minecraft.World/EnderCrystal.cpp new file mode 100644 index 00000000..719e9d96 --- /dev/null +++ b/Minecraft.World/EnderCrystal.cpp @@ -0,0 +1,128 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.boss.enderdragon.h" +#include "EnderCrystal.h" +#include "DamageSource.h" + + + +void EnderCrystal::_init(Level *level) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + blocksBuilding = true; + setSize(2.0f, 2.0f); + heightOffset = bbHeight / 2.0f; + life = MAX_LIFE; + + time = random->nextInt(100000); +} + +EnderCrystal::EnderCrystal(Level *level) : Entity( level ) +{ + _init(level); + +} + +EnderCrystal::EnderCrystal(Level *level, double x, double y, double z) : Entity( level ) +{ + _init(level); + setPos(x, y, z); + +} + +bool EnderCrystal::makeStepSound() +{ + return false; +} + +void EnderCrystal::defineSynchedData() +{ + entityData->define(DATA_REMAINING_LIFE, life); +} + +void EnderCrystal::tick() +{ + xo = x; + yo = y; + zo = z; + time++; + + entityData->set(DATA_REMAINING_LIFE, life); + + // Don't set the tile directly on the client, as this can end up in the updatesToReset queue in the MultiPlayerLevel, and the perpetually end up removing/adding these fire tiles causing timing + // glitches from the lighting changes requried. + if (!level->isClientSide) + { + int xt = Mth::floor(x); + int yt = Mth::floor(y); + int zt = Mth::floor(z); + if (level->getTile(xt, yt, zt) != Tile::fire_Id) + { + level->setTile(xt, yt, zt, Tile::fire_Id); + } + } +} + + +void EnderCrystal::addAdditonalSaveData(CompoundTag *tag) +{ +} + +void EnderCrystal::readAdditionalSaveData(CompoundTag *tag) +{ +} + + +float EnderCrystal::getShadowHeightOffs() +{ + return 0; +} + +bool EnderCrystal::isPickable() +{ + return true; +} + +bool EnderCrystal::hurt(DamageSource *source, int damage) +{ + // 4J-PB - if the owner of the source is the enderdragon, then ignore it (where the dragon's fireball hits an endercrystal) + shared_ptr sourceIsDragon = dynamic_pointer_cast(source->getEntity()); + + if(sourceIsDragon!=NULL) + { + return false; + } + + if (!removed && !level->isClientSide) + { + life = 0; + if (life <= 0) + { + remove(); + if (!level->isClientSide) + { + level->explode(nullptr, x, y, z, 6, true); + + vector > entities = level->getAllEntities(); + shared_ptr dragon = nullptr; + AUTO_VAR(itEnd, entities.end()); + for (AUTO_VAR(it, entities.begin()); it != itEnd; it++) + { + shared_ptr e = *it; //entities->at(i); + dragon = dynamic_pointer_cast(e); + if(dragon != NULL) + { + dragon->handleCrystalDestroyed(source); + break; + } + } + } + } + } + return true; +} \ No newline at end of file diff --git a/Minecraft.World/EnderCrystal.h b/Minecraft.World/EnderCrystal.h new file mode 100644 index 00000000..26606e26 --- /dev/null +++ b/Minecraft.World/EnderCrystal.h @@ -0,0 +1,42 @@ +#pragma once + +#include "Entity.h" + +class EnderCrystal : public Entity +{ +public: + eINSTANCEOF GetType() { return eTYPE_ENDER_CRYSTAL; }; + static Entity *create(Level *level) { return new EnderCrystal(level); } +private: + static const int MAX_LIFE = 5; + +public: + static const int serialVersionUID = 0; + int time; + int life; + +private: + static const int DATA_REMAINING_LIFE = 8; + + void _init(Level *level); + +public: + EnderCrystal(Level *level); + EnderCrystal(Level *level, double x, double y, double z); + +protected: + virtual bool makeStepSound(); + virtual void defineSynchedData(); + +public: + void tick(); + +protected: + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +public: + virtual float getShadowHeightOffs(); + virtual bool isPickable(); + virtual bool hurt(DamageSource *source, int damage); +}; \ No newline at end of file diff --git a/Minecraft.World/EnderDragon.cpp b/Minecraft.World/EnderDragon.cpp new file mode 100644 index 00000000..2ccd453c --- /dev/null +++ b/Minecraft.World/EnderDragon.cpp @@ -0,0 +1,1941 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.boss.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.damagesource.h" +#include "BasicTypeContainers.h" +#include "..\Minecraft.Client\Textures.h" +#include "net.minecraft.world.entity.boss.enderdragon.h" +#include "net.minecraft.world.level.pathfinder.h" +#include "SharedConstants.h" +#include "EnderDragon.h" + +#define PRINT_DRAGON_STATE_CHANGE_MESSAGES 1 + + + +// 4J Added for new dragon behaviour +const int EnderDragon::CRYSTAL_COUNT = 8; +const int EnderDragon::FLAME_TICKS = 60; +const float EnderDragon::FLAME_ANGLE = 22.5f; +const int EnderDragon::FLAME_PASSES = 4; // How many times it covers FLAME_ANGLE in FLAME_TICKS +const int EnderDragon::FLAME_FREQUENCY = 2; // Every FLAME_FREQUENCY ticks it sets fire to blocks while doing a flame pass +const int EnderDragon::FLAME_RANGE = 10; + +const int EnderDragon::ATTACK_TICKS = SharedConstants::TICKS_PER_SECOND * 2; // Time for the dragon roar to play + +const int EnderDragon::SITTING_ATTACK_Y_VIEW_RANGE = 10; // The player must be now lower and no higher than the dragon by this amount +const int EnderDragon::SITTING_ATTACK_VIEW_RANGE = EnderDragon::FLAME_RANGE * 2; +const int EnderDragon::SITTING_ATTACK_RANGE = EnderDragon::FLAME_RANGE * 2; +const int EnderDragon::SITTING_POST_ATTACK_IDLE_TICKS = 40; +const int EnderDragon::SITTING_SCANNING_IDLE_TICKS = 100; +const int EnderDragon::SITTING_FLAME_ATTACKS_COUNT = 4; // How many times the dragons does the scan/roar/flame cycle before flying off + +// The percentage of max health that the dragon will take while in the "Sitting" states before flying away +const float EnderDragon::SITTING_ALLOWED_DAMAGE_PERCENTAGE = 0.25f; + +void EnderDragon::_init() +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + xTarget = yTarget = zTarget = 0.0; + posPointer = -1; + oFlapTime = 0; + flapTime = 0; + newTarget = false; + inWall = false; + attackTarget = nullptr; + dragonDeathTime = 0; + nearestCrystal = nullptr; + + // 4J Stu - Added for new dragon behaviour + m_remainingCrystalsCount = CRYSTAL_COUNT; + m_fireballCharge = 0; + m_holdingPatternAngle = 0.0f; + m_holdingPatternClockwise = true; + setSynchedAction(e_EnderdragonAction_HoldingPattern); + m_actionTicks = 0; + m_sittingDamageReceived = 0; + m_headYRot = 0.0; + m_acidArea = AABB::newPermanent(-4,-10,-3,6,3,3); + m_flameAttacks = 0; + + for (int i = 0; i < positionsLength; i++) + { + positions[i][0] = 0; + positions[i][1] = 0; + positions[i][2] = 0; + } + + m_nodes = new NodeArray(24); + openSet = new BinaryHeap(); + m_currentPath = NULL; +} + +EnderDragon::EnderDragon(Level *level) : BossMob(level) +{ + _init(); + + head = shared_ptr( new BossMobPart(this, L"head", 6, 6) ); + neck = shared_ptr( new BossMobPart(this, L"neck", 6, 6) ); // 4J Added + body = shared_ptr( new BossMobPart(this, L"body", 8, 8) ); + tail1 = shared_ptr( new BossMobPart(this, L"tail", 4, 4) ); + tail2 = shared_ptr( new BossMobPart(this, L"tail", 4, 4) ); + tail3 = shared_ptr( new BossMobPart(this, L"tail", 4, 4) ); + wing1 = shared_ptr( new BossMobPart(this, L"wing", 4, 4) ); + wing2 = shared_ptr( new BossMobPart(this, L"wing", 4, 4) ); + + subEntities.push_back(head); + subEntities.push_back(neck); // 4J Added + subEntities.push_back(body); + subEntities.push_back(tail1); + subEntities.push_back(tail2); + subEntities.push_back(tail3); + subEntities.push_back(wing1); + subEntities.push_back(wing2); + + maxHealth = 200; + setHealth(maxHealth); + + this->textureIdx = TN_MOB_ENDERDRAGON; // 4J was "/mob/enderdragon/ender.png"; + setSize(16, 8); + + noPhysics = true; + fireImmune = true; + + yTarget = 100; + + m_iGrowlTimer=100; + + noCulling = true; +} + +EnderDragon::~EnderDragon() +{ + if(m_nodes->data != NULL) + { + for(unsigned int i = 0; i < m_nodes->length; ++i) + { + if(m_nodes->data[i]!=NULL) delete m_nodes->data[i]; + } + delete [] m_nodes->data; + } + delete openSet; + if( m_currentPath != NULL ) delete m_currentPath; +} + +void EnderDragon::defineSynchedData() +{ + BossMob::defineSynchedData(); + + entityData->define(DATA_ID_SYNCHED_HEALTH, maxHealth); + + // 4J Added for new dragon behaviour + entityData->define(DATA_ID_SYNCHED_ACTION, e_EnderdragonAction_HoldingPattern); +} + +void EnderDragon::getLatencyPos(doubleArray result, int step, float a) +{ + if (health <= 0) + { + a = 0; + } + + a = 1 - a; + + int p0 = (posPointer - step * 1) & 63; + int p1 = (posPointer - step * 1 - 1) & 63; + + // positions is a ring buffer of size positionsLength (64) storing positional information per tick + // positions[i][0] is y rotation + // positions[i][1] is y position + // positions[i][2] is currently always 0 + + double yr0 = positions[p0][0]; + double yrd = Mth::wrapDegrees(positions[p1][0] - yr0); + result[0] = yr0 + yrd * a; + + yr0 = positions[p0][1]; + yrd = positions[p1][1] - yr0; + + result[1] = yr0 + yrd * a; + result[2] = positions[p0][2] + (positions[p1][2] - positions[p0][2]) * a; +} + +void EnderDragon::aiStep() +{ + if (!level->isClientSide) + { + entityData->set(DATA_ID_SYNCHED_HEALTH, health); + } + else + { + // 4J Stu - If saved when dead we need to make sure that the actual health is updated correctly on the client + // Fix for TU9: Content: Gameplay: Enderdragon respawns after loading game which was previously saved at point of hes death + health = getSynchedHealth(); + + float flap = Mth::cos(flapTime * PI * 2); + float oldFlap = Mth::cos(oFlapTime * PI * 2); + + if (oldFlap <= -0.3f && flap >= -0.3f) + { + level->playLocalSound(x, y, z, eSoundType_MOB_ENDERDRAGON_MOVE, 1, 0.8f + random->nextFloat() * .3f, 100.0f); + } + // play a growl every now and then + if(! (getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || + getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || + getSynchedAction() == e_EnderdragonAction_Sitting_Attacking)) + { + m_iGrowlTimer--; + if(m_iGrowlTimer<0) + { + level->playLocalSound(x, y, z, eSoundType_MOB_ENDERDRAGON_GROWL, 0.5f, 0.8f + random->nextFloat() * .3f, 100.0f); + m_iGrowlTimer=200+(random->nextInt(200)); + } + } + } + + oFlapTime = flapTime; + + if (health <= 0) + { + // level.addParticle("explode", x + random.nextFloat() * bbWidth * 2 - bbWidth, y + random.nextFloat() * bbHeight, z + random.nextFloat() * bbWidth * 2 - bbWidth, 0, 0, 0); + float xo = (random->nextFloat() - 0.5f) * 8; + float yo = (random->nextFloat() - 0.5f) * 4; + float zo = (random->nextFloat() - 0.5f) * 8; + level->addParticle(eParticleType_largeexplode, x + xo, y + 2 + yo, z + zo, 0, 0, 0); + return; + } + + checkCrystals(); + + float flapSpeed = 0.2f / (sqrt(xd * xd + zd * zd) * 10.0f + 1); + flapSpeed *= (float) pow(2.0, yd); + if ( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || + getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || + getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) + { + //app.DebugPrintf("flapSpeed is %f\n", flapSpeed); + //flapTime += flapSpeed * 2; + flapTime += 0.1f; + } + else if (inWall) + { + flapTime += flapSpeed * 0.5f; + } + else + { + flapTime += flapSpeed; + } + + yRot = Mth::wrapDegrees(yRot); + + if (posPointer < 0) + { + for (int i = 0; i < positionsLength; i++) + { + positions[i][0] = yRot; + positions[i][1] = y; + } + } + + if (++posPointer == positionsLength) posPointer = 0; + positions[posPointer][0] = yRot; + positions[posPointer][1] = y; + + + if (level->isClientSide) + { + if (lSteps > 0) + { + double xt = x + (lx - x) / lSteps; + double yt = y + (ly - y) / lSteps; + double zt = z + (lz - z) / lSteps; + + // 4J Stu - The movement is so small that this head animation doesn't look good + //if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ) + //{ + // double yrd = lyr - (yRot + m_headYRot); + // while (yrd < -180) + // yrd += 360; + // while (yrd >= 180) + // yrd -= 360; + + // m_headYRot += (yrd) / lSteps; + //} + //else + { + double yrd = Mth::wrapDegrees(lyr - yRot); + + m_headYRot = 0.0; + yRot += (yrd) / lSteps; + } + xRot += (lxr - xRot) / lSteps; + + lSteps--; + this->setPos(xt, yt, zt); + this->setRot(yRot, xRot); + + /* + * List collisions = level.getCubes(this, bb.shrink(1 / 32.0, 0, 1 / + * 32.0)); if (collisions.size() > 0) { double yTop = 0; for (int i = 0; i < + * collisions.size(); i++) { AABB ab = collisions.get(i); if (ab.y1 > yTop) yTop + * = ab.y1; } yt += yTop - bb.y0; setPos(xt, yt, zt); } + */ + + } + + if( getSynchedAction() == e_EnderdragonAction_Landing || (getSynchedAction() == e_EnderdragonAction_Sitting_Flaming && tickCount%2==0) ) + { + double xP = 0.0; + double yP = 0.0; + double zP = 0.0; + Vec3 *v = getHeadLookVector(1); //getViewVector(1); + //app.DebugPrintf("View vector is (%f,%f,%f) - lsteps %d\n", v->x, v->y, v->z, lSteps); + //unsigned int d = 0; + //for(unsigned int d = 1; d < 3; ++d) + { + Vec3 *vN = v->normalize(); + vN->yRot(-PI/4); + for(unsigned int i = 0; i < 8; ++i) + { + if(getSynchedAction() == e_EnderdragonAction_Landing) + { + //for(unsigned int j = 0; j < 6; ++j) + { + xP = head->x;// - vN->x * d; + yP = head->bb->y0 + head->bbHeight / 2;// - vN->y * d; //head->y + head->bbHeight / 2 + 0.5f - v->y * d; + zP = head->z;// - vN->z * d; + xP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2; + yP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2; + zP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2; + level->addParticle(eParticleType_dragonbreath, xP, yP, zP, (-vN->x * 0.08) + xd, (-vN->y * 0.3) + yd, (-vN->z * 0.08) + zd); + } + } + else + { + double yVelocity = 0.6; + double xzVelocity = 0.08; + for(unsigned int j = 0; j < 6; ++j) + { + xP = head->x;// - vN->x * d; + yP = head->bb->y0 + head->bbHeight / 2;// - vN->y * d; //head->y + head->bbHeight / 2 + 0.5f - v->y * d; + zP = head->z;// - vN->z * d; + xP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2; + yP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2; + zP += (level->random->nextBoolean()?1:-1) * level->random->nextFloat()/2; + level->addParticle(eParticleType_dragonbreath, xP, yP, zP, -vN->x * xzVelocity*j, -vN->y * yVelocity, -vN->z * xzVelocity*j); + } + } + vN->yRot(PI/(2*8) ); + } + } + } + else if( getSynchedAction() == e_EnderdragonAction_Sitting_Attacking ) + { + // AP - changed this to use playLocalSound because no sound could be heard with playSound (cos it's a stub function) + level->playLocalSound(x, y, z, eSoundType_MOB_ENDERDRAGON_GROWL, 0.5f, 0.8f + random->nextFloat() * .3f, 100.0f); + } + } + else + { + double xdd = xTarget - x; + double ydd = yTarget - y; + double zdd = zTarget - z; + + double dist = xdd * xdd + ydd * ydd + zdd * zdd; + + if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ) + { + --m_actionTicks; + if(m_actionTicks <= 0) + { + if( m_flameAttacks >= SITTING_FLAME_ATTACKS_COUNT) + { + setSynchedAction(e_EnderdragonAction_Takeoff); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: Takeoff\n"); +#endif + newTarget = true; + } + else + { + setSynchedAction(e_EnderdragonAction_Sitting_Scanning); + attackTarget = level->getNearestPlayer( shared_from_this(), SITTING_ATTACK_VIEW_RANGE, SITTING_ATTACK_Y_VIEW_RANGE ); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: SittingScanning\n"); +#endif + } + } + } + else if( getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ) + { + attackTarget = level->getNearestPlayer( shared_from_this(), SITTING_ATTACK_VIEW_RANGE, SITTING_ATTACK_Y_VIEW_RANGE ); + + ++m_actionTicks; + if( attackTarget != NULL ) + { + if(m_actionTicks > SITTING_SCANNING_IDLE_TICKS/4) + { + setSynchedAction(e_EnderdragonAction_Sitting_Attacking); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: SittingAttacking\n"); +#endif + m_actionTicks = ATTACK_TICKS; + } + } + else + { + if(m_actionTicks >= SITTING_SCANNING_IDLE_TICKS) + { + setSynchedAction(e_EnderdragonAction_Takeoff); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: Takeoff\n"); +#endif + newTarget = true; + } + } + } + else if( getSynchedAction() == e_EnderdragonAction_Sitting_Attacking ) + { + --m_actionTicks; + if(m_actionTicks <= 0) + { + ++m_flameAttacks; + setSynchedAction(e_EnderdragonAction_Sitting_Flaming); + attackTarget = level->getNearestPlayer( shared_from_this(), SITTING_ATTACK_VIEW_RANGE, SITTING_ATTACK_Y_VIEW_RANGE ); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: SittingFlaming\n"); +#endif + m_actionTicks = FLAME_TICKS; + } + } + else if( !newTarget && getSynchedAction() == e_EnderdragonAction_Takeoff) + { + int eggHeight = level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS); //level->getHeightmap(4,4); + + float dist = distanceToSqr(PODIUM_X_POS, eggHeight, PODIUM_Z_POS); + if(dist > (10.0f * 10.0f) ) + { + setSynchedAction(e_EnderdragonAction_HoldingPattern); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: HoldingPattern\n"); +#endif + } + } + else if (newTarget || ( (getSynchedAction() != e_EnderdragonAction_Landing && dist < 10 * 10) || dist < 1) || dist > 150 * 150 || horizontalCollision || verticalCollision) + { + findNewTarget(); + } + + if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || getSynchedAction() == e_EnderdragonAction_Landing ) + { + if( m_actionTicks < (FLAME_TICKS - 10) ) + { + vector > *targets = level->getEntities(shared_from_this(), m_acidArea); + + for( AUTO_VAR(it, targets->begin() ); it != targets->end(); ++it) + { + shared_ptr e = dynamic_pointer_cast( *it ); + if (e != NULL) + { + //app.DebugPrintf("Attacking entity with acid\n"); + e->hurt(DamageSource::dragonbreath, 2); + } + } + } + } + if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming ) + { + // No movement + } + else if( getSynchedAction() == e_EnderdragonAction_Sitting_Scanning ) + { + if( attackTarget != NULL) + { + Vec3 *aim = Vec3::newTemp((attackTarget->x - x), 0, (attackTarget->z - z))->normalize(); + Vec3 *dir = Vec3::newTemp(sin(yRot * PI / 180), 0, -cos(yRot * PI / 180))->normalize(); + float dot = (float)dir->dot(aim); + float angleDegs = acos(dot)*180/PI; + angleDegs = angleDegs + 0.5f; + + if( angleDegs < 0 || angleDegs > 10 ) + { + double xdd = attackTarget->x - head->x; + //double ydd = (attackTarget->bb->y0 + attackTarget->bbHeight / 2) - (head->y + head->bbHeight / 2); + double zdd = attackTarget->z - head->z; + + double yRotT = (180) - atan2(xdd, zdd) * 180 / PI; + double yRotD = Mth::wrapDegrees(yRotT - yRot); + + if (yRotD > 50) yRotD = 50; + if (yRotD < -50) yRotD = -50; + + double xd = xTarget - x; + double zd = zTarget - z; + yRotA *= 0.80f; + + float rotSpeed = sqrt(xd * xd + zd * zd) * 1 + 1; + double distToTarget = sqrt(xd * xd + zd * zd) * 1 + 1; + if (distToTarget > 40) distToTarget = 40; + yRotA += yRotD * ((0.7f / distToTarget) / rotSpeed); + yRot += yRotA; + } + else + { + //setSynchedAction(e_EnderdragonAction_Sitting_Flaming); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + //app.DebugPrintf("Dragon action is now : SittingFlaming\n"); +#endif + //m_actionTicks = FLAME_TICKS; + } + } + else + { + //setSynchedAction(e_EnderdragonAction_Sitting_Flaming); + //app.DebugPrintf("Dragon action is now : SittingFlaming\n"); + //m_actionTicks = 0; + } + } + else if( getSynchedAction() == e_EnderdragonAction_Sitting_Attacking ) + { + + } + else + { +// double xTargetO = xTarget; +// double yTargetO = yTarget; +// double zTargetO = zTarget; + if (getSynchedAction() == e_EnderdragonAction_StrafePlayer && attackTarget != NULL && m_currentPath != NULL && m_currentPath->isDone()) + { + xTarget = attackTarget->x; + zTarget = attackTarget->z; + + double xd = xTarget - x; + double zd = zTarget - z; + double sd = sqrt(xd * xd + zd * zd); + double ho = 0.4f + sd / 80.0f - 1; + if (ho > 10) ho = 10; + yTarget = attackTarget->bb->y0 + ho; + } + else + { + //xTarget += random->nextGaussian() * 2; + //zTarget += random->nextGaussian() * 2; + } + ydd = ydd / (sqrt(xdd * xdd + zdd * zdd)); + float max = 0.6f; + if(getSynchedAction() == e_EnderdragonAction_Landing) max = 1.5f; + if (ydd < -max) ydd = -max; + if (ydd > max) ydd = max; + yd += (ydd) * 0.1f; + while (yRot < -180) + yRot += 180 * 2; + while (yRot >= 180) + yRot -= 180 * 2; + + + double yRotT = (180) - atan2(xdd, zdd) * 180 / PI; + double yRotD = yRotT - yRot; + while (yRotD < -180) + yRotD += 180 * 2; + while (yRotD >= 180) + yRotD -= 180 * 2; + + + if (yRotD > 50) yRotD = 50; + if (yRotD < -50) yRotD = -50; + + Vec3 *aim = Vec3::newTemp((xTarget - x), (yTarget - y), (zTarget - z))->normalize(); + Vec3 *dir = Vec3::newTemp(sin(yRot * PI / 180), yd, -cos(yRot * PI / 180))->normalize(); + float dot = (float) (dir->dot(aim) + 0.5f) / 1.5f; + if (dot < 0) dot = 0; + + yRotA *= 0.80f; + + float rotSpeed = sqrt(xd * xd + zd * zd) * 1 + 1; + double distToTarget = sqrt(xd * xd + zd * zd) * 1 + 1; + if (distToTarget > 40) distToTarget = 40; + if(getSynchedAction() == e_EnderdragonAction_Landing) + { + yRotA += yRotD * (distToTarget / rotSpeed); + } + else + { + yRotA += yRotD * ((0.7f / distToTarget) / rotSpeed); + } + yRot += yRotA * 0.1f; + + float span = (float) (2.0f / (distToTarget + 1)); + float speed = 0.06f; + moveRelative(0, -1, speed * (dot * span + (1 - span))); + if (inWall) + { + move(xd * 0.8f, yd * 0.8f, zd * 0.8f); + } + else + { + move(xd, yd, zd); + + } + + Vec3 *actual = Vec3::newTemp(xd, yd, zd)->normalize(); + float slide = (float) (actual->dot(dir) + 1) / 2.0f; + slide = 0.8f + 0.15f * slide; + + + xd *= slide; + zd *= slide; + yd *= 0.91f; + } + } + + yBodyRot = yRot; + + head->bbWidth = head->bbHeight = 1; // 4J Stu - Replaced what was "head" with "neck" //3; + neck->bbWidth = neck->bbHeight = 3; + tail1->bbWidth = tail1->bbHeight = 2; + tail2->bbWidth = tail2->bbHeight = 2; + tail3->bbWidth = tail3->bbHeight = 2; + body->bbHeight = 3; + body->bbWidth = 5; + wing1->bbHeight = 2; + wing1->bbWidth = 4; + wing2->bbHeight = 3; + wing2->bbWidth = 4; + + //double latencyPosAcomponents[3],latencyPosBcomponents[3]; + //doubleArray latencyPosA = doubleArray(latencyPosAcomponents,3); + //doubleArray latencyPosB = doubleArray(latencyPosBcomponents,3); + //getLatencyPos(latencyPosA, 5, 1); + //getLatencyPos(latencyPosB, 10, 1); + + //float tilt = (float) (latencyPosA[1] - latencyPosB[1]) * 10 / 180.0f * PI; + float tilt = (float) getTilt(1) / 180.0f * PI; + float ccTilt = cos(tilt); + + // 4J Stu - ssTilt was negative sin(tilt), but this causes the bounding boxes of the parts to head in the wrong y direction + // i.e. head moves up when tilting forward, and down when tilting backwards + float ssTilt = sin(tilt); + + + float rot1 = yRot * PI / 180; + float ss1 = sin(rot1); + float cc1 = cos(rot1); + + body->tick(); + body->moveTo(x + ss1 * 0.5f, y, z - cc1 * 0.5f, 0, 0); + wing1->tick(); + wing1->moveTo(x + cc1 * 4.5f, y + 2, z + ss1 * 4.5f, 0, 0); + wing2->tick(); + wing2->moveTo(x - cc1 * 4.5f, y + 2, z - ss1 * 4.5f, 0, 0); + + if (!level->isClientSide) checkAttack(); + if (!level->isClientSide && hurtDuration == 0) + { + knockBack(level->getEntities(shared_from_this(), wing1->bb->grow(4, 2, 4)->move(0, -2, 0))); + knockBack(level->getEntities(shared_from_this(), wing2->bb->grow(4, 2, 4)->move(0, -2, 0))); + hurt(level->getEntities(shared_from_this(), neck->bb->grow(1, 1, 1))); + hurt(level->getEntities(shared_from_this(), head->bb->grow(1, 1, 1))); + } + + double p1components[3]; + doubleArray p1 = doubleArray(p1components, 3); + getLatencyPos(p1, 5, 1); + + { + //double p0components[3]; + //doubleArray p0 = doubleArray(p0components, 3); + //getLatencyPos(p0, 0, 1); + + double yRotDiff = getHeadYRotDiff(1); + + float ss = sin((yRot + yRotDiff) * PI / 180 - yRotA * 0.01f); + float cc = cos((yRot + yRotDiff) * PI / 180 - yRotA * 0.01f); + head->tick(); + neck->tick(); + double yOffset = getHeadYOffset(1); // (p0[1] - p1[1]) * 1 + + // 4J Stu - Changed the head entity to only be the head, and not include the neck parts + head->moveTo(x + ss * 6.5f * ccTilt, y + yOffset + ssTilt * 6.5f, z - cc * 6.5f * ccTilt, 0, 0); + + // Neck position is where the java code used to move the "head" object which was head and neck + neck->moveTo(x + ss * 5.5f * ccTilt, y + yOffset + ssTilt * 5.5f, z - cc * 5.5f * ccTilt, 0, 0); + + double acidX = x + ss * 9.5f * ccTilt; + double acidY = y + yOffset + ssTilt * 10.5f; + double acidZ = z - cc * 9.5f * ccTilt; + m_acidArea->set(acidX - 5, acidY - 17, acidZ - 5, acidX + 5, acidY + 4, acidZ + 5); + + //app.DebugPrintf("\nDragon is %s, yRot = %f, yRotA = %f, ss = %f, cc = %f, ccTilt = %f\n",level->isClientSide?"client":"server", yRot, yRotA, ss, cc, ccTilt); + //app.DebugPrintf("Body (%f,%f,%f) to (%f,%f,%f)\n", body->bb->x0, body->bb->y0, body->bb->z0, body->bb->x1, body->bb->y1, body->bb->z1); + //app.DebugPrintf("Neck (%f,%f,%f) to (%f,%f,%f)\n", neck->bb->x0, neck->bb->y0, neck->bb->z0, neck->bb->x1, neck->bb->y1, neck->bb->z1); + //app.DebugPrintf("Head (%f,%f,%f) to (%f,%f,%f)\n", head->bb->x0, head->bb->y0, head->bb->z0, head->bb->x1, head->bb->y1, head->bb->z1); + //app.DebugPrintf("Acid (%f,%f,%f) to (%f,%f,%f)\n\n", m_acidArea->x0, m_acidArea->y0, m_acidArea->z0, m_acidArea->x1, m_acidArea->y1, m_acidArea->z1); + } + + // Curls/straightens the tail + for (int i = 0; i < 3; i++) + { + shared_ptr part = nullptr; + + if (i == 0) part = tail1; + if (i == 1) part = tail2; + if (i == 2) part = tail3; + + double p0components[3]; + doubleArray p0 = doubleArray(p0components, 3); + getLatencyPos(p0, 12 + i * 2, 1); + + float rot = yRot * PI / 180 + rotWrap(p0[0] - p1[0]) * PI / 180 * (1); + float ss = sin(rot); + float cc = cos(rot); + + float dd1 = 1.5f; + float dd = (i + 1) * 2.0f; + part->tick(); + part->moveTo(x - (ss1 * dd1 + ss * dd) * ccTilt, y + (p0[1] - p1[1]) * 1 - (dd + dd1) * ssTilt + 1.5f, z + (cc1 * dd1 + cc * dd) * ccTilt, 0, 0); + } + + +// 4J Stu - Fireball attack taken from Ghast + if (!level->isClientSide) + { + double maxDist = 64.0f; + if (getSynchedAction() == e_EnderdragonAction_StrafePlayer && attackTarget != NULL && attackTarget->distanceToSqr(shared_from_this()) < maxDist * maxDist) + { + if (this->canSee(attackTarget)) + { + m_fireballCharge++; + Vec3 *aim = Vec3::newTemp((attackTarget->x - x), 0, (attackTarget->z - z))->normalize(); + Vec3 *dir = Vec3::newTemp(sin(yRot * PI / 180), 0, -cos(yRot * PI / 180))->normalize(); + float dot = (float)dir->dot(aim); + float angleDegs = acos(dot)*180/PI; + angleDegs = angleDegs + 0.5f; + + if (m_fireballCharge >= 20 && ( angleDegs >= 0 && angleDegs < 10 )) + { + double d = 1; + Vec3 *v = getViewVector(1); + float startingX = head->x - v->x * d; + float startingY = head->y + head->bbHeight / 2 + 0.5f; + float startingZ = head->z - v->z * d; + + double xdd = attackTarget->x - startingX; + double ydd = (attackTarget->bb->y0 + attackTarget->bbHeight / 2) - (startingY + head->bbHeight / 2); + double zdd = attackTarget->z - startingZ; + + level->levelEvent(nullptr, LevelEvent::SOUND_GHAST_FIREBALL, (int) x, (int) y, (int) z, 0); + shared_ptr ie = shared_ptr( new DragonFireball(level, dynamic_pointer_cast( shared_from_this() ), xdd, ydd, zdd) ); + ie->x = startingX; + ie->y = startingY; + ie->z = startingZ; + level->addEntity(ie); + m_fireballCharge = 0; + + app.DebugPrintf("Finding new target due to having fired a fireball\n"); + if( m_currentPath != NULL ) + { + while(!m_currentPath->isDone()) + { + m_currentPath->next(); + } + } + newTarget = true; + findNewTarget(); + } + } + else + { + if (m_fireballCharge > 0) m_fireballCharge--; + } + } + else + { + if (m_fireballCharge > 0) m_fireballCharge--; + } + } +// End fireball attack + + if (!level->isClientSide) + { + inWall = checkWalls(head->bb) | checkWalls(neck->bb) | checkWalls(body->bb); + } +} + +void EnderDragon::checkCrystals() +{ + if (nearestCrystal != NULL) + { + if (nearestCrystal->removed) + { + if (!level->isClientSide) + { + hurt(head, DamageSource::explosion, 10); + } + + nearestCrystal = nullptr; + } + else if (tickCount % 10 == 0) + { + if (health < maxHealth) health++; + } + } + + if (random->nextInt(10) == 0) + { + float maxDist = 32; + vector > *crystals = level->getEntitiesOfClass(typeid(EnderCrystal), bb->grow(maxDist, maxDist, maxDist)); + + shared_ptr crystal = nullptr; + double nearest = Double::MAX_VALUE; + //for (Entity ec : crystals) + for(AUTO_VAR(it, crystals->begin()); it != crystals->end(); ++it) + { + shared_ptr ec = dynamic_pointer_cast( *it ); + double dist = ec->distanceToSqr(shared_from_this() ); + if (dist < nearest) + { + nearest = dist; + crystal = ec; + } + } + delete crystals; + + + nearestCrystal = crystal; + } +} + +void EnderDragon::checkAttack() +{ + //if (tickCount % 20 == 0) + { +// Vec3 *v = getViewVector(1); +// double xdd = 0; +// double ydd = -1; +// double zdd = 0; + + // double x = (body.bb.x0 + body.bb.x1) / 2; + // double y = (body.bb.y0 + body.bb.y1) / 2 - 2; + // double z = (body.bb.z0 + body.bb.z1) / 2; + + } +} + +void EnderDragon::knockBack(vector > *entities) +{ + double xm = (body->bb->x0 + body->bb->x1) / 2; + // double ym = (body.bb.y0 + body.bb.y1) / 2; + double zm = (body->bb->z0 + body->bb->z1) / 2; + + //for (Entity e : entities) + for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) + { + shared_ptr e = dynamic_pointer_cast( *it ); + if (e != NULL)//(e instanceof Mob) + { + double xd = e->x - xm; + double zd = e->z - zm; + double dd = xd * xd + zd * zd; + e->push(xd / dd * 4, 0.2f, zd / dd * 4); + } + } +} + +void EnderDragon::hurt(vector > *entities) +{ + //for (int i = 0; i < entities->size(); i++) + for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) + { + shared_ptr e = dynamic_pointer_cast( *it );//entities.get(i); + if (e != NULL) //(e instanceof Mob) + { + DamageSource *damageSource = DamageSource::mobAttack( dynamic_pointer_cast( shared_from_this() )); + e->hurt(damageSource, 10); + delete damageSource; + } + } +} + +void EnderDragon::findNewTarget() +{ + shared_ptr playerNearestToEgg = nullptr; + + // Update current action + switch(getSynchedAction()) + { + case e_EnderdragonAction_Takeoff: + case e_EnderdragonAction_HoldingPattern: + { + if(!newTarget && m_currentPath != NULL && m_currentPath->isDone()) + { + // Distance is 64, which is the radius of the circle + int eggHeight = max(level->seaLevel + 5, level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS)); //level->getHeightmap(4,4); + playerNearestToEgg = level->getNearestPlayer(PODIUM_X_POS, eggHeight, PODIUM_Z_POS, 64.0); + double dist = 64.0f; + if(playerNearestToEgg != NULL) + { + dist = playerNearestToEgg->distanceToSqr(PODIUM_X_POS, eggHeight, PODIUM_Z_POS); + dist /= (8*8*8); + } + //app.DebugPrintf("Adjusted dist is %f\n", dist); + + if( random->nextInt(m_remainingCrystalsCount + 3) == 0 ) + { + setSynchedAction(e_EnderdragonAction_LandingApproach); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: LandingApproach\n"); +#endif + } + // More likely to strafe a player if they are close to the egg, or there are not many crystals remaining + else if( playerNearestToEgg != NULL && (random->nextInt( abs(dist) + 2 ) == 0 || random->nextInt( m_remainingCrystalsCount + 2 ) == 0) ) + { + setSynchedAction(e_EnderdragonAction_StrafePlayer); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: StrafePlayer\n"); +#endif + } + } + } + break; + case e_EnderdragonAction_StrafePlayer: + // Always return to the holding pattern after strafing + if(m_currentPath == NULL || (m_currentPath->isDone() && newTarget) ) + { + setSynchedAction(e_EnderdragonAction_HoldingPattern); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: HoldingPattern\n"); +#endif + } + break; + case e_EnderdragonAction_Landing: +// setSynchedAction(e_EnderdragonAction_Sitting_Flaming); +//#if PRINT_DRAGON_STATE_CHANGE_MESSAGES +// app.DebugPrintf("Dragon action is now: SittingFlaming\n"); +//#endif +// m_actionTicks = FLAME_TICKS; + + m_flameAttacks = 0; + setSynchedAction(e_EnderdragonAction_Sitting_Scanning); + attackTarget = level->getNearestPlayer( shared_from_this(), SITTING_ATTACK_VIEW_RANGE, SITTING_ATTACK_Y_VIEW_RANGE ); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: SittingScanning\n"); +#endif + m_actionTicks = 0; + break; + }; + + newTarget = false; + + //if (random->nextInt(2) == 0 && level->players.size() > 0) + if(getSynchedAction() == e_EnderdragonAction_StrafePlayer && playerNearestToEgg != NULL) + { + attackTarget = playerNearestToEgg; + strafeAttackTarget(); + } + else if(getSynchedAction() == e_EnderdragonAction_LandingApproach) + { + // Generate a new path if we don't currently have one + if( m_currentPath == NULL || m_currentPath->isDone() ) + { + int currentNodeIndex = findClosestNode(); + + // To get the angle to the player correct when landing, head to a node diametrically opposite the player, then swoop in to 4,4 + int eggHeight = max( level->seaLevel + 5, level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS) ); //level->getHeightmap(4,4); + playerNearestToEgg = level->getNearestPlayer(PODIUM_X_POS, eggHeight, PODIUM_Z_POS, 128.0); + + int targetNodeIndex = 0 ; + if(playerNearestToEgg != NULL) + { + Vec3 *aim = Vec3::newTemp(playerNearestToEgg->x, 0, playerNearestToEgg->z)->normalize(); + //app.DebugPrintf("Final marker node near (%f,%d,%f)\n", -aim->x*40,105,-aim->z*40 ); + targetNodeIndex = findClosestNode(-aim->x*40,105.0,-aim->z*40); + } + else + { + targetNodeIndex = findClosestNode(40.0, eggHeight, 0.0); + } + Node finalNode(PODIUM_X_POS, eggHeight, PODIUM_Z_POS); + + if(m_currentPath != NULL) delete m_currentPath; + m_currentPath = findPath(currentNodeIndex,targetNodeIndex, &finalNode); + + // Always skip the first node (as that's where we are already) + if(m_currentPath != NULL) m_currentPath->next(); + } + + m_actionTicks = 0; + + navigateToNextPathNode(); + + if(m_currentPath != NULL && m_currentPath->isDone()) + { + setSynchedAction(e_EnderdragonAction_Landing); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: Landing\n"); +#endif + } + } + else if(getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || + getSynchedAction() == e_EnderdragonAction_Sitting_Attacking || + getSynchedAction() == e_EnderdragonAction_Sitting_Scanning) + { + // Does no movement + } + else + { + // Default is e_EnderdragonAction_HoldingPattern + // Generate a new path if we don't currently have one + if(m_currentPath == NULL || m_currentPath->isDone() ) + { + int currentNodeIndex = findClosestNode(); + int targetNodeIndex = currentNodeIndex; + //if(random->nextInt(4) == 0) m_holdingPatternClockwise = !m_holdingPatternClockwise; + + if( getSynchedAction() == e_EnderdragonAction_Takeoff) + { + Vec3 *v = getHeadLookVector(1); + targetNodeIndex = findClosestNode(-v->x*40,105.0,-v->z*40); + } + else + { + if(random->nextInt(8) == 0) + { + m_holdingPatternClockwise = !m_holdingPatternClockwise; + targetNodeIndex = targetNodeIndex + 6; + } + + if(m_holdingPatternClockwise) targetNodeIndex = targetNodeIndex + 1; + else targetNodeIndex = targetNodeIndex - 1; + } + + if(m_remainingCrystalsCount <= 0) + { + // If no crystals left, navigate only between nodes 12-19 + targetNodeIndex -= 12; + targetNodeIndex = targetNodeIndex&7; // 4J-RR - was %8, but that could create a result of -1 here when targetNodeIndex was 11 + targetNodeIndex += 12; + } + else + { + // If crystals are left, navigate only between nodes 0-11 + targetNodeIndex = targetNodeIndex%12; + if(targetNodeIndex < 0) targetNodeIndex += 12; + } + + if(m_currentPath != NULL) delete m_currentPath; + m_currentPath = findPath(currentNodeIndex,targetNodeIndex); + + // Always skip the first node (as that's where we are already) + if(m_currentPath != NULL) m_currentPath->next(); + } + + navigateToNextPathNode(); + + if(getSynchedAction() != e_EnderdragonAction_StrafePlayer) attackTarget = nullptr; + } +} + +float EnderDragon::rotWrap(double d) +{ + while (d >= 180) + d -= 360; + while (d < -180) + d += 360; + return (float) d; +} + +bool EnderDragon::checkWalls(AABB *bb) +{ + int x0 = Mth::floor(bb->x0); + int y0 = Mth::floor(bb->y0); + int z0 = Mth::floor(bb->z0); + int x1 = Mth::floor(bb->x1); + int y1 = Mth::floor(bb->y1); + int z1 = Mth::floor(bb->z1); + bool hitWall = false; + bool destroyedTile = false; + for (int x = x0; x <= x1; x++) + { + for (int y = y0; y <= y1; y++) + { + for (int z = z0; z <= z1; z++) + { + int t = level->getTile(x, y, z); + // 4J Stu - Don't remove fire + if (t == 0 || t == Tile::fire_Id) + { + + } + else if (t == Tile::obsidian_Id || t == Tile::whiteStone_Id || t == Tile::unbreakable_Id) + { + hitWall = true; + } + else + { + destroyedTile = true; + level->setTile(x, y, z, 0); + } + } + } + } + + if (destroyedTile) + { + double x = bb->x0 + (bb->x1 - bb->x0) * random->nextFloat(); + double y = bb->y0 + (bb->y1 - bb->y0) * random->nextFloat(); + double z = bb->z0 + (bb->z1 - bb->z0) * random->nextFloat(); + level->addParticle(eParticleType_largeexplode, x, y, z, 0, 0, 0); + } + + return hitWall; +} + +bool EnderDragon::hurt(shared_ptr bossMobPart, DamageSource *source, int damage) +{ + if (bossMobPart != head) + { + damage = damage / 4 + 1; + } + + //float rot1 = yRot * PI / 180; + //float ss1 = sin(rot1); + //float cc1 = cos(rot1); + + //xTarget = x + ss1 * 5 + (random->nextFloat() - 0.5f) * 2; + //yTarget = y + random->nextFloat() * 3 + 1; + //zTarget = z - cc1 * 5 + (random->nextFloat() - 0.5f) * 2; + //attackTarget = NULL; + + if (source == DamageSource::explosion || (dynamic_pointer_cast(source->getEntity()) != NULL)) + { + int healthBefore = health; + reallyHurt(source, damage); + + //if(!level->isClientSide) app.DebugPrintf("Health is now %d\n", health); + if( health <= 0 && + !( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || + getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || + getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) ) + { + health = 1; + + if( setSynchedAction(e_EnderdragonAction_LandingApproach) ) + { + if( m_currentPath != NULL ) + { + while(!m_currentPath->isDone()) + { + m_currentPath->next(); + } + } + app.DebugPrintf("Dragon should be dead, so landing.\n"); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: LandingApproach\n"); +#endif + findNewTarget(); + } + } + + if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || + getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || + getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) + { + m_sittingDamageReceived += healthBefore - health; + + if(m_sittingDamageReceived > (SITTING_ALLOWED_DAMAGE_PERCENTAGE*getMaxHealth() ) ) + { + m_sittingDamageReceived = 0; + setSynchedAction(e_EnderdragonAction_Takeoff); + newTarget = true; +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: Takeoff\n"); +#endif + } + } + } + return true; +} + +void EnderDragon::tickDeath() +{ + if( getSynchedAction() != e_EnderdragonAction_Sitting_Flaming && + getSynchedAction() != e_EnderdragonAction_Sitting_Scanning && + getSynchedAction() != e_EnderdragonAction_Sitting_Attacking) + { + if(!level->isClientSide) health = 1; + return; + } + + dragonDeathTime++; + if (dragonDeathTime >= 180 && dragonDeathTime <= 200) + { + float xo = (random->nextFloat() - 0.5f) * 8; + float yo = (random->nextFloat() - 0.5f) * 4; + float zo = (random->nextFloat() - 0.5f) * 8; + level->addParticle(eParticleType_hugeexplosion, x + xo, y + 2 + yo, z + zo, 0, 0, 0); + } + if (!level->isClientSide) + { + if (dragonDeathTime > 150 && dragonDeathTime % 5 == 0) + { + int xpCount = 1000; + while (xpCount > 0) + { + int newCount = ExperienceOrb::getExperienceValue(xpCount); + xpCount -= newCount; + level->addEntity(shared_ptr( new ExperienceOrb(level, x, y, z, newCount) )); + } + } + if (dragonDeathTime == 1) + { + //level->globalLevelEvent(LevelEvent::SOUND_DRAGON_DEATH, (int) x, (int) y, (int) z, 0); + level->levelEvent(LevelEvent::SOUND_DRAGON_DEATH, (int) x, (int) y, (int) z, 0); + } + } + move(0, 0.1f, 0); + yBodyRot = yRot += 20.0f; + + if (dragonDeathTime == 200 && !level->isClientSide) + { + //level->levelEvent(NULL, LevelEvent::ENDERDRAGON_KILLED, (int) x, (int) y, (int) z, 0); + + int xpCount = 2000; + while (xpCount > 0) + { + int newCount = ExperienceOrb::getExperienceValue(xpCount); + xpCount -= newCount; + level->addEntity(shared_ptr( new ExperienceOrb(level, x, y, z, newCount))); + } + int xo = 5 + random->nextInt(2) * 2 - 1; + int zo = 5 + random->nextInt(2) * 2 - 1; + if (random->nextInt(2) == 0) + { + xo = 0; + } + else + { + zo = 0; + } + // 4J-PB changed to center this between the pillars + spawnExitPortal(0,0);//Mth::floor(x), Mth::floor(z)); + remove(); + } +} + +void EnderDragon::spawnExitPortal(int x, int z) +{ + int y = level->seaLevel; + + TheEndPortal::allowAnywhere(true); + + int r = 4; + for (int yy = y - 1; yy <= y + 32; yy++) + { + for (int xx = x - r; xx <= x + r; xx++) + { + for (int zz = z - r; zz <= z + r; zz++) + { + double xd = xx - x; + double zd = zz - z; + double d = sqrt(xd * xd + zd * zd); + if (d <= r - 0.5) + { + if (yy < y) + { + if (d > r - 1 - 0.5) + { + } + else + { + level->setTile(xx, yy, zz, Tile::unbreakable_Id); + } + } + else if (yy > y) + { + level->setTile(xx, yy, zz, 0); + } + else + { + if (d > r - 1 - 0.5) + { + level->setTile(xx, yy, zz, Tile::unbreakable_Id); + } + else + { + level->setTile(xx, yy, zz, Tile::endPortalTile_Id); + } + } + } + } + } + } + + level->setTile(x, y + 0, z, Tile::unbreakable_Id); + level->setTile(x, y + 1, z, Tile::unbreakable_Id); + level->setTile(x, y + 2, z, Tile::unbreakable_Id); + level->setTile(x - 1, y + 2, z, Tile::torch_Id); + level->setTile(x + 1, y + 2, z, Tile::torch_Id); + level->setTile(x, y + 2, z - 1, Tile::torch_Id); + level->setTile(x, y + 2, z + 1, Tile::torch_Id); + level->setTile(x, y + 3, z, Tile::unbreakable_Id); + level->setTile(x, y + 4, z, Tile::dragonEgg_Id); + + // 4J-PB - The podium can be floating with nothing under it, so put some whiteStone under it if this is the case + for (int yy = y - 5; yy < y - 1; yy++) + { + for (int xx = x - (r - 1); xx <= x + (r - 1); xx++) + { + for (int zz = z - (r - 1); zz <= z + (r - 1); zz++) + { + if(level->isEmptyTile(xx,yy,zz)) + { + level->setTile(xx, yy, zz, Tile::whiteStone_Id); + } + } + } + } + + TheEndPortal::allowAnywhere(false); +} + +void EnderDragon::checkDespawn() +{ +} + +vector > *EnderDragon::getSubEntities() +{ + return &subEntities; +} + +bool EnderDragon::isPickable() +{ + return false; +} + +// Fix for TU9 Enderdragon sound hits being the player sound hits - moved this forward from later version +int EnderDragon::getHurtSound() +{ + return eSoundType_MOB_ENDERDRAGON_HIT; +} + +int EnderDragon::getSynchedHealth() +{ + return entityData->getInteger(DATA_ID_SYNCHED_HEALTH); +} + +// 4J Added for new dragon behaviour +bool EnderDragon::setSynchedAction(EEnderdragonAction action, bool force /*= false*/) +{ + bool validTransition = false; + // Check if this is a valid state transition + switch(getSynchedAction()) + { + case e_EnderdragonAction_HoldingPattern: + switch(action) + { + case e_EnderdragonAction_StrafePlayer: + case e_EnderdragonAction_LandingApproach: + validTransition = true; + break; + }; + break; + case e_EnderdragonAction_StrafePlayer: + switch(action) + { + case e_EnderdragonAction_HoldingPattern: + case e_EnderdragonAction_LandingApproach: + validTransition = true; + break; + }; + break; + case e_EnderdragonAction_LandingApproach: + switch(action) + { + case e_EnderdragonAction_Landing: + validTransition = true; + break; + }; + break; + case e_EnderdragonAction_Landing: + switch(action) + { + case e_EnderdragonAction_Sitting_Flaming: + case e_EnderdragonAction_Sitting_Scanning: + validTransition = true; + break; + }; + break; + case e_EnderdragonAction_Takeoff: + switch(action) + { + case e_EnderdragonAction_HoldingPattern: + validTransition = true; + break; + }; + break; + case e_EnderdragonAction_Sitting_Flaming: + switch(action) + { + case e_EnderdragonAction_Sitting_Scanning: + case e_EnderdragonAction_Sitting_Attacking: + case e_EnderdragonAction_Takeoff: + validTransition = true; + break; + }; + break; + case e_EnderdragonAction_Sitting_Scanning: + switch(action) + { + case e_EnderdragonAction_Sitting_Flaming: + case e_EnderdragonAction_Sitting_Attacking: + case e_EnderdragonAction_Takeoff: + validTransition = true; + break; + }; + break; + case e_EnderdragonAction_Sitting_Attacking: + switch(action) + { + case e_EnderdragonAction_Sitting_Flaming: + case e_EnderdragonAction_Sitting_Scanning: + case e_EnderdragonAction_Takeoff: + validTransition = true; + break; + }; + break; + }; + + if( force || validTransition ) + { + entityData->set(DATA_ID_SYNCHED_ACTION, action); + } + else + { + app.DebugPrintf("EnderDragon: Invalid state transition from %d to %d\n", getSynchedAction(), action); + } + + return force || validTransition; +} + +EnderDragon::EEnderdragonAction EnderDragon::getSynchedAction() +{ + return (EEnderdragonAction)entityData->getInteger(DATA_ID_SYNCHED_ACTION); +} + +void EnderDragon::handleCrystalDestroyed(DamageSource *source) +{ + AABB *tempBB = AABB::newTemp(PODIUM_X_POS,84.0,PODIUM_Z_POS,PODIUM_X_POS+1.0,85.0,PODIUM_Z_POS+1.0); + vector > *crystals = level->getEntitiesOfClass(typeid(EnderCrystal), tempBB->grow(48, 40, 48)); + m_remainingCrystalsCount = (int)crystals->size() - 1; + if(m_remainingCrystalsCount < 0) m_remainingCrystalsCount = 0; + delete crystals; + + app.DebugPrintf("Crystal count is now %d\n",m_remainingCrystalsCount); + + //--m_remainingCrystalsCount; + + if(m_remainingCrystalsCount%2 == 0) + { + if(setSynchedAction(e_EnderdragonAction_LandingApproach)) + { + if( m_currentPath != NULL ) + { + while(!m_currentPath->isDone()) + { + m_currentPath->next(); + } + } + m_actionTicks = 1; +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: LandingApproach\n"); +#endif + } + } + else if(dynamic_pointer_cast(source->getEntity()) != NULL) + { + if(setSynchedAction(e_EnderdragonAction_StrafePlayer)) + { + attackTarget = dynamic_pointer_cast(source->getEntity()); +#if PRINT_DRAGON_STATE_CHANGE_MESSAGES + app.DebugPrintf("Dragon action is now: StrafePlayer\n"); +#endif + strafeAttackTarget(); + } + } +} + +void EnderDragon::strafeAttackTarget() +{ + app.DebugPrintf("Setting path to strafe attack target\n"); + int currentNodeIndex = findClosestNode(); + int targetNodeIndex = findClosestNode(attackTarget->x,attackTarget->y,attackTarget->z); + + int finalXTarget = attackTarget->x; + int finalZTarget = attackTarget->z; + + double xd = finalXTarget - x; + double zd = finalZTarget - z; + double sd = sqrt(xd * xd + zd * zd); + double ho = 0.4f + sd / 80.0f - 1; + if (ho > 10) ho = 10; + int finalYTarget = attackTarget->bb->y0 + ho; + + Node finalNode(finalXTarget, finalYTarget, finalZTarget); + + if(m_currentPath != NULL) delete m_currentPath; + m_currentPath = findPath(currentNodeIndex,targetNodeIndex, &finalNode); + + if(m_currentPath != NULL) + { + // Always skip the first node (as that's where we are already) + m_currentPath->next(); + + navigateToNextPathNode(); + } +} + +void EnderDragon::navigateToNextPathNode() +{ + if(m_currentPath != NULL && !m_currentPath->isDone()) + { + Vec3 *curr = m_currentPath->currentPos(); + + m_currentPath->next(); + xTarget = curr->x; + + if(getSynchedAction() == e_EnderdragonAction_LandingApproach && m_currentPath->isDone()) + { + // When heading to the last node on the landing approach, we want the yCoord to be exact + yTarget = curr->y; + } + else + { + do + { + yTarget = curr->y + random->nextFloat() * 20; + } while( yTarget < (curr->y) ); + } + zTarget = curr->z; + app.DebugPrintf("Path node pos is (%f,%f,%f)\n",curr->x,curr->y,curr->z); + app.DebugPrintf("Setting new target to (%f,%f,%f)\n",xTarget, yTarget, zTarget); + } +} + +int EnderDragon::findClosestNode() +{ + // Setup all the nodes on the first time this is called + if(m_nodes->data[0] == NULL) + { + // Path nodes for navigation + // 0 - 11 are the outer ring at 60 blocks from centre + // 12 - 19 are the middle ring at 40 blocks from centre + // 20 - 23 are the inner ring at 20 blocks from centre + int nodeX=0; + int nodeY=0; + int nodeZ=0; + int multiplier = 0; + for(unsigned int i = 0; i < 24; ++i) + { + int yAdjustment = 5; + multiplier = i; + if(i < 12) + { + nodeX=60 * Mth::cos(2*(-PI+(PI/12)*multiplier)); + nodeZ=60 * Mth::sin(2*(-PI+(PI/12)*multiplier)); + } + else if(i < 20) + { + multiplier -= 12; + nodeX=40 * Mth::cos(2*(-PI+(PI/8)*multiplier)); + nodeZ=40 * Mth::sin(2*(-PI+(PI/8)*multiplier)); + yAdjustment += 10; // Make the target well above the top of the towers + } + else + { + multiplier -= 20; + nodeX=20 * Mth::cos(2*(-PI+(PI/4)*multiplier)); + nodeZ=20 * Mth::sin(2*(-PI+(PI/4)*multiplier)); + } + // Fix for #77202 - TU9: Content: Gameplay: The Ender Dragon sometimes flies through terrain + // Add minimum height + nodeY = max( (level->seaLevel + 10), level->getTopSolidBlock(nodeX, nodeZ) + yAdjustment ); + + app.DebugPrintf("Node %d is at (%d,%d,%d)\n", i, nodeX, nodeY, nodeZ); + + m_nodes->data[i] = new Node(nodeX,nodeY,nodeZ); + + //level->setTile(nodeX,nodeY,nodeZ,Tile::obsidian_Id); + } + + m_nodeAdjacency[0] = (1<<11) | (1<<1) | (1<<12); + m_nodeAdjacency[1] = (1<<0) | (1<<2) | (1<<13); + m_nodeAdjacency[2] = (1<<1) | (1<<3) | (1<<13); + m_nodeAdjacency[3] = (1<<2) | (1<<4) | (1<<14); + m_nodeAdjacency[4] = (1<<3) | (1<<5) | (1<<15); + m_nodeAdjacency[5] = (1<<4) | (1<<6) | (1<<15); + m_nodeAdjacency[6] = (1<<5) | (1<<7) | (1<<16); + m_nodeAdjacency[7] = (1<<6) | (1<<8) | (1<<17); + m_nodeAdjacency[8] = (1<<7) | (1<<9) | (1<<17); + m_nodeAdjacency[9] = (1<<8) | (1<<10) | (1<<18); + m_nodeAdjacency[10] = (1<<9) | (1<<11) | (1<<19); + m_nodeAdjacency[11] = (1<<10) | (1<<0) | (1<<19); + + m_nodeAdjacency[12] = (1<<0) | (1<<13) | (1<<20) | (1<<19); + m_nodeAdjacency[13] = (1<<1) | (1<<2) | (1<<14) | (1<<21) | (1<<20) | (1<<12); + m_nodeAdjacency[14] = (1<<3) | (1<<15) | (1<<21) | (1<<13); + m_nodeAdjacency[15] = (1<<4) | (1<<5) | (1<<16) | (1<<22) | (1<<21) | (1<<14); + m_nodeAdjacency[16] = (1<<6) | (1<<17) | (1<<22) | (1<<15); + m_nodeAdjacency[17] = (1<<7) | (1<<8) | (1<<18) | (1<<23) | (1<<22) | (1<<16); + m_nodeAdjacency[18] = (1<<9) | (1<<19) | (1<<23) | (1<<17); + m_nodeAdjacency[19] = (1<<10) | (1<<11) | (1<<12) | (1<<20) | (1<<23) | (1<<18); + + m_nodeAdjacency[20] = (1<<12) | (1<<13) | (1<<21) | (1<<22) | (1<<23) | (1<<19); + m_nodeAdjacency[21] = (1<<14) | (1<<15) | (1<<22) | (1<<23) | (1<<20) | (1<<13); + m_nodeAdjacency[22] = (1<<15) | (1<<16) | (1<<17) | (1<<23) | (1<<20) | (1<<21); + m_nodeAdjacency[23] = (1<<17) | (1<<18) | (1<<19) | (1<<20) | (1<<21) | (1<<22); + } + + return findClosestNode(x,y,z); +} + +int EnderDragon::findClosestNode(double tX, double tY, double tZ) +{ + float closestDist = 100.0f; + int closestIndex = 0; + Node *currentPos = new Node((int) floor(tX), (int) floor(tY), (int) floor(tZ)); + int startIndex = 0; + if(m_remainingCrystalsCount <= 0) + { + // If not crystals are left then we try and stay in the middle ring and avoid the outer ring + startIndex = 12; + } + for(unsigned int i = startIndex; i < 24; ++i) + { + if( m_nodes->data[i] != NULL ) + { + float dist = m_nodes->data[i]->distanceTo(currentPos); + if(dist < closestDist) + { + closestDist = dist; + closestIndex = i; + } + } + } + delete currentPos; + return closestIndex; +} + +// 4J Stu - A* taken from PathFinder and modified +Path *EnderDragon::findPath(int startIndex, int endIndex, Node *finalNode /* = NULL */) +{ + for(unsigned int i = 0; i < 24; ++i) + { + Node *n = m_nodes->data[i]; + n->closed = false; + n->f = 0; + n->g = 0; + n->h = 0; + n->cameFrom = NULL; + n->heapIdx = -1; + } + + Node *from = m_nodes->data[startIndex]; + Node *to = m_nodes->data[endIndex]; + + from->g = 0; + from->h = from->distanceTo(to); + from->f = from->h; + + openSet->clear(); + openSet->insert(from); + + Node *closest = from; + + int minimumNodeIndex = 0; + if(m_remainingCrystalsCount <= 0) + { + // If not crystals are left then we try and stay in the middle ring and avoid the outer ring + minimumNodeIndex = 12; + } + + while (!openSet->isEmpty()) + { + Node *x = openSet->pop(); + + if (x->equals(to)) + { + app.DebugPrintf("Found path from %d to %d\n", startIndex, endIndex); + if(finalNode != NULL) + { + finalNode->cameFrom = to; + to = finalNode; + } + return reconstruct_path(from, to); + } + + if (x->distanceTo(to) < closest->distanceTo(to)) + { + closest = x; + } + x->closed = true; + + unsigned int xIndex = 0; + for(unsigned int i = 0; i < 24; ++i) + { + if(m_nodes->data[i] == x) + { + xIndex = i; + break; + } + } + + for (int i = minimumNodeIndex; i < 24; i++) + { + if(m_nodeAdjacency[xIndex] & (1<data[i]; + + if(y->closed) continue; + + float tentative_g_score = x->g + x->distanceTo(y); + if (!y->inOpenSet() || tentative_g_score < y->g) + { + y->cameFrom = x; + y->g = tentative_g_score; + y->h = y->distanceTo(to); + if (y->inOpenSet()) + { + openSet->changeCost(y, y->g + y->h); + } + else + { + y->f = y->g + y->h; + openSet->insert(y); + } + } + } + } + } + + if (closest == from) return NULL; + app.DebugPrintf("Failed to find path from %d to %d\n", startIndex, endIndex); + if(finalNode != NULL) + { + finalNode->cameFrom = closest; + closest = finalNode; + } + return reconstruct_path(from, closest); +} + +// function reconstruct_path(came_from,current_node) +Path *EnderDragon::reconstruct_path(Node *from, Node *to) +{ + int count = 1; + Node *n = to; + while (n->cameFrom != NULL) + { + count++; + n = n->cameFrom; + } + + NodeArray nodes = NodeArray(count); + n = to; + nodes.data[--count] = n; + while (n->cameFrom != NULL) + { + n = n->cameFrom; + nodes.data[--count] = n; + } + Path *ret = new Path(nodes); + delete [] nodes.data; + return ret; +} + +void EnderDragon::addAdditonalSaveData(CompoundTag *entityTag) +{ + app.DebugPrintf("Adding EnderDragon additional save data\n"); + entityTag->putShort(L"RemainingCrystals", m_remainingCrystalsCount); + entityTag->putInt(L"DragonState", (int)getSynchedAction() ); + + BossMob::addAdditonalSaveData(entityTag); +} + +void EnderDragon::readAdditionalSaveData(CompoundTag *tag) +{ + app.DebugPrintf("Reading EnderDragon additional save data\n"); + m_remainingCrystalsCount = tag->getShort(L"RemainingCrystals"); + if(!tag->contains(L"RemainingCrystals")) m_remainingCrystalsCount = CRYSTAL_COUNT; + + if(tag->contains(L"DragonState")) setSynchedAction( (EEnderdragonAction)tag->getInt(L"DragonState"), true); + + BossMob::readAdditionalSaveData(tag); +} + +float EnderDragon::getTilt(float a) +{ + float tilt = 0.0f; + //if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || + // getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || + // getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) + //{ + // tilt = -25.0f; + // xRot = -25.0f; + //} + //else + { + double latencyPosAcomponents[3],latencyPosBcomponents[3]; + doubleArray latencyPosA = doubleArray(latencyPosAcomponents,3); + doubleArray latencyPosB = doubleArray(latencyPosBcomponents,3); + getLatencyPos(latencyPosA, 5, a); + getLatencyPos(latencyPosB, 10, a); + + tilt = (latencyPosA[1] - latencyPosB[1]) * 10; + } + //app.DebugPrintf("Tilt is %f\n", tilt); + + return tilt; +} + +double EnderDragon::getHeadYOffset(float a) +{ + double headYOffset = 0.0; + if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || + getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || + getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) + { + headYOffset = -1.0; + } + else + { + double p1components[3]; + doubleArray p1 = doubleArray(p1components, 3); + getLatencyPos(p1, 5, 1); + + double p0components[3]; + doubleArray p0 = doubleArray(p0components, 3); + getLatencyPos(p0, 0, 1); + + headYOffset = (p0[1] - p1[1]) * 1; + } + //app.DebugPrintf("headYOffset is %f\n", headYOffset); + return headYOffset; +} + +double EnderDragon::getHeadYRotDiff(float a) +{ + double result = 0.0; + //if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || + // getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || + // getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) + //{ + // result = m_headYRot; + //} + return result; +} + +double EnderDragon::getHeadPartYOffset(int partIndex, doubleArray bodyPos, doubleArray partPos) +{ + double result = 0.0; + if( getSynchedAction() == e_EnderdragonAction_Landing || getSynchedAction() == e_EnderdragonAction_Takeoff ) + { + int eggHeight = level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS); //level->getHeightmap(4,4); + float dist = sqrt( distanceToSqr(PODIUM_X_POS, eggHeight, PODIUM_Z_POS) )/4; + if( dist < 1.0f ) dist = 1.0f; + result = partIndex / dist; + //app.DebugPrintf("getHeadPartYOffset - dist = %f, result = %f (%d)\n", dist, result, partIndex); + } + else if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || + getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || + getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) + { + result = partIndex; + } + else + { + if(partIndex == 6) + { + result = 0.0; + } + else + { + result = partPos[1] - bodyPos[1]; + } + } + //app.DebugPrintf("Part %d is at %f\n", partIndex, result); + return result; +} + +double EnderDragon::getHeadPartYRotDiff(int partIndex, doubleArray bodyPos, doubleArray partPos) +{ + double result = 0.0; + //if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || + // getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || + // getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) + //{ + // result = m_headYRot / (7 - partIndex); + //} + //else + { + result = partPos[0] - bodyPos[0]; + } + //app.DebugPrintf("Part %d is at %f\n", partIndex, result); + return result; +} + +Vec3 *EnderDragon::getHeadLookVector(float a) +{ + Vec3 *result = NULL; + + if( getSynchedAction() == e_EnderdragonAction_Landing || getSynchedAction() == e_EnderdragonAction_Takeoff ) + { + int eggHeight = level->getTopSolidBlock(PODIUM_X_POS,PODIUM_Z_POS); //level->getHeightmap(4,4); + float dist = sqrt(distanceToSqr(PODIUM_X_POS, eggHeight, PODIUM_Z_POS))/4; + if( dist < 1.0f ) dist = 1.0f; + // The 6.0f is dragon->getHeadPartYOffset(6, start, p) + float yOffset = 6.0f / dist; + + double xRotTemp = xRot; + double rotScale = 1.5f; + xRot = -yOffset * rotScale * 5.0f; + + double yRotTemp = yRot; + yRot += getHeadYRotDiff(a); + + result = getViewVector(a); + + xRot = xRotTemp; + yRot = yRotTemp; + } + else if( getSynchedAction() == e_EnderdragonAction_Sitting_Flaming || + getSynchedAction() == e_EnderdragonAction_Sitting_Scanning || + getSynchedAction() == e_EnderdragonAction_Sitting_Attacking) + { + double xRotTemp = xRot; + double rotScale = 1.5f; + // The 6.0f is dragon->getHeadPartYOffset(6, start, p) + xRot = -6.0f * rotScale * 5.0f; + + double yRotTemp = yRot; + yRot += getHeadYRotDiff(a); + + result = getViewVector(a); + + xRot = xRotTemp; + yRot = yRotTemp; + } + else + { + result = getViewVector(a); + } + return result; +} diff --git a/Minecraft.World/EnderDragon.h b/Minecraft.World/EnderDragon.h new file mode 100644 index 00000000..8a12c08c --- /dev/null +++ b/Minecraft.World/EnderDragon.h @@ -0,0 +1,177 @@ +#pragma once +using namespace std; +#include "BossMob.h" + +class BossMobPart; +class EnderCrystal; +class Node; +class BinaryHeap; +class Path; + +class EnderDragon : public BossMob +{ +public: + eINSTANCEOF GetType() { return eTYPE_ENDERDRAGON; }; + static Entity *create(Level *level) { return new EnderDragon(level); } + +private: + static const int DATA_ID_SYNCHED_HEALTH = 16; + + // 4J Added for new behaviours + static const int DATA_ID_SYNCHED_ACTION = 17; + + static const int positionsLength = 64; // 4J Stu - Defined this so that we can keep the positions array as a basic type array +public: + double xTarget, yTarget, zTarget; + + double positions[positionsLength][3]; + int posPointer; + + //BossMobPart[] subEntities; + vector > subEntities; + shared_ptr head; + shared_ptr neck; // 4J Added + shared_ptr body; + shared_ptr tail1; + shared_ptr tail2; + shared_ptr tail3; + shared_ptr wing1; + shared_ptr wing2; + + float oFlapTime; + float flapTime; + bool newTarget; + bool inWall; + + // 4J Stu - Added for new dragon behaviour +private: + int m_fireballCharge; + float m_holdingPatternAngle; + bool m_holdingPatternClockwise; + int m_actionTicks; + int m_sittingDamageReceived; + int m_remainingCrystalsCount; + int m_flameAttacks; + + int m_iGrowlTimer; + + double m_headYRot; + AABB *m_acidArea; + + NodeArray *m_nodes; + int m_nodeAdjacency[24]; + BinaryHeap *openSet; + Path *m_currentPath; + + enum EEnderdragonAction + { + e_EnderdragonAction_HoldingPattern, + e_EnderdragonAction_StrafePlayer, + e_EnderdragonAction_LandingApproach, + e_EnderdragonAction_Landing, + e_EnderdragonAction_Takeoff, + e_EnderdragonAction_Sitting_Flaming, + e_EnderdragonAction_Sitting_Scanning, + e_EnderdragonAction_Sitting_Attacking, + }; + + static const int CRYSTAL_COUNT; + + static const int FLAME_TICKS; + static const float FLAME_ANGLE; + static const int FLAME_PASSES; + static const int FLAME_FREQUENCY; + static const int FLAME_RANGE; + + static const int ATTACK_TICKS; + + static const int SITTING_ATTACK_Y_VIEW_RANGE; + static const int SITTING_ATTACK_VIEW_RANGE; + static const int SITTING_ATTACK_RANGE; + static const int SITTING_POST_ATTACK_IDLE_TICKS; + static const int SITTING_SCANNING_IDLE_TICKS; + static const int SITTING_FLAME_ATTACKS_COUNT; + + // The percentage of max health that the dragon will take while in the "Sitting" states before flying away + static const float SITTING_ALLOWED_DAMAGE_PERCENTAGE; + + static const int PODIUM_X_POS = 0; + static const int PODIUM_Z_POS = 0; + +private: + shared_ptr attackTarget; + +public: + int dragonDeathTime; + +public: + shared_ptr nearestCrystal; + +private: + void _init(); + +public: + EnderDragon(Level *level); + virtual ~EnderDragon(); + +protected: + virtual void defineSynchedData(); + +public: + void getLatencyPos(doubleArray result, int step, float a); + virtual void aiStep(); + +private: + using BossMob::hurt; + + void checkCrystals(); + void checkAttack(); + void knockBack(vector > *entities); + void hurt(vector > *entities); + void findNewTarget(); + float rotWrap(double d); + bool checkWalls(AABB *bb); + +public: + virtual bool hurt(shared_ptr bossMobPart, DamageSource *source, int damage); + +protected: + virtual void tickDeath(); + +private: + void spawnExitPortal(int x, int z); + +protected: + virtual void checkDespawn(); + virtual int getHurtSound(); +public: + virtual vector > *getSubEntities(); + virtual bool isPickable(); + virtual int getSynchedHealth(); + +private: + // 4J added for new dragon behaviour + bool setSynchedAction(EEnderdragonAction action, bool force = false); + EEnderdragonAction getSynchedAction(); + int findClosestNode(double tX, double tY, double tZ); + int findClosestNode(); + Path *findPath(int startIndex, int endIndex, Node *finalNode = NULL); + Path *reconstruct_path(Node *from, Node *to); + + void strafeAttackTarget(); + void navigateToNextPathNode(); + +public: + virtual void addAdditonalSaveData(CompoundTag *entityTag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +public: + void handleCrystalDestroyed(DamageSource *source); + + float getTilt(float a); + double getHeadYOffset(float a); + double getHeadYRotDiff(float a); + double getHeadPartYOffset(int partIndex, doubleArray bodyPos, doubleArray partPos); + double getHeadPartYRotDiff(int partIndex, doubleArray bodyPos, doubleArray partPos); + Vec3 *getHeadLookVector(float a); +}; diff --git a/Minecraft.World/EnderEyeItem.cpp b/Minecraft.World/EnderEyeItem.cpp new file mode 100644 index 00000000..a08c1f62 --- /dev/null +++ b/Minecraft.World/EnderEyeItem.cpp @@ -0,0 +1,238 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.levelgen.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.phys.h" +#include "EnderEyeItem.h" +#include "SoundTypes.h" +#include "LevelData.h" + +EnderEyeItem::EnderEyeItem(int id) : Item(id) +{ +} + +bool EnderEyeItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + int targetType = level->getTile(x, y, z); + int targetData = level->getData(x, y, z); + + if (player->mayBuild(x, y, z) && targetType == Tile::endPortalFrameTile_Id && !TheEndPortalFrameTile::hasEye(targetData)) + { + if(bTestUseOnOnly) return true; + if (level->isClientSide) return true; + level->setData(x, y, z, targetData + TheEndPortalFrameTile::EYE_BIT); + instance->count--; + + for (int i = 0; i < 16; i++) + { + double xp = x + (5.0f + random->nextFloat() * 6.0f) / 16.0f; + double yp = y + 13.0f / 16.0f; + double zp = z + (5.0f + random->nextFloat() * 6.0f) / 16.0f; + double xa = 0; + double ya = 0; + double za = 0; + + level->addParticle(eParticleType_smoke, xp, yp, zp, xa, ya, za); + } + + // scan if the circle is complete + int direction = targetData & 3; + + // find borders + int min = 0; + int max = 0; + bool firstFound = false; + bool valid = true; + int rightHandDirection = Direction::DIRECTION_CLOCKWISE[direction]; + for (int offset = -2; offset <= 2; offset++) + { + int testX = x + Direction::STEP_X[rightHandDirection] * offset; + int testZ = z + Direction::STEP_Z[rightHandDirection] * offset; + + int tile = level->getTile(testX, y, testZ); + if (tile == Tile::endPortalFrameTile->id) + { + int data = level->getData(testX, y, testZ); + if (!TheEndPortalFrameTile::hasEye(data)) + { + valid = false; + break; + } + max = offset; + if (!firstFound) + { + min = offset; + firstFound = true; + } + } + } + + // got a full frame? + if (valid && max == min + 2) + { + + // check if other edge is valid + for (int offset = min; offset <= max; offset++) + { + int testX = x + Direction::STEP_X[rightHandDirection] * offset; + int testZ = z + Direction::STEP_Z[rightHandDirection] * offset; + testX += Direction::STEP_X[direction] * 4; + testZ += Direction::STEP_Z[direction] * 4; + + int tile = level->getTile(testX, y, testZ); + int data = level->getData(testX, y, testZ); + if (tile != Tile::endPortalFrameTile_Id || !TheEndPortalFrameTile::hasEye(data)) + { + valid = false; + break; + } + } + // check if edges on the sides are valid + for (int side = (min - 1); side <= (max + 1); side += 4) + { + for (int offset = 1; offset <= 3; offset++) + { + int testX = x + Direction::STEP_X[rightHandDirection] * side; + int testZ = z + Direction::STEP_Z[rightHandDirection] * side; + testX += Direction::STEP_X[direction] * offset; + testZ += Direction::STEP_Z[direction] * offset; + + int tile = level->getTile(testX, y, testZ); + int data = level->getData(testX, y, testZ); + if (tile != Tile::endPortalFrameTile_Id || !TheEndPortalFrameTile::hasEye(data)) + { + valid = false; + break; + } + } + } + if (valid) + { + + // fill portal + for (int px = min; px <= max; px++) + { + for (int pz = 1; pz <= 3; pz++) + { + int targetX = x + Direction::STEP_X[rightHandDirection] * px; + int targetZ = z + Direction::STEP_Z[rightHandDirection] * px; + targetX += Direction::STEP_X[direction] * pz; + targetZ += Direction::STEP_Z[direction] * pz; + + level->setTile(targetX, y, targetZ, Tile::endPortalTile_Id); + } + } + } + } + + return true; + } + return false; +} + +bool EnderEyeItem::TestUse(Level *level, shared_ptr player) +{ + HitResult *hr = getPlayerPOVHitResult(level, player, false); + if (hr != NULL && hr->type == HitResult::TILE) + { + int tile = level->getTile(hr->x, hr->y, hr->z); + delete hr; + if (tile == Tile::endPortalFrameTile_Id) + { + return false; + } + } + else if( hr != NULL ) + { + delete hr; + } + + //if (!level->isClientSide) + { + if((level->dimension->id==LevelData::DIMENSION_OVERWORLD) && level->getLevelData()->getHasStronghold()) + { + return true; + } + else + { +// int x,z; +// if(app.GetTerrainFeaturePosition(eTerrainFeature_Stronghold,&x,&z)) +// { +// level->getLevelData()->setXStronghold(x); +// level->getLevelData()->setZStronghold(z); +// level->getLevelData()->setHasStronghold(); +// +// app.DebugPrintf("=== FOUND stronghold in terrain features list\n"); +// +// app.SetXuiServerAction(ProfileManager.GetPrimaryPad(),eXuiServerAction_StrongholdPosition); +// } +// else + { + // can't find the stronghold position in the terrain feature list. Do we have to run a post-process? + app.DebugPrintf("=== Can't find stronghold in terrain features list\n"); + } + + } +// TilePos *nearestMapFeature = level->findNearestMapFeature(LargeFeature::STRONGHOLD, (int) player->x, (int) player->y, (int) player->z); +// if (nearestMapFeature != NULL) +// { +// delete nearestMapFeature; +// return true; +// } + } + return false; +} + +shared_ptr EnderEyeItem::use(shared_ptr instance, Level *level, shared_ptr player) +{ + HitResult *hr = getPlayerPOVHitResult(level, player, false); + if (hr != NULL && hr->type == HitResult::TILE) + { + int tile = level->getTile(hr->x, hr->y, hr->z); + delete hr; + if (tile == Tile::endPortalFrameTile_Id) + { + return instance; + } + } + else if( hr != NULL ) + { + delete hr; + } + + if (!level->isClientSide) + { + if((level->dimension->id==LevelData::DIMENSION_OVERWORLD) && level->getLevelData()->getHasStronghold()) + { + shared_ptr eyeOfEnderSignal = shared_ptr( new EyeOfEnderSignal(level, player->x, player->y + 1.62 - player->heightOffset, player->z) ); + eyeOfEnderSignal->signalTo(level->getLevelData()->getXStronghold()<<4, player->y + 1.62 - player->heightOffset, level->getLevelData()->getZStronghold()<<4); + level->addEntity(eyeOfEnderSignal); + + level->playSound(player, eSoundType_RANDOM_BOW, 0.5f, 0.4f / (random->nextFloat() * 0.4f + 0.8f)); + level->levelEvent(nullptr, LevelEvent::SOUND_LAUNCH, (int) player->x, (int) player->y, (int) player->z, 0); + if (!player->abilities.instabuild) + { + instance->count--; + } + } + + /*TilePos *nearestMapFeature = level->findNearestMapFeature(LargeFeature::STRONGHOLD, (int) player->x, (int) player->y, (int) player->z); + if (nearestMapFeature != NULL) + { + shared_ptr eyeOfEnderSignal = shared_ptr( new EyeOfEnderSignal(level, player->x, player->y + 1.62 - player->heightOffset, player->z) ); + eyeOfEnderSignal->signalTo(nearestMapFeature->x, nearestMapFeature->y, nearestMapFeature->z); + delete nearestMapFeature; + level->addEntity(eyeOfEnderSignal); + + level->playSound(player, eSoundType_RANDOM_BOW, 0.5f, 0.4f / (random->nextFloat() * 0.4f + 0.8f)); + level->levelEvent(NULL, LevelEvent::SOUND_LAUNCH, (int) player->x, (int) player->y, (int) player->z, 0); + if (!player->abilities.instabuild) + { + instance->count--; + } + }*/ + } + return instance; +} \ No newline at end of file diff --git a/Minecraft.World/EnderEyeItem.h b/Minecraft.World/EnderEyeItem.h new file mode 100644 index 00000000..72036aa5 --- /dev/null +++ b/Minecraft.World/EnderEyeItem.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Item.h" + +class EnderEyeItem : public Item +{ +public: + EnderEyeItem(int id); + + virtual bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + virtual bool TestUse(Level *level, shared_ptr player); + virtual shared_ptr use(shared_ptr instance, Level *level, shared_ptr player); +}; \ No newline at end of file diff --git a/Minecraft.World/EnderMan.cpp b/Minecraft.World/EnderMan.cpp new file mode 100644 index 00000000..abfc5cb5 --- /dev/null +++ b/Minecraft.World/EnderMan.cpp @@ -0,0 +1,411 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.phys.h" +#include "com.mojang.nbt.h" +#include "..\Minecraft.Client\Textures.h" +#include "EnderMan.h" + + + +bool EnderMan::MAY_TAKE[256]; + +void EnderMan::staticCtor() +{ + ZeroMemory(MAY_TAKE, sizeof(bool) * 256); + MAY_TAKE[Tile::grass_Id] = true; + MAY_TAKE[Tile::dirt_Id] = true; + MAY_TAKE[Tile::sand_Id] = true; + MAY_TAKE[Tile::gravel_Id] = true; + MAY_TAKE[Tile::flower_Id] = true; + MAY_TAKE[Tile::rose_Id] = true; + MAY_TAKE[Tile::mushroom1_Id] = true; + MAY_TAKE[Tile::mushroom2_Id] = true; + MAY_TAKE[Tile::tnt_Id] = true; + MAY_TAKE[Tile::cactus_Id] = true; + MAY_TAKE[Tile::clay_Id] = true; + MAY_TAKE[Tile::pumpkin_Id] = true; + MAY_TAKE[Tile::melon_Id] = true; + MAY_TAKE[Tile::mycel_Id] = true; +} + +EnderMan::EnderMan(Level *level) : Monster( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + // Brought forward from 1.2.3 + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + // 4J initialisors + teleportTime = 0; + aggroTime = 0; + + this->textureIdx = TN_MOB_ENDERMAN; // 4J was "/mob/enderman.png"; + runSpeed = 0.2f; + attackDamage = 7; + setSize(0.6f, 2.9f); + footSize = 1; +} + +int EnderMan::getMaxHealth() +{ + return 40; +} + +// Brought forward from 1.2.3 +void EnderMan::defineSynchedData() +{ + Monster::defineSynchedData(); + + entityData->define(DATA_CARRY_ITEM_ID, (byte) 0); + entityData->define(DATA_CARRY_ITEM_DATA, (byte) 0); + entityData->define(DATA_CREEPY, (byte) 0); +} + +void EnderMan::addAdditonalSaveData(CompoundTag *tag) +{ + Monster::addAdditonalSaveData(tag); + tag->putShort(L"carried", (short) getCarryingTile()); + tag->putShort(L"carriedData", (short) getCarryingData()); +} + +void EnderMan::readAdditionalSaveData(CompoundTag *tag) +{ + Monster::readAdditionalSaveData(tag); + setCarryingTile(tag->getShort(L"carried")); + setCarryingData(tag->getShort(L"carryingData")); +} + +shared_ptr EnderMan::findAttackTarget() +{ +#ifndef _FINAL_BUILD + if(app.GetMobsDontAttackEnabled()) + { + return shared_ptr(); + } +#endif + + shared_ptr player = level->getNearestAttackablePlayer(shared_from_this(), 64); + if (player != NULL) + { + if (isLookingAtMe(player)) + { + if (aggroTime++ == 5) + { + aggroTime = 0; + setCreepy(true); + return player; + } + } + else + { + aggroTime = 0; + } + } + return nullptr; +} + +bool EnderMan::isLookingAtMe(shared_ptr player) +{ + shared_ptr helmet = player->inventory->armor[3]; + if (helmet != NULL && helmet->id == Tile::pumpkin_Id) return false; + + Vec3 *look = player->getViewVector(1)->normalize(); + Vec3 *dir = Vec3::newTemp(x - player->x, (bb->y0 + bbHeight / 2) - (player->y + player->getHeadHeight()), z - player->z); + double dist = dir->length(); + dir = dir->normalize(); + double dot = look->dot(dir); + if (dot > 1 - 0.025 / dist) + { + return player->canSee(shared_from_this()); + } + return false; +} + +void EnderMan::aiStep() +{ + if (this->isInWaterOrRain()) hurt(DamageSource::drown, 1); + + runSpeed = attackTarget != NULL ? 6.5f : 0.3f; + + if (!level->isClientSide) + { + if (getCarryingTile() == 0) + { + if (random->nextInt(20) == 0) + { + int xt = Mth::floor(x - 2 + random->nextDouble() * 4); + int yt = Mth::floor(y + random->nextDouble() * 3); + int zt = Mth::floor(z - 2 + random->nextDouble() * 4); + int t = level->getTile(xt, yt, zt); + //if (t > 0 && Tile::tiles[t]->isCubeShaped()) + if(EnderMan::MAY_TAKE[t]) // 4J - Brought forward from 1.2.3 + { + setCarryingTile(level->getTile(xt, yt, zt)); + setCarryingData(level->getData(xt, yt, zt)); + level->setTile(xt, yt, zt, 0); + } + } + } + else + { + if (random->nextInt(2000) == 0) + { + int xt = Mth::floor(x - 1 + random->nextDouble() * 2); + int yt = Mth::floor(y + random->nextDouble() * 2); + int zt = Mth::floor(z - 1 + random->nextDouble() * 2); + int t = level->getTile(xt, yt, zt); + int bt = level->getTile(xt, yt - 1, zt); + if (t == 0 && bt > 0 && Tile::tiles[bt]->isCubeShaped()) + { + level->setTileAndData(xt, yt, zt, getCarryingTile(), getCarryingData()); + setCarryingTile(0); + } + } + } + } + + // 4J - Brought forward particles from 1.2.3 + for (int i = 0; i < 2; i++) + { + level->addParticle(eParticleType_ender, x + (random->nextDouble() - 0.5) * bbWidth, y + random->nextDouble() * bbHeight - 0.25f, z + (random->nextDouble() - 0.5) * bbWidth, + (random->nextDouble() - 0.5) * 2, -random->nextDouble(), (random->nextDouble() - 0.5) * 2); + } + + if (level->isDay() && !level->isClientSide) + { + float br = getBrightness(1); + if (br > 0.5f) + { + if (level->canSeeSky(Mth::floor(x), Mth::floor(y), Mth::floor(z)) && random->nextFloat() * 30 < (br - 0.4f) * 2) + { + // 4J - Brought forward behaviour change from 1.2.3 + //onFire = 20 * 15; + attackTarget = nullptr; + setCreepy(false); + teleport(); + } + } + } + // 4J Brought forward behaviour change from 1.2.3 + if (isInWaterOrRain() || isOnFire()) + { + attackTarget = nullptr; + setCreepy(false); + teleport(); + } + jumping = false; + if (attackTarget != NULL) + { + this->lookAt(attackTarget, 100, 100); + } + + if (!level->isClientSide && isAlive()) + { + if (attackTarget != NULL) + { + if ( dynamic_pointer_cast(attackTarget) != NULL && isLookingAtMe(dynamic_pointer_cast(attackTarget))) + { + xxa = yya = 0; + runSpeed = 0; + if (attackTarget->distanceToSqr(shared_from_this()) < 4 * 4) + { + teleport(); + } + teleportTime = 0; + } + else if (attackTarget->distanceToSqr(shared_from_this()) > 16 * 16) + { + if (teleportTime++ >= 30) + { + if (teleportTowards(attackTarget)) + { + teleportTime = 0; + } + } + } + } + else + { + setCreepy(false); + teleportTime = 0; + } + } + + Monster::aiStep(); +} + +bool EnderMan::teleport() +{ + double xx = x + (random->nextDouble() - 0.5) * 64; + double yy = y + (random->nextInt(64) - 32); + double zz = z + (random->nextDouble() - 0.5) * 64; + return teleport(xx, yy, zz); +} + +bool EnderMan::teleportTowards(shared_ptr e) +{ + Vec3 *dir = Vec3::newTemp(x - e->x, bb->y0 + bbHeight / 2 - e->y + e->getHeadHeight(), z - e->z); + dir = dir->normalize(); + double d = 16; + double xx = x + (random->nextDouble() - 0.5) * 8 - dir->x * d; + double yy = y + (random->nextInt(16) - 8) - dir->y * d; + double zz = z + (random->nextDouble() - 0.5) * 8 - dir->z * d; + return teleport(xx, yy, zz); +} + +bool EnderMan::teleport(double xx, double yy, double zz) +{ + double xo = x; + double yo = y; + double zo = z; + + x = xx; + y = yy; + z = zz; + bool ok = false; + int xt = Mth::floor(x); + int yt = Mth::floor(y); + int zt = Mth::floor(z); + + if (level->hasChunkAt(xt, yt, zt)) + { + bool landed = false; + while (!landed && yt > 0) + { + int t = level->getTile(xt, yt - 1, zt); + if (t == 0 || !(Tile::tiles[t]->material->blocksMotion())) + { + y--; + yt--; + } + else + { + landed = true; + } + } + if (landed) + { + setPos(x, y, z); + if (level->getCubes(shared_from_this(), bb)->empty() && !level->containsAnyLiquid(bb)) + { + ok = true; + } + } + } + + + if (ok) + { + int count = 128; + for (int i = 0; i < count; i++) + { + double d = i / (count - 1.0); + float xa = (random->nextFloat() - 0.5f) * 0.2f; + float ya = (random->nextFloat() - 0.5f) * 0.2f; + float za = (random->nextFloat() - 0.5f) * 0.2f; + + double _x = xo + (x - xo) * d + (random->nextDouble() - 0.5) * bbWidth * 2; + double _y = yo + (y - yo) * d + random->nextDouble() * bbHeight; + double _z = zo + (z - zo) * d + (random->nextDouble() - 0.5) * bbWidth * 2; + + // 4J - Brought forward particle change from 1.2.3 + //level->addParticle(eParticleType_largesmoke, _x, _y, _z, xa, ya, za); + level->addParticle(eParticleType_ender, _x, _y, _z, xa, ya, za); + } + // 4J - moved sounds forward from 1.2.3 + level->playSound(xo, yo, zo, eSoundType_MOB_ENDERMEN_PORTAL, 1, 1); + level->playSound(shared_from_this(), eSoundType_MOB_ENDERMEN_PORTAL, 1, 1); + return true; + } + else + { + setPos(xo, yo, zo); + return false; + } +} + +int EnderMan::getAmbientSound() +{ + // 4J - brought sound change forward from 1.2.3 + return eSoundType_MOB_ENDERMEN_IDLE; +} + +int EnderMan::getHurtSound() +{ + // 4J - brought sound change forward from 1.2.3 + return eSoundType_MOB_ENDERMEN_HIT; +} + +int EnderMan::getDeathSound() +{ + // 4J - brought sound change forward from 1.2.3 + return eSoundType_MOB_ENDERMEN_DEATH; +} + +int EnderMan::getDeathLoot() +{ + return Item::enderPearl_Id; +} + +void EnderMan::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + int loot = getDeathLoot(); + if (loot > 0) + { + int count = random->nextInt(2 + playerBonusLevel); + for (int i = 0; i < count; i++) + spawnAtLocation(loot, 1); + } +} + +// 4J Brought forward from 1.2.3 to help fix Enderman behaviour +void EnderMan::setCarryingTile(int carryingTile) +{ + entityData->set(DATA_CARRY_ITEM_ID, (byte) (carryingTile & 0xff)); +} + +int EnderMan::getCarryingTile() +{ + return entityData->getByte(DATA_CARRY_ITEM_ID); +} + +void EnderMan::setCarryingData(int carryingData) +{ + entityData->set(DATA_CARRY_ITEM_DATA, (byte) (carryingData & 0xff)); +} + +int EnderMan::getCarryingData() +{ + return entityData->getByte(DATA_CARRY_ITEM_DATA); +} + +bool EnderMan::hurt(DamageSource *source, int damage) +{ + if (dynamic_cast(source) != NULL) + { + for (int i = 0; i < 64; i++) + { + if (teleport()) return true; + } + return false; + } + return Monster::hurt(source, damage); +} + +bool EnderMan::isCreepy() +{ + return entityData->getByte(DATA_CREEPY) > 0; +} + +void EnderMan::setCreepy(bool creepy) +{ + entityData->set(DATA_CREEPY, (byte)(creepy ? 1 : 0)); +} \ No newline at end of file diff --git a/Minecraft.World/EnderMan.h b/Minecraft.World/EnderMan.h new file mode 100644 index 00000000..f5084532 --- /dev/null +++ b/Minecraft.World/EnderMan.h @@ -0,0 +1,64 @@ +#pragma once + +#include "Monster.h" + +class EnderMan : public Monster +{ +public: + eINSTANCEOF GetType() { return eTYPE_ENDERMAN; } + static Entity *create(Level *level) { return new EnderMan(level); } +public: + static void staticCtor(); +private: + static bool MAY_TAKE[256]; + + static const int DATA_CARRY_ITEM_ID = 16; + static const int DATA_CARRY_ITEM_DATA = 17; + static const int DATA_CREEPY = 18; + +private: + int teleportTime; + int aggroTime; + +public: + EnderMan(Level *level); + + virtual int getMaxHealth(); + +protected: + virtual void defineSynchedData(); + +public: + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +protected: + virtual shared_ptr findAttackTarget(); + +private: + bool isLookingAtMe(shared_ptr player); + +public: + virtual void aiStep(); + +protected: + bool teleport(); + bool teleportTowards(shared_ptr e); + bool teleport(double xx, double yy, double zz); + + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual int getDeathLoot(); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + +public: + // 4J Brought forward from 1.2.3 to help fix Enderman behaviour + void setCarryingTile(int carryingTile); + int getCarryingTile(); + void setCarryingData(int carryingData); + int getCarryingData(); + virtual bool hurt(DamageSource *source, int damage); + bool isCreepy(); + void setCreepy(bool creepy); +}; \ No newline at end of file diff --git a/Minecraft.World/EnderpearlItem.cpp b/Minecraft.World/EnderpearlItem.cpp new file mode 100644 index 00000000..7a9b6e52 --- /dev/null +++ b/Minecraft.World/EnderpearlItem.cpp @@ -0,0 +1,34 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.projectile.h" +#include "EnderpearlItem.h" +#include "SoundTypes.h" + +EnderpearlItem::EnderpearlItem(int id) : Item(id) +{ + this->maxStackSize = 16; +} + +bool EnderpearlItem::TestUse(Level *level, shared_ptr player) +{ + return true; +} + +shared_ptr EnderpearlItem::use(shared_ptr instance, Level *level, shared_ptr player) +{ + // 4J-PB - Not sure why this was disabled for creative mode, so commenting out + //if (player->abilities.instabuild) return instance; + if (player->riding != NULL) return instance; + if (!player->abilities.instabuild) + { + instance->count--; + } + + level->playSound(player, eSoundType_RANDOM_BOW, 0.5f, 0.4f / (random->nextFloat() * 0.4f + 0.8f)); + if (!level->isClientSide) + { + level->addEntity( shared_ptr( new ThrownEnderpearl(level, player) ) ); + } + return instance; +} \ No newline at end of file diff --git a/Minecraft.World/EnderpearlItem.h b/Minecraft.World/EnderpearlItem.h new file mode 100644 index 00000000..84913b9e --- /dev/null +++ b/Minecraft.World/EnderpearlItem.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Item.h" + +class EnderpearlItem : public Item +{ +public: + EnderpearlItem(int id); + + virtual shared_ptr use(shared_ptr instance, Level *level, shared_ptr player); + // 4J added + virtual bool TestUse(Level *level, shared_ptr player); +}; \ No newline at end of file diff --git a/Minecraft.World/Enemy.cpp b/Minecraft.World/Enemy.cpp new file mode 100644 index 00000000..25d1ad47 --- /dev/null +++ b/Minecraft.World/Enemy.cpp @@ -0,0 +1,10 @@ +#include "stdafx.h" +#include "Enemy.h" + + + +const int Enemy::XP_REWARD_NONE = 0; +const int Enemy::XP_REWARD_SMALL = 3; +const int Enemy::XP_REWARD_MEDIUM = 5; +const int Enemy::XP_REWARD_LARGE = 10; +const int Enemy::XP_REWARD_HUGE = 20; \ No newline at end of file diff --git a/Minecraft.World/Enemy.h b/Minecraft.World/Enemy.h new file mode 100644 index 00000000..cb7d3a3e --- /dev/null +++ b/Minecraft.World/Enemy.h @@ -0,0 +1,14 @@ +#pragma once +#include "Creature.h" + +class Level; + +class Enemy : public Creature +{ +public: + static const int XP_REWARD_NONE; + static const int XP_REWARD_SMALL; + static const int XP_REWARD_MEDIUM; + static const int XP_REWARD_LARGE; + static const int XP_REWARD_HUGE; +}; diff --git a/Minecraft.World/Entity.cpp b/Minecraft.World/Entity.cpp new file mode 100644 index 00000000..4c9d5cf6 --- /dev/null +++ b/Minecraft.World/Entity.cpp @@ -0,0 +1,1956 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.damagesource.h" +#include "SynchedEntityData.h" +#include "EntityIO.h" +#include "SharedConstants.h" + +#include "ParticleTypes.h" + +#include "EntityPos.h" +#include "Entity.h" +#include "SoundTypes.h" +#include "..\minecraft.Client\HumanoidModel.h" +#include "..\Minecraft.Client\MinecraftServer.h" +#include "..\Minecraft.Client\MultiPlayerLevel.h" +#include "..\Minecraft.Client\MultiplayerLocalPlayer.h" + + +int Entity::entityCounter = 2048; // 4J - changed initialiser to 2048, as we are using range 0 - 2047 as special unique smaller ids for things that need network tracked +DWORD Entity::tlsIdx = TlsAlloc(); + +// 4J - added getSmallId & freeSmallId methods +unsigned int Entity::entityIdUsedFlags[2048/32] = {0}; +unsigned int Entity::entityIdWanderFlags[2048/32] = {0}; +unsigned int Entity::entityIdRemovingFlags[2048/32] = {0}; +int Entity::extraWanderIds[EXTRA_WANDER_MAX] = {0}; +int Entity::extraWanderTicks = 0; +int Entity::extraWanderCount = 0; + +int Entity::getSmallId() +{ + unsigned int *puiUsedFlags = entityIdUsedFlags; + unsigned int *puiRemovedFlags = NULL; + + // If we are the server (we should be, if we are allocating small Ids), then check with the server if there are any small Ids which are + // still in the ServerPlayer's vectors of entities to be removed - these are used to gather up a set of entities into one network packet + // for final notification to the client that the entities are removed. We can't go re-using these small Ids yet, as otherwise we will + // potentially end up telling the client that the entity has been removed After we have already re-used its Id and created a new entity. + // This ends up with newly created client-side entities being removed by accident, causing invisible mobs. + if( ((size_t)TlsGetValue(tlsIdx) != 0 ) ) + { + MinecraftServer *server = MinecraftServer::getInstance(); + if( server ) + { + // In some attempt to optimise this, flagEntitiesToBeRemoved most of the time shouldn't do anything at all, and in this case it + // doesn't even memset the entityIdRemovingFlags array, so we shouldn't use it if the return value is false. + bool removedFound = server->flagEntitiesToBeRemoved(entityIdRemovingFlags); + if( removedFound ) + { + // Has set up the entityIdRemovingFlags vector in this case, so we should check against this when allocating new ids +// app.DebugPrintf("getSmallId: Removed entities found\n"); + puiRemovedFlags = entityIdRemovingFlags; + } + } + } + + for( int i = 0; i < (2048 / 32 ); i++ ) + { + unsigned int uiFlags = *puiUsedFlags; + if( uiFlags != 0xffffffff ) + { + unsigned int uiMask = 0x80000000; + for( int j = 0; j < 32; j++ ) + { + // See comments above - now checking (if required) that these aren't newly removed entities that the clients still haven't been told about, + // so we don't reuse those ids before we should. + if( puiRemovedFlags ) + { + if( puiRemovedFlags[i] & uiMask ) + { +// app.DebugPrintf("Avoiding using ID %d (0x%x)\n", i * 32 + j,puiRemovedFlags[i]); + uiMask >>= 1; + continue; + } + } + if( ( uiFlags & uiMask ) == 0 ) + { + uiFlags |= uiMask; + *puiUsedFlags = uiFlags; + return i * 32 + j; + } + uiMask >>= 1; + } + } + puiUsedFlags++; + } + + app.DebugPrintf("Out of small entity Ids... possible leak?\n"); + __debugbreak(); + return -1; +} + +void Entity::countFlagsForPIX() +{ + int freecount = 0; + unsigned int *puiUsedFlags = entityIdUsedFlags; + for( int i = 0; i < (2048 / 32 ); i++ ) + { + unsigned int uiFlags = *puiUsedFlags; + if( uiFlags != 0xffffffff ) + { + unsigned int uiMask = 0x80000000; + for( int j = 0; j < 32; j++ ) + { + if( ( uiFlags & uiMask ) == 0 ) + { + freecount++; + } + uiMask >>= 1; + } + } + puiUsedFlags++; + } + PIXAddNamedCounter(freecount,"Small Ids free"); + PIXAddNamedCounter(2048 - freecount,"Small Ids used"); +} + +void Entity::resetSmallId() +{ + freeSmallId(entityId); + if( ((size_t)TlsGetValue(tlsIdx) != 0 ) ) + { + entityId = getSmallId(); + } +} + +void Entity::freeSmallId(int index) +{ + if( ( (size_t)TlsGetValue(tlsIdx) ) == 0 ) return; // Don't do anything with small ids if this isn't the server thread + if( index >= 2048 ) return; // Don't do anything if this isn't a short id + + unsigned int i = index / 32; + unsigned int j = index % 32; + unsigned int uiMask = ~(0x80000000 >> j); + + entityIdUsedFlags[i] &= uiMask; + entityIdWanderFlags[i] &= uiMask; +} + +void Entity::useSmallIds() +{ + TlsSetValue(tlsIdx,(LPVOID)1); +} + +// Things also added here to be able to manage the concept of a number of extra "wandering" entities - normally path finding entities aren't allowed to +// randomly wander about once they are a certain distance away from any player, but we want to be able to (in a controlled fashion) allow some to be able +// to move so that we can determine whether they have been enclosed in some kind of farm, and so be able to better determine what shouldn't or shouldn't be despawned. + +// Let the management system here know whether or not to consider this particular entity for some extra wandering +void Entity::considerForExtraWandering(bool enable) +{ + if( ( (size_t)TlsGetValue(tlsIdx) ) == 0 ) return; // Don't do anything with small ids if this isn't the server thread + if( entityId >= 2048 ) return; // Don't do anything if this isn't a short id + + unsigned int i = entityId / 32; + unsigned int j = entityId % 32; + if( enable ) + { + unsigned int uiMask = 0x80000000 >> j; + entityIdWanderFlags[i] |= uiMask; + } + else + { + unsigned int uiMask = ~(0x80000000 >> j); + entityIdWanderFlags[i] &= uiMask; + } +} + +// Should this entity do wandering in addition to what the java code would have done? +bool Entity::isExtraWanderingEnabled() +{ + if( ( (size_t)TlsGetValue(tlsIdx) ) == 0 ) return false; // Don't do anything with small ids if this isn't the server thread + if( entityId >= 2048 ) return false; // Don't do anything if this isn't a short id + + for( int i = 0; i < extraWanderCount; i++ ) + { + if( extraWanderIds[i] == entityId ) return true; + } + return false; +} + + +// Returns a quadrant of direction that a given entity should be moved in - this is to stop the randomness of the wandering/strolling from just making the entity double back +// on itself and thus making the determination of whether the entity has been enclosed take longer than it needs to. This function returns a quadrant from 0 to 3 +// that should be consistent within one period of an entity being considered for extra wandering, but should potentially vary between tries and between different entities. +int Entity::getWanderingQuadrant() +{ + return ( entityId + ( extraWanderTicks / EXTRA_WANDER_TICKS ) ) & 3; +} + +// Every EXTRA_WANDER_TICKS ticks, attempt to find EXTRA_WANDER_MAX entity Ids from those that have been flagged as ones that should be considered for +// extra wandering +void Entity::tickExtraWandering() +{ + extraWanderTicks++; + // Time to move onto some new entities? + + if( ( extraWanderTicks % EXTRA_WANDER_TICKS == 0 ) ) + { + // printf("Updating extras: "); + // Start from the next Id after the one that we last found, or zero if we didn't find anything last time + int entityId = 0; + if( extraWanderCount ) + { + entityId = ( extraWanderIds[ extraWanderCount - 1 ] + 1 ) % 2048; + } + + extraWanderCount = 0; + + for( int k = 0; ( k < 2048 ) && ( extraWanderCount < EXTRA_WANDER_MAX); k++ ) + { + unsigned int i = entityId / 32; + unsigned int j = entityId % 32; + unsigned int uiMask = 0x80000000 >> j; + + if( entityIdWanderFlags[i] & uiMask ) + { + extraWanderIds[ extraWanderCount++ ] = entityId; + // printf("%d, ", entityId); + } + + entityId = ( entityId + 1 ) % 2048; + } + // printf("\n"); + } +} + +// 4J - added for common ctor code +// Do all the default initialisations done in the java class +void Entity::_init(bool useSmallId) +{ + // 4J - changed to assign two different types of ids. A range from 0-2047 is used for things that we'll be wanting to identify over the network, + // so we should only need 11 bits rather than 32 to uniquely identify them. The rest of the range is used for anything we don't need to track like this, + // currently particles. We only ever want to allocate this type of id from the server thread, so using thread local storage to isolate this. + if( useSmallId && ((size_t)TlsGetValue(tlsIdx) != 0 ) ) + { + entityId = getSmallId(); + } + else + { + entityId = Entity::entityCounter++; + if(entityCounter == 0x7ffffff ) entityCounter = 2048; + } + + viewScale = 1.0; + + blocksBuilding = false; + rider = weak_ptr(); + riding = nullptr; + + //level = NULL; // Level is assigned to in the original c_tor code + xo = yo = zo = 0.0; + x = y = z = 0.0; + xd = yd = zd = 0.0; + yRot = xRot = 0.0f; + yRotO = xRotO = 0.0f; + bb = AABB::newPermanent(0, 0, 0, 0, 0, 0); // 4J Was final + onGround = false; + horizontalCollision = verticalCollision = false; + collision = false; + hurtMarked = false; + isStuckInWeb = false; + + slide = true; + removed = false; + heightOffset = 0 / 16.0f; + + bbWidth = 0.6f; + bbHeight = 1.8f; + + walkDistO = 0; + walkDist = 0; + + fallDistance = 0; + + nextStep = 1; + + xOld = yOld = zOld = 0.0; + ySlideOffset = 0; + footSize = 0.0f; + noPhysics = false; + pushthrough = 0.0f; + + random = new Random(); + + tickCount = 0; + flameTime = 1; + + onFire = 0; + wasInWater = false; + + + invulnerableTime = 0; + + firstTick = true; + + + customTextureUrl = L""; + customTextureUrl2 = L""; + + + fireImmune = false; + + // values that need to be sent to clients in SMP + entityData = shared_ptr(new SynchedEntityData()); + + xRideRotA = yRideRotA = 0.0; + inChunk = false; + xChunk = yChunk = zChunk = 0; + xp = yp = zp = 0; + xRotp = yRotp = 0; + noCulling = false; + + hasImpulse = false; + + // 4J Added + m_ignoreVerticalCollisions = false; + m_uiAnimOverrideBitmask = 0L; +} + +Entity::Entity(Level *level, bool useSmallId) // 4J - added useSmallId parameter +{ + MemSect(16); + _init(useSmallId); + MemSect(0); + + this->level = level; + // resetPos(); + setPos(0, 0, 0); + + entityData->define(DATA_SHARED_FLAGS_ID, (byte) 0); + entityData->define(DATA_AIR_SUPPLY_ID, TOTAL_AIR_SUPPLY); // 4J Stu - Brought forward from 1.2.3 to fix 38654 - Gameplay: Player will take damage when air bubbles are present if resuming game from load/autosave underwater. + + // 4J Stu - We cannot call virtual functions in ctors, as at this point the object + // is of type Entity and not a derived class + //this->defineSynchedData(); +} + +Entity::~Entity() +{ + freeSmallId(entityId); + delete random; + delete bb; +} + +shared_ptr Entity::getEntityData() +{ + return entityData; +} + +/* +public bool equals(Object obj) { +if (obj instanceof Entity) { +return ((Entity) obj).entityId == entityId; +} +return false; +} + +public int hashCode() { +return entityId; +} +*/ + +void Entity::resetPos() +{ + if (level == NULL) return; + + shared_ptr sharedThis = shared_from_this(); + while (true && y > 0) + { + setPos(x, y, z); + if (level->getCubes(sharedThis, bb)->empty()) break; + y += 1; + } + + xd = yd = zd = 0; + xRot = 0; +} + +void Entity::remove() +{ + removed = true; +} + + +void Entity::setSize(float w, float h) +{ + if (w != bbWidth || h != bbHeight) + { + float oldW = bbWidth; + + bbWidth = w; + bbHeight = h; + + bb->x1 = bb->x0 + bbWidth; + bb->z1 = bb->z0 + bbWidth; + bb->y1 = bb->y0 + bbHeight; + + if (bbWidth > oldW && !firstTick && !level->isClientSide) + { + move(oldW - bbWidth, 0, oldW - bbWidth); + } + } +} + +void Entity::setPos(EntityPos *pos) +{ + if (pos->move) setPos(pos->x, pos->y, pos->z); + else setPos(x, y, z); + + if (pos->rot) setRot(pos->yRot, pos->xRot); + else setRot(yRot, xRot); +} + +void Entity::setRot(float yRot, float xRot) +{ + /* JAVA: + this->yRot = yRot % 360.0f; + this->xRot = xRot % 360.0f; + + C++ Cannot do mod of non-integral type + */ + + while( yRot >= 360.0f ) + yRot -= 360.0f; + while( yRot < 0 ) + yRot += 360.0f; + while( xRot >= 360.0f ) + xRot -= 360.0f; + + this->yRot = yRot; + this->xRot = xRot; +} + + +void Entity::setPos(double x, double y, double z) +{ + this->x = x; + this->y = y; + this->z = z; + float w = bbWidth / 2; + float h = bbHeight; + bb->set(x - w, y - heightOffset + ySlideOffset, z - w, x + w, y - heightOffset + ySlideOffset + h, z + w); +} + +void Entity::turn(float xo, float yo) +{ + float xRotOld = xRot; + float yRotOld = yRot; + + yRot += xo * 0.15f; + xRot -= yo * 0.15f; + if (xRot < -90) xRot = -90; + if (xRot > 90) xRot = 90; + + xRotO += xRot - xRotOld; + yRotO += yRot - yRotOld; +} + +void Entity::interpolateTurn(float xo, float yo) +{ + yRot += xo * 0.15f; + xRot -= yo * 0.15f; + if (xRot < -90) xRot = -90; + if (xRot > 90) xRot = 90; +} + +void Entity::tick() +{ + baseTick(); +} + +void Entity::baseTick() +{ + // 4J Stu - Not needed + //util.Timer.push("entityBaseTick"); + + if (riding != NULL && riding->removed) riding = nullptr; + + tickCount++; + walkDistO = walkDist; + xo = x; + yo = y; + zo = z; + xRotO = xRot; + yRotO = yRot; + + if (isSprinting() && !isInWater() && canCreateParticles()) + { + int xt = Mth::floor(x); + int yt = Mth::floor(y - 0.2f - this->heightOffset); + int zt = Mth::floor(z); + int t = level->getTile(xt, yt, zt); + int d = level->getData(xt, yt, zt); + if (t > 0) + { + level->addParticle(PARTICLE_TILECRACK(t,d), x + (random->nextFloat() - 0.5) * bbWidth, bb->y0 + 0.1, z + (random->nextFloat() - 0.5) * bbWidth, -xd * 4, 1.5, -zd * 4); + } + } + + if (updateInWaterState()) + { + if (!wasInWater && !firstTick && canCreateParticles()) + { + float speed = Mth::sqrt(xd * xd * 0.2f + yd * yd + zd * zd * 0.2f) * 0.2f; + if (speed > 1) speed = 1; + MemSect(31); + level->playSound(shared_from_this(), eSoundType_RANDOM_SPLASH, speed, 1 + (random->nextFloat() - random->nextFloat()) * 0.4f); + MemSect(0); + float yt = (float) Mth::floor(bb->y0); + for (int i = 0; i < 1 + bbWidth * 20; i++) + { + float xo = (random->nextFloat() * 2 - 1) * bbWidth; + float zo = (random->nextFloat() * 2 - 1) * bbWidth; + level->addParticle(eParticleType_bubble, x + xo, yt + 1, z + zo, xd, yd - random->nextFloat() * 0.2f, zd); + } + for (int i = 0; i < 1 + bbWidth * 20; i++) + { + float xo = (random->nextFloat() * 2 - 1) * bbWidth; + float zo = (random->nextFloat() * 2 - 1) * bbWidth; + level->addParticle(eParticleType_splash, x + xo, yt + 1, z + zo, xd, yd, zd); + } + } + fallDistance = 0; + wasInWater = true; + onFire = 0; + } + else + { + wasInWater = false; + } + + if (level->isClientSide) + { + onFire = 0; + } + else + { + if (onFire > 0) + { + if (fireImmune) + { + onFire -= 4; + if (onFire < 0) onFire = 0; + } + else + { + if (onFire % 20 == 0) + { + hurt(DamageSource::onFire, 1); + } + onFire--; + } + } + } + + if (isInLava()) + { + lavaHurt(); + fallDistance *= .5f; + } + + if (y < -64) + { + outOfWorld(); + } + + if (!level->isClientSide) + { + setSharedFlag(FLAG_ONFIRE, onFire > 0); + setSharedFlag(FLAG_RIDING, riding != NULL); + } + + firstTick = false; + + // 4J Stu - Unused + //util.Timer.pop(); +} + + +void Entity::lavaHurt() +{ + if (fireImmune) + { + } + else + { + hurt(DamageSource::lava, 4); + setOnFire(15); + } +} + +void Entity::setOnFire(int numberOfSeconds) +{ + int newValue = numberOfSeconds * SharedConstants::TICKS_PER_SECOND; + newValue = ProtectionEnchantment::getFireAfterDampener(shared_from_this(), newValue); + if (onFire < newValue) + { + onFire = newValue; + } +} + +void Entity::clearFire() +{ + onFire = 0; +} + +void Entity::outOfWorld() +{ + remove(); +} + + +bool Entity::isFree(float xa, float ya, float za, float grow) +{ + AABB *box = bb->grow(grow, grow, grow)->cloneMove(xa, ya, za); + AABBList *aABBs = level->getCubes(shared_from_this(), box); + if (!aABBs->empty()) return false; + if (level->containsAnyLiquid(box)) return false; + return true; +} + +bool Entity::isFree(double xa, double ya, double za) +{ + AABB *box = bb->cloneMove(xa, ya, za); + AABBList *aABBs = level->getCubes(shared_from_this(), box); + if (!aABBs->empty()) return false; + if (level->containsAnyLiquid(box)) return false; + return true; +} + +void Entity::move(double xa, double ya, double za, bool noEntityCubes) // 4J - added noEntityCubes parameter +{ + if (noPhysics) + { + bb->move(xa, ya, za); + x = (bb->x0 + bb->x1) / 2.0f; + y = bb->y0 + heightOffset - ySlideOffset; + z = (bb->z0 + bb->z1) / 2.0f; + return; + } + + ySlideOffset *= 0.4f; + + double xo = x; + double zo = z; + + if (isStuckInWeb) + { + isStuckInWeb = false; + + xa *= 0.25f; + ya *= 0.05f; + za *= 0.25f; + xd = 0.0f; + yd = 0.0f; + zd = 0.0f; + } + + double xaOrg = xa; + double yaOrg = ya; + double zaOrg = za; + + AABB *bbOrg = bb->copy(); + + bool isPlayerSneaking = onGround && isSneaking() && dynamic_pointer_cast(shared_from_this()) != NULL; + + if (isPlayerSneaking) + { + double d = 0.05; + while (xa != 0 && level->getCubes(shared_from_this(), bb->cloneMove(xa, -1.0, 0))->empty()) + { + if (xa < d && xa >= -d) xa = 0; + else if (xa > 0) xa -= d; + else xa += d; + xaOrg = xa; + } + while (za != 0 && level->getCubes(shared_from_this(), bb->cloneMove(0, -1.0, za))->empty()) + { + if (za < d && za >= -d) za = 0; + else if (za > 0) za -= d; + else za += d; + zaOrg = za; + } + while (xa != 0 && za != 0 && level->getCubes(shared_from_this(), bb->cloneMove(xa, -1.0, za))->empty()) + { + if (xa < d && xa >= -d) xa = 0; + else if (xa > 0) xa -= d; + else xa += d; + if (za < d && za >= -d) za = 0; + else if (za > 0) za -= d; + else za += d; + xaOrg = xa; + zaOrg = za; + } + } + + AABBList *aABBs = level->getCubes(shared_from_this(), bb->expand(xa, ya, za), noEntityCubes, true); + + + // LAND FIRST, then x and z + AUTO_VAR(itEndAABB, aABBs->end()); + + // 4J Stu - Particles (and possibly other entities) don't have xChunk and zChunk set, so calculate the chunk instead + int xc = Mth::floor(x / 16); + int zc = Mth::floor(z / 16); + if(!level->isClientSide || level->reallyHasChunk(xc, zc)) + { + // 4J Stu - It's horrible that the client is doing any movement at all! But if we don't have the chunk + // data then all the collision info will be incorrect as well + for (AUTO_VAR(it, aABBs->begin()); it != itEndAABB; it++) + ya = (*it)->clipYCollide(bb, ya); + bb->move(0, ya, 0); + } + + if (!slide && yaOrg != ya) + { + xa = ya = za = 0; + } + + bool og = onGround || (yaOrg != ya && yaOrg < 0); + + itEndAABB = aABBs->end(); + for (AUTO_VAR(it, aABBs->begin()); it != itEndAABB; it++) + xa = (*it)->clipXCollide(bb, xa); + + bb->move(xa, 0, 0); + + if (!slide && xaOrg != xa) + { + xa = ya = za = 0; + } + + itEndAABB = aABBs->end(); + for (AUTO_VAR(it, aABBs->begin()); it != itEndAABB; it++) + za = (*it)->clipZCollide(bb, za); + bb->move(0, 0, za); + + if (!slide && zaOrg != za) + { + xa = ya = za = 0; + } + + if (footSize > 0 && og && (isPlayerSneaking || ySlideOffset < 0.05f) && ((xaOrg != xa) || (zaOrg != za))) + { + double xaN = xa; + double yaN = ya; + double zaN = za; + + xa = xaOrg; + ya = footSize; + za = zaOrg; + + AABB *normal = bb->copy(); + bb->set(bbOrg); + // 4J - added extra expand, as if we don't move up by footSize by hitting a block above us, then overall we could be trying to move as much as footSize downwards, + // so we'd better include cubes under our feet in this list of things we might possibly collide with + aABBs = level->getCubes(shared_from_this(), bb->expand(xa, ya, za)->expand(0,-ya,0),false,true); + + // LAND FIRST, then x and z + itEndAABB = aABBs->end(); + + if(!level->isClientSide || level->reallyHasChunk(xc, zc)) + { + // 4J Stu - It's horrible that the client is doing any movement at all! But if we don't have the chunk + // data then all the collision info will be incorrect as well + for (AUTO_VAR(it, aABBs->begin()); it != itEndAABB; it++) + ya = (*it)->clipYCollide(bb, ya); + bb->move(0, ya, 0); + } + + if (!slide && yaOrg != ya) + { + xa = ya = za = 0; + } + + + itEndAABB = aABBs->end(); + for (AUTO_VAR(it, aABBs->begin()); it != itEndAABB; it++) + xa = (*it)->clipXCollide(bb, xa); + bb->move(xa, 0, 0); + + if (!slide && xaOrg != xa) + { + xa = ya = za = 0; + } + + itEndAABB = aABBs->end(); + for (AUTO_VAR(it, aABBs->begin()); it != itEndAABB; it++) + za = (*it)->clipZCollide(bb, za); + bb->move(0, 0, za); + + if (!slide && zaOrg != za) + { + xa = ya = za = 0; + } + + + if (!slide && yaOrg != ya) + { + xa = ya = za = 0; + } + else + { + ya = -footSize; + // LAND FIRST, then x and z + itEndAABB = aABBs->end(); + for (AUTO_VAR(it, aABBs->begin()); it != itEndAABB; it++) + ya = (*it)->clipYCollide(bb, ya); + bb->move(0, ya, 0); + } + + if (xaN * xaN + zaN * zaN >= xa * xa + za * za) + { + xa = xaN; + ya = yaN; + za = zaN; + bb->set(normal); + } + else + { + double ss = bb->y0 - (int) bb->y0; + if (ss > 0) { + ySlideOffset += (float) (ss + 0.01); + } + } + } + + + x = (bb->x0 + bb->x1) / 2.0f; + y = bb->y0 + heightOffset - ySlideOffset; + z = (bb->z0 + bb->z1) / 2.0f; + + horizontalCollision = (xaOrg != xa) || (zaOrg != za); + verticalCollision = !m_ignoreVerticalCollisions && (yaOrg != ya); + onGround = !m_ignoreVerticalCollisions && yaOrg != ya && yaOrg < 0; + collision = horizontalCollision || verticalCollision; + checkFallDamage(ya, onGround); + + if (xaOrg != xa) xd = 0; + if (yaOrg != ya) yd = 0; + if (zaOrg != za) zd = 0; + + double xm = x - xo; + double zm = z - zo; + + + if (makeStepSound() && !isPlayerSneaking && riding == NULL) + { + walkDist += (float) ( sqrt(xm * xm + zm * zm) * 0.6 ); + int xt = Mth::floor(x); + int yt = Mth::floor(y - 0.2f - this->heightOffset); + int zt = Mth::floor(z); + int t = level->getTile(xt, yt, zt); + if (t == 0) + { + int renderShape = level->getTileRenderShape(xt, yt - 1, zt); + if (renderShape == Tile::SHAPE_FENCE || renderShape == Tile::SHAPE_WALL || renderShape == Tile::SHAPE_FENCE_GATE) + { + t = level->getTile(xt, yt - 1, zt); + } + } + + if (walkDist > nextStep && t > 0) + { + nextStep = (int) walkDist + 1; + playStepSound(xt, yt, zt, t); + Tile::tiles[t]->stepOn(level, xt, yt, zt, shared_from_this()); + } + } + + checkInsideTiles(); + + + bool water = this->isInWaterOrRain(); + if (level->containsFireTile(bb->shrink(0.001, 0.001, 0.001))) + { + burn(1); + if (!water) + { + onFire++; + if (onFire == 0) setOnFire(8); + } + } + else + { + if (onFire <= 0) + { + onFire = -flameTime; + } + } + + if (water && onFire > 0) + { + level->playSound(shared_from_this(), eSoundType_RANDOM_FIZZ, 0.7f, 1.6f + (random->nextFloat() - random->nextFloat()) * 0.4f); + onFire = -flameTime; + } +} + +void Entity::checkInsideTiles() +{ + int x0 = Mth::floor(bb->x0 + 0.001); + int y0 = Mth::floor(bb->y0 + 0.001); + int z0 = Mth::floor(bb->z0 + 0.001); + int x1 = Mth::floor(bb->x1 - 0.001); + int y1 = Mth::floor(bb->y1 - 0.001); + int z1 = Mth::floor(bb->z1 - 0.001); + + if (level->hasChunksAt(x0, y0, z0, x1, y1, z1)) + { + for (int x = x0; x <= x1; x++) + for (int y = y0; y <= y1; y++) + for (int z = z0; z <= z1; z++) + { + int t = level->getTile(x, y, z); + if (t > 0) + { + Tile::tiles[t]->entityInside(level, x, y, z, shared_from_this()); + } + } + } +} + + +void Entity::playStepSound(int xt, int yt, int zt, int t) +{ + const Tile::SoundType *soundType = Tile::tiles[t]->soundType; + MemSect(31); + if(GetType() == eTYPE_PLAYER) + { + // should we turn off step sounds? + unsigned int uiAnimOverrideBitmask=getAnimOverrideBitmask(); // this is masked for custom anim off, and force anim + + if(( uiAnimOverrideBitmask& (1<getTile(xt, yt + 1, zt) == Tile::topSnow_Id) + { + soundType = Tile::topSnow->soundType; + mplevel->playLocalSound((double)xt+0.5,(double)yt,(double)zt+0.5,soundType->getStepSound(), soundType->getVolume() * 0.15f, soundType->getPitch()); + } + else if (!Tile::tiles[t]->material->isLiquid()) + { + mplevel->playLocalSound((double)xt+0.5,(double)yt,(double)zt+0.5,soundType->getStepSound(), soundType->getVolume() * 0.15f, soundType->getPitch()); + } + } + else + { + if (level->getTile(xt, yt + 1, zt) == Tile::topSnow_Id) + { + soundType = Tile::topSnow->soundType; + level->playLocalSound((double)xt+0.5,(double)yt,(double)zt+0.5,soundType->getStepSound(), soundType->getVolume() * 0.15f, soundType->getPitch()); + } + else if (!Tile::tiles[t]->material->isLiquid()) + { + level->playLocalSound((double)xt+0.5,(double)yt,(double)zt+0.5,soundType->getStepSound(), soundType->getVolume() * 0.15f, soundType->getPitch()); + } + } + } + else + { + if (level->getTile(xt, yt + 1, zt) == Tile::topSnow_Id) + { + soundType = Tile::topSnow->soundType; + level->playSound(shared_from_this(), soundType->getStepSound(), soundType->getVolume() * 0.15f, soundType->getPitch()); + } + else if (!Tile::tiles[t]->material->isLiquid()) + { + level->playSound(shared_from_this(), soundType->getStepSound(), soundType->getVolume() * 0.15f, soundType->getPitch()); + } + } + MemSect(0); +} + +void Entity::playSound(int iSound, float volume, float pitch) +{ + level->playSound(shared_from_this(), iSound, volume, pitch); +} + +bool Entity::makeStepSound() +{ + return true; +} + +void Entity::checkFallDamage(double ya, bool onGround) +{ + if (onGround) + { + if (fallDistance > 0) + { + if (dynamic_pointer_cast(shared_from_this()) != NULL) + { + int xt = Mth::floor(x); + int yt = Mth::floor(y - 0.2f - this->heightOffset); + int zt = Mth::floor(z); + int t = level->getTile(xt, yt, zt); + if (t == 0 && level->getTile(xt, yt - 1, zt) == Tile::fence_Id) + { + t = level->getTile(xt, yt - 1, zt); + } + + if (t > 0) + { + Tile::tiles[t]->fallOn(level, xt, yt, zt, shared_from_this(), fallDistance); + } + } + causeFallDamage(fallDistance); + fallDistance = 0; + } + } + else + { + if (ya < 0) fallDistance -= (float) ya; + } +} + +AABB *Entity::getCollideBox() +{ + return NULL; +} + +void Entity::burn(int dmg) +{ + if (!fireImmune) + { + hurt(DamageSource::inFire, dmg); + } +} + +bool Entity::isFireImmune() +{ + return fireImmune; +} + +void Entity::causeFallDamage(float distance) +{ + if (rider.lock() != NULL) rider.lock()->causeFallDamage(distance); +} + + +bool Entity::isInWaterOrRain() +{ + return wasInWater || (level->isRainingAt( Mth::floor(x), Mth::floor(y), Mth::floor(z))); +} + +bool Entity::isInWater() +{ + return wasInWater; +} + +bool Entity::updateInWaterState() +{ + return level->checkAndHandleWater(bb->grow(0, -0.4f, 0)->shrink(0.001, 0.001, 0.001), Material::water, shared_from_this()); +} + +bool Entity::isUnderLiquid(Material *material) +{ + double yp = y + getHeadHeight(); + int xt = Mth::floor(x); + int yt = Mth::floor(yp); // 4J - this used to be a nested pair of floors for some reason + int zt = Mth::floor(z); + int t = level->getTile(xt, yt, zt); + if (t != 0 && Tile::tiles[t]->material == material) { + float hh = LiquidTile::getHeight(level->getData(xt, yt, zt)) - 1 / 9.0f; + float h = yt + 1 - hh; + return yp < h; + } + return false; +} + +float Entity::getHeadHeight() +{ + return 0; +} + +bool Entity::isInLava() +{ + return level->containsMaterial(bb->grow(-0.1f, -0.4f, -0.1f), Material::lava); +} + +void Entity::moveRelative(float xa, float za, float speed) +{ + float dist = xa * xa + za * za; + if (dist < 0.01f * 0.01f) return; + + dist = sqrt(dist); + if (dist < 1) dist = 1; + dist = speed / dist; + xa *= dist; + za *= dist; + + float sinVar = Mth::sin(yRot * PI / 180); + float cosVar = Mth::cos(yRot * PI / 180); + + xd += xa * cosVar - za * sinVar; + zd += za * cosVar + xa * sinVar; +} + +// 4J - change brought forward from 1.8.2 +int Entity::getLightColor(float a) +{ + int xTile = Mth::floor(x); + int zTile = Mth::floor(z); + + if (level->hasChunkAt(xTile, 0, zTile)) + { + double hh = (bb->y1 - bb->y0) * 0.66; + int yTile = Mth::floor(y - this->heightOffset + hh); + return level->getLightColor(xTile, yTile, zTile, 0); + } + return 0; +} + +// 4J - changes brought forward from 1.8.2 +float Entity::getBrightness(float a) +{ + int xTile = Mth::floor(x); + int zTile = Mth::floor(z); + if (level->hasChunkAt(xTile, 0, zTile)) + { + double hh = (bb->y1 - bb->y0) * 0.66; + int yTile = Mth::floor(y - this->heightOffset + hh); + return level->getBrightness(xTile, yTile, zTile); + } + return 0; +} + +void Entity::setLevel(Level *level) +{ + this->level = level; +} + +void Entity::absMoveTo(double x, double y, double z, float yRot, float xRot) +{ + this->xo = this->x = x; + this->yo = this->y = y; + this->zo = this->z = z; + this->yRotO = this->yRot = yRot; + this->xRotO = this->xRot = xRot; + ySlideOffset = 0; + + double yRotDiff = yRotO - yRot; + if (yRotDiff < -180) yRotO += 360; + if (yRotDiff >= 180) yRotO -= 360; + this->setPos(this->x, this->y, this->z); + this->setRot(yRot, xRot); +} + +void Entity::moveTo(double x, double y, double z, float yRot, float xRot) +{ + this->xOld = this->xo = this->x = x; + this->yOld = this->yo = this->y = y + heightOffset; + this->zOld = this->zo = this->z = z; + this->yRot = yRot; + this->xRot = xRot; + this->setPos(this->x, this->y, this->z); +} + +float Entity::distanceTo(shared_ptr e) +{ + float xd = (float) (x - e->x); + float yd = (float) (y - e->y); + float zd = (float) (z - e->z); + return sqrt(xd * xd + yd * yd + zd * zd); +} + +double Entity::distanceToSqr(double x2, double y2, double z2) +{ + double xd = (x - x2); + double yd = (y - y2); + double zd = (z - z2); + return xd * xd + yd * yd + zd * zd; +} + +double Entity::distanceTo(double x2, double y2, double z2) +{ + double xd = (x - x2); + double yd = (y - y2); + double zd = (z - z2); + return sqrt(xd * xd + yd * yd + zd * zd); +} + +double Entity::distanceToSqr(shared_ptr e) +{ + double xd = x - e->x; + double yd = y - e->y; + double zd = z - e->z; + return xd * xd + yd * yd + zd * zd; +} + +void Entity::playerTouch(shared_ptr player) +{ +} + +void Entity::push(shared_ptr e) +{ + if (e->rider.lock().get() == this || e->riding.get() == this) return; + + double xa = e->x - x; + double za = e->z - z; + + double dd = Mth::asbMax(xa, za); + + if (dd >= 0.01f) + { + dd = sqrt(dd); + xa /= dd; + za /= dd; + + double pow = 1 / dd; + if (pow > 1) pow = 1; + xa *= pow; + za *= pow; + + xa *= 0.05f; + za *= 0.05f; + + xa *= 1 - pushthrough; + za *= 1 - pushthrough; + + this->push(-xa, 0, -za); + e->push(xa, 0, za); + } +} + +void Entity::push(double xa, double ya, double za) +{ + xd += xa; + yd += ya; + zd += za; + this->hasImpulse = true; +} + + +void Entity::markHurt() +{ + this->hurtMarked = true; +} + + +bool Entity::hurt(DamageSource *source, int damage) +{ + markHurt(); + return false; +} + +bool Entity::intersects(double x0, double y0, double z0, double x1, double y1, double z1) +{ + return bb->intersects(x0, y0, z0, x1, y1, z1); +} + +bool Entity::isPickable() +{ + return false; +} + +bool Entity::isPushable() +{ + return false; +} + +bool Entity::isShootable() +{ + return false; +} + +void Entity::awardKillScore(shared_ptr victim, int score) +{ +} + +bool Entity::shouldRender(Vec3 *c) +{ + double xd = x - c->x; + double yd = y - c->y; + double zd = z - c->z; + double distance = xd * xd + yd * yd + zd * zd; + return shouldRenderAtSqrDistance(distance); +} + +bool Entity::shouldRenderAtSqrDistance(double distance) +{ + double size = bb->getSize(); + size *= 64.0f * viewScale; + return distance < size * size; +} + +// 4J - used to be wstring return type, returning L"" +int Entity::getTexture() +{ + return -1; +} + +bool Entity::isCreativeModeAllowed() +{ + return false; +} + +bool Entity::save(CompoundTag *entityTag) +{ + wstring id = getEncodeId(); + if (removed || id.empty() ) + { + return false; + } + // TODO Is this fine to be casting to a non-const char pointer? + entityTag->putString(L"id", id ); + saveWithoutId(entityTag); + return true; +} + +void Entity::saveWithoutId(CompoundTag *entityTag) +{ + entityTag->put(L"Pos", newDoubleList(3, x, y + ySlideOffset, z)); + entityTag->put(L"Motion", newDoubleList(3, xd, yd, zd)); + entityTag->put(L"Rotation", newFloatList(2, yRot, xRot)); + + entityTag->putFloat(L"FallDistance", fallDistance); + entityTag->putShort(L"Fire", (short) onFire); + entityTag->putShort(L"Air", (short) getAirSupply()); + entityTag->putBoolean(L"OnGround", onGround); + + addAdditonalSaveData(entityTag); +} + +void Entity::load(CompoundTag *tag) +{ + ListTag *pos = (ListTag *) tag->getList(L"Pos"); + ListTag *motion = (ListTag *) tag->getList(L"Motion"); + ListTag *rotation = (ListTag *) tag->getList(L"Rotation"); + + xd = motion->get(0)->data; + yd = motion->get(1)->data; + zd = motion->get(2)->data; + + if (abs(xd) > 10.0) + { + xd = 0; + } + if (abs(yd) > 10.0) + { + yd = 0; + } + if (abs(zd) > 10.0) + { + zd = 0; + } + + xo = xOld = x = pos->get(0)->data; + yo = yOld = y = pos->get(1)->data; + zo = zOld = z = pos->get(2)->data; + + yRotO = yRot = rotation->get(0)->data; + xRotO = xRot = rotation->get(1)->data; + + fallDistance = tag->getFloat(L"FallDistance"); + onFire = tag->getShort(L"Fire"); + setAirSupply(tag->getShort(L"Air")); + onGround = tag->getBoolean(L"OnGround"); + + setPos(x, y, z); + setRot(yRot, xRot); + + readAdditionalSaveData(tag); +} + + +const wstring Entity::getEncodeId() +{ + return EntityIO::getEncodeId( shared_from_this() ); +} + +ListTag *Entity::newDoubleList(unsigned int number, double firstValue, ...) +{ + ListTag *res = new ListTag(); + + // Add the first parameter to the ListTag + res->add( new DoubleTag(L"", firstValue ) ); + + va_list vl; + va_start(vl,firstValue); + + double val; + + for (unsigned int i=1;iadd(new DoubleTag(L"", val)); + } + + va_end(vl); + + return res; +} + +ListTag *Entity::newFloatList(unsigned int number, float firstValue, float secondValue) +{ + ListTag *res = new ListTag(); + + // Add the first parameter to the ListTag + res->add( new FloatTag( L"", firstValue ) ); + + // TODO - 4J Stu For some reason the va_list wasn't working correctly here + // We only make a list of two floats so just overriding and not using va_list + res->add( new FloatTag( L"", secondValue ) ); + + /* + va_list vl; + va_start(vl,firstValue); + + float val; + + for (unsigned int i = 1; i < number; i++) + { + val = va_arg(vl,float); + res->add(new FloatTag(val)); + } + va_end(vl); + */ + return res; +} + +float Entity::getShadowHeightOffs() +{ + return bbHeight / 2; +} + +shared_ptr Entity::spawnAtLocation(int resource, int count) +{ + return spawnAtLocation(resource, count, 0); +} + +shared_ptr Entity::spawnAtLocation(int resource, int count, float yOffs) +{ + return spawnAtLocation(shared_ptr( new ItemInstance(resource, count, 0) ), yOffs); +} + +shared_ptr Entity::spawnAtLocation(shared_ptr itemInstance, float yOffs) +{ + shared_ptr ie = shared_ptr( new ItemEntity(level, x, y + yOffs, z, itemInstance) ); + ie->throwTime = 10; + level->addEntity(ie); + return ie; +} + +bool Entity::isAlive() +{ + return !removed; +} + +bool Entity::isInWall() +{ + for (int i = 0; i < 8; i++) + { + float xo = ((i >> 0) % 2 - 0.5f) * bbWidth * 0.8f; + float yo = ((i >> 1) % 2 - 0.5f) * 0.1f; + float zo = ((i >> 2) % 2 - 0.5f) * bbWidth * 0.8f; + int xt = Mth::floor(x + xo); + int yt = Mth::floor(y + this->getHeadHeight() + yo); + int zt = Mth::floor(z + zo); + if (level->isSolidBlockingTile(xt, yt, zt)) + { + return true; + } + } + return false; +} + +bool Entity::interact(shared_ptr player) +{ + return false; +} + +AABB *Entity::getCollideAgainstBox(shared_ptr entity) +{ + return NULL; +} + +void Entity::rideTick() +{ + if (riding->removed) + { + riding = nullptr; + return; + } + xd = yd = zd = 0; + tick(); + + if (riding == NULL) return; + + // Sets riders old&new position to it's mount's old&new position (plus the ride y-seperatation). + riding->positionRider(); + + yRideRotA += (riding->yRot - riding->yRotO); + xRideRotA += (riding->xRot - riding->xRotO); + + // Wrap rotation angles. + while (yRideRotA >= 180) yRideRotA -= 360; + while (yRideRotA < -180) yRideRotA += 360; + while (xRideRotA >= 180) xRideRotA -= 360; + while (xRideRotA < -180) xRideRotA += 360; + + double yra = yRideRotA * 0.5; + double xra = xRideRotA * 0.5; + + // Cap rotation speed. + float max = 10; + if (yra > max) yra = max; + if (yra < -max) yra = -max; + if (xra > max) xra = max; + if (xra < -max) xra = -max; + + yRideRotA -= yra; + xRideRotA -= xra; + + yRot += (float) yra; + xRot += (float) xra; +} + +void Entity::positionRider() +{ + shared_ptr lockedRider = rider.lock(); + if( lockedRider ) + { + shared_ptr player = dynamic_pointer_cast(lockedRider); + if (!(player && player->isLocalPlayer())) + { + lockedRider->xOld = xOld; + lockedRider->yOld = yOld + getRideHeight() + lockedRider->getRidingHeight(); + lockedRider->zOld = zOld; + } + lockedRider->setPos(x, y + getRideHeight() + lockedRider->getRidingHeight(), z); + } +} + +double Entity::getRidingHeight() +{ + return heightOffset; +} + +double Entity::getRideHeight() +{ + return bbHeight * .75; +} + +void Entity::ride(shared_ptr e) +{ + xRideRotA = 0; + yRideRotA = 0; + + if (e == NULL) + { + if (riding != NULL) + { + // 4J Stu - Position should already be updated before the SetRidingPacket comes in + if(!level->isClientSide) moveTo(riding->x, riding->bb->y0 + riding->bbHeight, riding->z, yRot, xRot); + riding->rider = weak_ptr(); + } + riding = nullptr; + return; + } + if (riding != NULL) + { + riding->rider = weak_ptr(); + } + riding = e; + e->rider = shared_from_this(); +} + +// 4J Stu - Brought forward from 12w36 to fix #46282 - TU5: Gameplay: Exiting the minecart in a tight corridor damages the player +void Entity::findStandUpPosition(shared_ptr vehicle) +{ + AABB *boundingBox; + double fallbackX = vehicle->x; + double fallbackY = vehicle->bb->y0 + vehicle->bbHeight; + double fallbackZ = vehicle->z; + + for (double xDiff = -1.5; xDiff < 2; xDiff += 1.5) + { + for (double zDiff = -1.5; zDiff < 2; zDiff += 1.5) + { + if (xDiff == 0 && zDiff == 0) + { + continue; + } + + int xToInt = (int) (this->x + xDiff); + int zToInt = (int) (this->z + zDiff); + + // 4J Stu - Added loop over y to restaring the bb into 2 block high spaces if required (eg the track block plus 1 air block above it for minecarts) + for(double yDiff = 1.0; yDiff >= 0; yDiff -= 0.5) + { + boundingBox = this->bb->cloneMove(xDiff, yDiff, zDiff); + + if (level->getTileCubes(boundingBox,true)->size() == 0) + { + if (level->isTopSolidBlocking(xToInt, (int) (y - (1-yDiff)), zToInt)) + { + this->moveTo(this->x + xDiff, this->y + yDiff, this->z + zDiff, yRot, xRot); + return; + } + else if (level->isTopSolidBlocking(xToInt, (int) (y - (1-yDiff)) - 1, zToInt) || level->getMaterial(xToInt, (int) (y - (1-yDiff)) - 1, zToInt) == Material::water) + { + fallbackX = x + xDiff; + fallbackY = y + yDiff; + fallbackZ = z + zDiff; + } + } + } + } + } + + this->moveTo(fallbackX, fallbackY, fallbackZ, yRot, xRot); +} + +void Entity::lerpTo(double x, double y, double z, float yRot, float xRot, int steps) +{ + setPos(x, y, z); + setRot(yRot, xRot); + + // 4J - don't know what this special y collision is specifically for, but its definitely bad news + // for arrows as they are actually Meant to intersect the geometry they land in slightly. + if( GetType() != eTYPE_ARROW ) + { + AABBList *collisions = level->getCubes(shared_from_this(), bb->shrink(1 / 32.0, 0, 1 / 32.0)); + if (!collisions->empty()) + { + double yTop = 0; + AUTO_VAR(itEnd, collisions->end()); + for (AUTO_VAR(it, collisions->begin()); it != itEnd; it++) + { + AABB *ab = *it; //collisions->at(i); + if (ab->y1 > yTop) yTop = ab->y1; + } + + y += yTop - bb->y0; + setPos(x, y, z); + } + } +} + +float Entity::getPickRadius() +{ + return 0.1f; +} + +Vec3 *Entity::getLookAngle() +{ + return NULL; +} + +void Entity::handleInsidePortal() +{ +} + +void Entity::lerpMotion(double xd, double yd, double zd) +{ + this->xd = xd; + this->yd = yd; + this->zd = zd; +} + +void Entity::handleEntityEvent(byte eventId) +{ +} + +void Entity::animateHurt() +{ +} + +void Entity::prepareCustomTextures() +{ +} + +ItemInstanceArray Entity::getEquipmentSlots() // ItemInstance[] +{ + return ItemInstanceArray(); // Default ctor creates NULL internal array +} + +// 4J Stu - Brought forward change from 1.3 to fix #64688 - Customer Encountered: TU7: Content: Art: Aura of enchanted item is not displayed for other players in online game +void Entity::setEquippedSlot(int slot, shared_ptr item) +{ +} + +bool Entity::isOnFire() +{ + return onFire > 0 || getSharedFlag(FLAG_ONFIRE); +} + +bool Entity::isRiding() +{ + return riding != NULL || getSharedFlag(FLAG_RIDING); +} + +bool Entity::isSneaking() +{ + return getSharedFlag(FLAG_SNEAKING); +} + +void Entity::setSneaking(bool value) +{ + setSharedFlag(FLAG_SNEAKING, value); +} + +bool Entity::isIdle() +{ + return getSharedFlag(FLAG_IDLEANIM); +} + +void Entity::setIsIdle(bool value) +{ + setSharedFlag(FLAG_IDLEANIM, value); +} + +bool Entity::isSprinting() +{ + return getSharedFlag(FLAG_SPRINTING); +} + +void Entity::setSprinting(bool value) +{ + setSharedFlag(FLAG_SPRINTING, value); +} + +bool Entity::isInvisible() +{ + return getSharedFlag(FLAG_INVISIBLE); +} + +bool Entity::isInvisibleTo(shared_ptr plr) +{ + return isInvisible(); +} + +void Entity::setInvisible(bool value) +{ + setSharedFlag(FLAG_INVISIBLE, value); +} + +bool Entity::isWeakened() +{ + return getSharedFlag(FLAG_EFFECT_WEAKENED); +} + +void Entity::setWeakened(bool value) +{ + setSharedFlag(FLAG_EFFECT_WEAKENED, value); +} + +bool Entity::isUsingItemFlag() +{ + return getSharedFlag(FLAG_USING_ITEM); +} + +void Entity::setUsingItemFlag(bool value) +{ + setSharedFlag(FLAG_USING_ITEM, value); +} + +bool Entity::getSharedFlag(int flag) +{ + return (entityData->getByte(DATA_SHARED_FLAGS_ID) & (1 << flag)) != 0; +} + +void Entity::setSharedFlag(int flag, bool value) +{ + byte currentValue = entityData->getByte(DATA_SHARED_FLAGS_ID); + if (value) + { + entityData->set(DATA_SHARED_FLAGS_ID, (byte) (currentValue | (1 << flag))); + } + else + { + entityData->set(DATA_SHARED_FLAGS_ID, (byte) (currentValue & ~(1 << flag))); + } +} + +// 4J Stu - Brought forward from 1.2.3 to fix 38654 - Gameplay: Player will take damage when air bubbles are present if resuming game from load/autosave underwater. +int Entity::getAirSupply() +{ + return entityData->getShort(DATA_AIR_SUPPLY_ID); +} + +// 4J Stu - Brought forward from 1.2.3 to fix 38654 - Gameplay: Player will take damage when air bubbles are present if resuming game from load/autosave underwater. +void Entity::setAirSupply(int supply) +{ + entityData->set(DATA_AIR_SUPPLY_ID, (short) supply); +} + +void Entity::thunderHit(const LightningBolt *lightningBolt) +{ + burn(5); + onFire++; + if (onFire == 0) setOnFire(8); +} + +void Entity::killed(shared_ptr mob) +{ +} + + +bool Entity::checkInTile(double x, double y, double z) +{ + int xTile = Mth::floor(x); + int yTile = Mth::floor(y); + int zTile = Mth::floor(z); + + double xd = x - (xTile); + double yd = y - (yTile); + double zd = z - (zTile); + + if (level->isSolidBlockingTile(xTile, yTile, zTile)) + { + bool west = !level->isSolidBlockingTile(xTile - 1, yTile, zTile); + bool east = !level->isSolidBlockingTile(xTile + 1, yTile, zTile); + bool up = !level->isSolidBlockingTile(xTile, yTile - 1, zTile); + bool down = !level->isSolidBlockingTile(xTile, yTile + 1, zTile); + bool north = !level->isSolidBlockingTile(xTile, yTile, zTile - 1); + bool south = !level->isSolidBlockingTile(xTile, yTile, zTile + 1); + + int dir = -1; + double closest = 9999; + if (west && xd < closest) + { + closest = xd; + dir = 0; + } + if (east && 1 - xd < closest) + { + closest = 1 - xd; + dir = 1; + } + if (up && yd < closest) + { + closest = yd; + dir = 2; + } + if (down && 1 - yd < closest) + { + closest = 1 - yd; + dir = 3; + } + if (north && zd < closest) + { + closest = zd; + dir = 4; + } + if (south && 1 - zd < closest) + { + closest = 1 - zd; + dir = 5; + } + + float speed = random->nextFloat() * 0.2f + 0.1f; + if (dir == 0) this->xd = -speed; + if (dir == 1) this->xd = +speed; + + if (dir == 2) this->yd = -speed; + if (dir == 3) this->yd = +speed; + + if (dir == 4) this->zd = -speed; + if (dir == 5) this->zd = +speed; + return true; + } + + return false; +} + +void Entity::makeStuckInWeb() +{ + isStuckInWeb = true; + fallDistance = 0; +} + +wstring Entity::getAName() +{ + wstring id = EntityIO::getEncodeId(shared_from_this()); + if (id.empty()) id = L"generic"; + return L"entity." + id + _toString(entityId); + //return I18n.get("entity." + id + ".name"); +} + +vector > *Entity::getSubEntities() +{ + return NULL; +} + +bool Entity::is(shared_ptr other) +{ + return shared_from_this() == other; +} + +float Entity::getYHeadRot() +{ + return 0; +} + +void Entity::setYHeadRot(float yHeadRot) +{ +} + +bool Entity::isAttackable() +{ + return true; +} + +bool Entity::isInvulnerable() +{ + return false; +} + +void Entity::copyPosition(shared_ptr target) +{ + moveTo(target->x, target->y, target->z, target->yRot, target->xRot); +} + +void Entity::setAnimOverrideBitmask(unsigned int uiBitmask) +{ + m_uiAnimOverrideBitmask=uiBitmask; + app.DebugPrintf("!!! Setting anim override bitmask to %d\n",uiBitmask); +} +unsigned int Entity::getAnimOverrideBitmask() +{ + if(app.GetGameSettings(eGameSetting_CustomSkinAnim)==0 ) + { + // We have a force animation for some skins (claptrap) + // 4J-PB - treat all the eAnim_Disable flags as a force anim + unsigned int uiIgnoreUserCustomSkinAnimSettingMask=(1< +{ +friend class Gui; // 4J Stu - Added to be able to access the shared flag functions and constants, without making them publicly available to everything +public: + // 4J-PB - added to replace (e instanceof Type), avoiding dynamic casts + virtual eINSTANCEOF GetType() = 0; + +public: + + static const short TOTAL_AIR_SUPPLY = 20 * 15; + +private: + static int entityCounter; + +public: + int entityId; + + double viewScale; + + bool blocksBuilding; + weak_ptr rider; // Changed to weak to avoid circular dependency between rider/riding entity + shared_ptr riding; + + Level *level; + double xo, yo, zo; + double x, y, z; + double xd, yd, zd; + float yRot, xRot; + float yRotO, xRotO; + /*const*/ AABB *bb; // 4J Was final + bool onGround; + bool horizontalCollision, verticalCollision; + bool collision; + bool hurtMarked; + +protected: + bool isStuckInWeb; + +public: + bool slide; + bool removed; + float heightOffset; + + float bbWidth; + float bbHeight; + + float walkDistO; + float walkDist; + float fallDistance; + +private: + int nextStep; + +public: + double xOld, yOld, zOld; + float ySlideOffset; + float footSize; + bool noPhysics; + float pushthrough; + +protected: + Random *random; + +public: + int tickCount; + int flameTime; + +private: + int onFire; + +protected: + bool wasInWater; + +public: + int invulnerableTime; + +private: + bool firstTick; + +public: + wstring customTextureUrl; + wstring customTextureUrl2; + +protected: + bool fireImmune; + + // values that need to be sent to clients in SMP + shared_ptr entityData; + +private: + // shared flags that are sent to clients (max 8) + static const int DATA_SHARED_FLAGS_ID = 0; + static const int FLAG_ONFIRE = 0; + static const int FLAG_SNEAKING = 1; + static const int FLAG_RIDING = 2; + static const int FLAG_SPRINTING = 3; + static const int FLAG_USING_ITEM = 4; + static const int FLAG_INVISIBLE = 5; + static const int FLAG_IDLEANIM = 6; + static const int FLAG_EFFECT_WEAKENED = 7; //4J ADDED, needed for cure villager tooltip. + static const int DATA_AIR_SUPPLY_ID = 1; + +private: + double xRideRotA, yRideRotA; + +public: + bool inChunk; + int xChunk, yChunk, zChunk; + int xp, yp, zp, xRotp, yRotp; + bool noCulling; + bool hasImpulse; + +protected: + // 4J Added so that client side simulations on the host are not affected by zero-lag + bool m_ignoreVerticalCollisions; + +public: + Entity(Level *level, bool useSmallId = true); // 4J - added useSmallId parameter + virtual ~Entity(); + +protected: + // 4J - added for common ctor code + void _init(bool useSmallId); + +protected: + virtual void defineSynchedData() = 0; + +public: + shared_ptr getEntityData(); + + /* + public bool equals(Object obj) { + if (obj instanceof Entity) { + return ((Entity) obj).entityId == entityId; + } + return false; + } + + public int hashCode() { + return entityId; + } + */ + +protected: + virtual void resetPos(); + +public: + virtual void remove(); + +protected: + virtual void setSize(float w, float h); + void setPos(EntityPos *pos); + void setRot(float yRot, float xRot); + +public: + void setPos(double x, double y, double z); + void turn(float xo, float yo); + void interpolateTurn(float xo, float yo); + virtual void tick(); + virtual void baseTick(); + +protected: + void lavaHurt(); + +public: + virtual void setOnFire(int numberOfSeconds); + virtual void clearFire(); + +protected: + virtual void outOfWorld(); + +public: + bool isFree(float xa, float ya, float za, float grow); + bool isFree(double xa, double ya, double za); + virtual void move(double xa, double ya, double za, bool noEntityCubes=false); // 4J - added noEntityCubes parameter + +protected: + virtual void checkInsideTiles(); + virtual void playStepSound(int xt, int yt, int zt, int t); + +public: + virtual void playSound(int iSound, float volume, float pitch); + +protected: + virtual bool makeStepSound(); + virtual void checkFallDamage(double ya, bool onGround); + +public: + virtual AABB *getCollideBox(); + +protected: + virtual void burn(int dmg); + +public: + bool isFireImmune(); + +protected: + virtual void causeFallDamage(float distance); + +public: + bool isInWaterOrRain(); + virtual bool isInWater(); + virtual bool updateInWaterState(); + bool isUnderLiquid(Material *material); + virtual float getHeadHeight(); + bool isInLava(); + void moveRelative(float xa, float za, float speed); + virtual int getLightColor(float a); // 4J - change brought forward from 1.8.2 + virtual float getBrightness(float a); + virtual void setLevel(Level *level); + void absMoveTo(double x, double y, double z, float yRot, float xRot); + void moveTo(double x, double y, double z, float yRot, float xRot); + float distanceTo(shared_ptr e); + double distanceToSqr(double x2, double y2, double z2); + double distanceTo(double x2, double y2, double z2); + double distanceToSqr(shared_ptr e); + virtual void playerTouch(shared_ptr player); + virtual void push(shared_ptr e); + virtual void push(double xa, double ya, double za); + +protected: + void markHurt(); + +public: + // 4J Added damageSource param to enable telemetry on player deaths + virtual bool hurt(DamageSource *source, int damage); + bool intersects(double x0, double y0, double z0, double x1, double y1, double z1); + virtual bool isPickable(); + virtual bool isPushable(); + virtual bool isShootable(); + virtual void awardKillScore(shared_ptr victim, int score); + virtual bool shouldRender(Vec3 *c); + virtual bool shouldRenderAtSqrDistance(double distance); + virtual int getTexture(); // 4J - changed from wstring to int + virtual bool isCreativeModeAllowed(); + bool save(CompoundTag *entityTag); + void saveWithoutId(CompoundTag *entityTag); + virtual void load(CompoundTag *tag); + +protected: + const wstring getEncodeId(); + +public: + virtual void readAdditionalSaveData(CompoundTag *tag) = 0; + virtual void addAdditonalSaveData(CompoundTag *tag) = 0; + +protected: + ListTag *newDoubleList(unsigned int number, double firstValue, ...); + ListTag *newFloatList(unsigned int number, float firstValue, float secondValue); + +public: + virtual float getShadowHeightOffs(); + shared_ptr spawnAtLocation(int resource, int count); + shared_ptr spawnAtLocation(int resource, int count, float yOffs); + shared_ptr spawnAtLocation(shared_ptr itemInstance, float yOffs); + virtual bool isAlive(); + virtual bool isInWall(); + virtual bool interact(shared_ptr player); + virtual AABB *getCollideAgainstBox(shared_ptr entity); + + virtual void rideTick(); + virtual void positionRider(); + virtual double getRidingHeight(); + virtual double getRideHeight(); + virtual void ride(shared_ptr e); + virtual void findStandUpPosition(shared_ptr vehicle); // 4J Stu - Brought forward from 12w36 to fix #46282 - TU5: Gameplay: Exiting the minecart in a tight corridor damages the player + virtual void lerpTo(double x, double y, double z, float yRot, float xRot, int steps); + virtual float getPickRadius(); + virtual Vec3 *getLookAngle(); + virtual void handleInsidePortal(); + virtual void lerpMotion(double xd, double yd, double zd); + virtual void handleEntityEvent(byte eventId); + virtual void animateHurt(); + virtual void prepareCustomTextures(); + virtual ItemInstanceArray getEquipmentSlots(); // ItemInstance[] + virtual void setEquippedSlot(int slot, shared_ptr item); // 4J Stu - Brought forward change from 1.3 to fix #64688 - Customer Encountered: TU7: Content: Art: Aura of enchanted item is not displayed for other players in online game + virtual bool isOnFire(); + virtual bool isRiding(); + virtual bool isSneaking(); + virtual void setSneaking(bool value); + virtual bool isIdle(); + virtual void setIsIdle(bool value); + virtual bool isSprinting(); + virtual void setSprinting(bool value); + virtual bool isInvisible(); + virtual bool isInvisibleTo(shared_ptr plr); + virtual void setInvisible(bool value); + virtual bool isUsingItemFlag(); + virtual void setUsingItemFlag(bool value); + + // 4J-ADDED, we need to see if this is weakened + // on the client for the cure villager tooltip. + bool isWeakened(); + void setWeakened(bool value); + +protected: + bool getSharedFlag(int flag); + void setSharedFlag(int flag, bool value); + +public: + // 4J Stu - Brought forward from 1.2.3 to fix 38654 - Gameplay: Player will take damage when air bubbles are present if resuming game from load/autosave underwater. + int getAirSupply(); + void setAirSupply(int supply); + + virtual void thunderHit(const LightningBolt *lightningBolt); + virtual void killed(shared_ptr mob); + +protected: + bool checkInTile(double x, double y, double z); + +public: + virtual void makeStuckInWeb(); + + virtual wstring getAName(); + + // TU9 + bool skipAttackInteraction(shared_ptr source) {return false;} + + // 4J - added to manage allocation of small ids +private: + // Things also added here to be able to manage the concept of a number of extra "wandering" entities - normally path finding entities aren't allowed to + // randomly wander about once they are a certain distance away from any player, but we want to be able to (in a controlled fashion) allow some to be able + // to move so that we can determine whether they have been enclosed in some kind of farm, and so be able to better determine what shouldn't or shouldn't be despawned. + static const int EXTRA_WANDER_MAX = 3; // Number of entities that can simultaneously wander (in addition to the ones that would be wandering in java) + static const int EXTRA_WANDER_TICKS = 20 * 30; // Number of ticks each extra entity will be allowed to wander for. This should be enough for it to realistically be able to walk further than the biggest enclosure we want to consider + + int getSmallId(); + void freeSmallId(int index); + static unsigned int entityIdUsedFlags[2048/32]; + static unsigned int entityIdWanderFlags[2048/32]; + static unsigned int entityIdRemovingFlags[2048/32]; + static int extraWanderIds[EXTRA_WANDER_MAX]; + static int extraWanderCount; + static int extraWanderTicks; + static DWORD tlsIdx; +public: + static void tickExtraWandering(); + static void countFlagsForPIX(); + void resetSmallId(); + static void useSmallIds(); + void considerForExtraWandering(bool enable); + bool isExtraWanderingEnabled(); + int getWanderingQuadrant(); + + virtual vector > *getSubEntities(); + virtual bool is(shared_ptr other); + virtual float getYHeadRot(); + virtual void setYHeadRot(float yHeadRot); + virtual bool isAttackable(); + virtual bool isInvulnerable(); + virtual void copyPosition(shared_ptr target); + +private: + unsigned int m_uiAnimOverrideBitmask; +public: + void setAnimOverrideBitmask(unsigned int uiBitmask); + unsigned int getAnimOverrideBitmask(); + + // 4J added + virtual bool isDespawnProtected() { return false; } + virtual void setDespawnProtected() {} + virtual bool couldWander() { return false; } + virtual bool canCreateParticles() { return true; } +}; diff --git a/Minecraft.World/EntityActionAtPositionPacket.cpp b/Minecraft.World/EntityActionAtPositionPacket.cpp new file mode 100644 index 00000000..7353f932 --- /dev/null +++ b/Minecraft.World/EntityActionAtPositionPacket.cpp @@ -0,0 +1,56 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "PacketListener.h" +#include "EntityActionAtPositionPacket.h" + + + +const int EntityActionAtPositionPacket::START_SLEEP = 0; + +EntityActionAtPositionPacket::EntityActionAtPositionPacket() +{ + id = -1; + x = 0; + y = 0; + z = 0; + action = 0; +} + +EntityActionAtPositionPacket::EntityActionAtPositionPacket(shared_ptr e, int action, int x, int y, int z) +{ + this->action = action; + this->x = x; + this->y = y; + this->z = z; + this->id = e->entityId; +} + +void EntityActionAtPositionPacket::read(DataInputStream *dis) //throws IOException +{ + id = dis->readInt(); + action = dis->readByte(); + x = dis->readInt(); + y = dis->readByte(); + z = dis->readInt(); +} + +void EntityActionAtPositionPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(id); + dos->writeByte(action); + dos->writeInt(x); + dos->writeByte(y); + dos->writeInt(z); +} + +void EntityActionAtPositionPacket::handle(PacketListener *listener) +{ + listener->handleEntityActionAtPosition(shared_from_this()); +} + +int EntityActionAtPositionPacket::getEstimatedSize() +{ + return 14; +} diff --git a/Minecraft.World/EntityActionAtPositionPacket.h b/Minecraft.World/EntityActionAtPositionPacket.h new file mode 100644 index 00000000..35202017 --- /dev/null +++ b/Minecraft.World/EntityActionAtPositionPacket.h @@ -0,0 +1,23 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class EntityActionAtPositionPacket : public Packet, public enable_shared_from_this +{ +public: + static const int START_SLEEP; + int id, x, y, z, action; + + EntityActionAtPositionPacket(); + EntityActionAtPositionPacket(shared_ptr e, int action, int x, int y, int z); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new EntityActionAtPositionPacket()); } + virtual int getId() { return 17; } +}; \ No newline at end of file diff --git a/Minecraft.World/EntityDamageSource.cpp b/Minecraft.World/EntityDamageSource.cpp new file mode 100644 index 00000000..8c7a5ee1 --- /dev/null +++ b/Minecraft.World/EntityDamageSource.cpp @@ -0,0 +1,38 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.network.packet.h" + +//EntityDamageSource::EntityDamageSource(const wstring &msgId, shared_ptr entity) : DamageSource(msgId) +EntityDamageSource::EntityDamageSource(ChatPacket::EChatPacketMessage msgId, shared_ptr entity) : DamageSource(msgId) +{ + this->entity = entity; +} + +shared_ptr EntityDamageSource::getEntity() +{ + return entity; +} + +//wstring EntityDamageSource::getLocalizedDeathMessage(shared_ptr player) +//{ +// return L"death." + msgId + player->name + entity->getAName(); +// //return I18n.get("death." + msgId, player.name, entity.getAName()); +//} + +shared_ptr EntityDamageSource::getDeathMessagePacket(shared_ptr player) +{ + wstring additional = L""; + if(entity->GetType() == eTYPE_SERVERPLAYER) + { + shared_ptr sourcePlayer = dynamic_pointer_cast(entity); + if(sourcePlayer != NULL) additional = sourcePlayer->name; + } + return shared_ptr( new ChatPacket(player->name, m_msgId, entity->GetType(), additional ) ); +} + +bool EntityDamageSource::scalesWithDifficulty() +{ + return entity != NULL && dynamic_pointer_cast(entity) && !(dynamic_pointer_cast(entity)); +} \ No newline at end of file diff --git a/Minecraft.World/EntityDamageSource.h b/Minecraft.World/EntityDamageSource.h new file mode 100644 index 00000000..bdbe36e7 --- /dev/null +++ b/Minecraft.World/EntityDamageSource.h @@ -0,0 +1,26 @@ +#pragma once +using namespace std; + +#include "DamageSource.h" + +class Entity; +class Player; + +class EntityDamageSource : public DamageSource +{ +protected: + shared_ptr entity; + +public: + //EntityDamageSource(const wstring &msgId, shared_ptr entity); + EntityDamageSource(ChatPacket::EChatPacketMessage msgId, shared_ptr entity); + virtual ~EntityDamageSource() { } + + shared_ptr getEntity(); + + // 4J Stu - Made return a packet + //virtual wstring getLocalizedDeathMessage(shared_ptr player); + virtual shared_ptr getDeathMessagePacket(shared_ptr player); + + virtual bool scalesWithDifficulty(); +}; \ No newline at end of file diff --git a/Minecraft.World/EntityEvent.h b/Minecraft.World/EntityEvent.h new file mode 100644 index 00000000..4d2064ab --- /dev/null +++ b/Minecraft.World/EntityEvent.h @@ -0,0 +1,29 @@ +#pragma once + +class EntityEvent +{ +public: + static const BYTE JUMP = 1; + static const BYTE HURT = 2; + static const BYTE DEATH = 3; + static const BYTE START_ATTACKING = 4; + static const BYTE STOP_ATTACKING = 5; + + static const BYTE TAMING_FAILED = 6; + static const BYTE TAMING_SUCCEEDED = 7; + static const BYTE SHAKE_WETNESS = 8; + + static const BYTE USE_ITEM_COMPLETE = 9; + + static const BYTE EAT_GRASS = 10; + static const BYTE OFFER_FLOWER = 11; + static const BYTE LOVE_HEARTS = 12; + static const BYTE VILLAGER_ANGRY = 13; + static const BYTE VILLAGER_HAPPY = 14; + static const BYTE WITCH_HAT_MAGIC = 15; + static const BYTE ZOMBIE_CONVERTING = 16; + + static const BYTE FIREWORKS_EXPLODE = 17; + + static const BYTE IN_LOVE_HEARTS = 18; +}; \ No newline at end of file diff --git a/Minecraft.World/EntityEventPacket.cpp b/Minecraft.World/EntityEventPacket.cpp new file mode 100644 index 00000000..e92d7692 --- /dev/null +++ b/Minecraft.World/EntityEventPacket.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "EntityEventPacket.h" + + + +EntityEventPacket::EntityEventPacket() +{ + entityId = 0; + eventId = 0; +} + +EntityEventPacket::EntityEventPacket(int entityId, byte eventId) +{ + this->entityId = entityId; + this->eventId = eventId; +} + +void EntityEventPacket::read(DataInputStream *dis) //throws IOException +{ + entityId = dis->readInt(); + eventId = dis->readByte(); +} + +void EntityEventPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(entityId); + dos->writeByte(eventId); +} + +void EntityEventPacket::handle(PacketListener *listener) +{ + listener->handleEntityEvent(shared_from_this()); +} + +int EntityEventPacket::getEstimatedSize() +{ + return 5; +} diff --git a/Minecraft.World/EntityEventPacket.h b/Minecraft.World/EntityEventPacket.h new file mode 100644 index 00000000..a151dbdb --- /dev/null +++ b/Minecraft.World/EntityEventPacket.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class EntityEventPacket : public Packet, public enable_shared_from_this +{ +public: + int entityId; + byte eventId; + + EntityEventPacket(); + EntityEventPacket(int entityId, byte eventId); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener) ; + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new EntityEventPacket()); } + virtual int getId() { return 38; } +}; + + diff --git a/Minecraft.World/EntityIO.cpp b/Minecraft.World/EntityIO.cpp new file mode 100644 index 00000000..d0677d38 --- /dev/null +++ b/Minecraft.World/EntityIO.cpp @@ -0,0 +1,264 @@ +#include "stdafx.h" +#include "Class.h" +#include "Painting.h" +#include "System.h" +#include "Entity.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.entity.boss.enderdragon.h" +#include "net.minecraft.world.entity.npc.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "com.mojang.nbt.h" +#include "EntityIO.h" + +unordered_map *EntityIO::idCreateMap = new unordered_map; +unordered_map *EntityIO::classIdMap = new unordered_map; +unordered_map *EntityIO::numCreateMap = new unordered_map; +unordered_map *EntityIO::numClassMap = new unordered_map; +unordered_map *EntityIO::classNumMap = new unordered_map; +unordered_map *EntityIO::idNumMap = new unordered_map; +unordered_map EntityIO::idsSpawnableInCreative; + +void EntityIO::setId(entityCreateFn createFn, eINSTANCEOF clas, const wstring &id, int idNum) +{ + idCreateMap->insert( unordered_map::value_type(id, createFn) ); + classIdMap->insert( unordered_map::value_type(clas,id ) ); + numCreateMap->insert( unordered_map::value_type(idNum, createFn) ); + numClassMap->insert( unordered_map::value_type(idNum, clas) ); + classNumMap->insert( unordered_map::value_type(clas, idNum) ); + idNumMap->insert( unordered_map::value_type(id, idNum) ); +} + +void EntityIO::setId(entityCreateFn createFn, eINSTANCEOF clas, const wstring &id, int idNum, eMinecraftColour color1, eMinecraftColour color2, int nameId) +{ + setId(createFn, clas, id, idNum); + + idsSpawnableInCreative.insert( unordered_map::value_type( idNum, new SpawnableMobInfo(idNum, color1, color2, nameId) ) ); +} + +void EntityIO::staticCtor() +{ + setId(ItemEntity::create, eTYPE_ITEMENTITY, L"Item", 1); + setId(ExperienceOrb::create, eTYPE_EXPERIENCEORB, L"XPOrb", 2); + + setId(Painting::create, eTYPE_PAINTING, L"Painting", 9); + setId(Arrow::create, eTYPE_ARROW, L"Arrow", 10); + setId(Snowball::create, eTYPE_SNOWBALL, L"Snowball", 11); + setId(Fireball::create, eTYPE_FIREBALL, L"Fireball", 12); + setId(SmallFireball::create, eTYPE_SMALL_FIREBALL, L"SmallFireball", 13); + setId(ThrownEnderpearl::create, eTYPE_THROWNENDERPEARL, L"ThrownEnderpearl", 14); + setId(EyeOfEnderSignal::create, eTYPE_EYEOFENDERSIGNAL, L"EyeOfEnderSignal", 15); + setId(ThrownPotion::create, eTYPE_THROWNPOTION, L"ThrownPotion", 16); + setId(ThrownExpBottle::create, eTYPE_THROWNEXPBOTTLE, L"ThrownExpBottle", 17); + setId(ItemFrame::create, eTYPE_ITEM_FRAME, L"ItemFrame", 18); + + setId(PrimedTnt::create, eTYPE_PRIMEDTNT, L"PrimedTnt", 20); + setId(FallingTile::create, eTYPE_FALLINGTILE, L"FallingSand", 21); + + setId(Minecart::create, eTYPE_MINECART, L"Minecart", 40); + setId(Boat::create, eTYPE_BOAT, L"Boat", 41); + + setId(Mob::create, eTYPE_MOB, L"Mob", 48); + setId(Monster::create, eTYPE_MONSTER, L"Monster", 49); + + setId(Creeper::create, eTYPE_CREEPER, L"Creeper", 50, eMinecraftColour_Mob_Creeper_Colour1, eMinecraftColour_Mob_Creeper_Colour2, IDS_CREEPER); + setId(Skeleton::create, eTYPE_SKELETON, L"Skeleton", 51, eMinecraftColour_Mob_Skeleton_Colour1, eMinecraftColour_Mob_Skeleton_Colour2, IDS_SKELETON); + setId(Spider::create, eTYPE_SPIDER, L"Spider", 52, eMinecraftColour_Mob_Spider_Colour1, eMinecraftColour_Mob_Spider_Colour2, IDS_SPIDER); + setId(Giant::create, eTYPE_GIANT, L"Giant", 53); + setId(Zombie::create, eTYPE_ZOMBIE, L"Zombie", 54, eMinecraftColour_Mob_Zombie_Colour1, eMinecraftColour_Mob_Zombie_Colour2, IDS_ZOMBIE); + setId(Slime::create, eTYPE_SLIME, L"Slime", 55, eMinecraftColour_Mob_Slime_Colour1, eMinecraftColour_Mob_Slime_Colour2, IDS_SLIME); + setId(Ghast::create, eTYPE_GHAST, L"Ghast", 56, eMinecraftColour_Mob_Ghast_Colour1, eMinecraftColour_Mob_Ghast_Colour2, IDS_GHAST); + setId(PigZombie::create, eTYPE_PIGZOMBIE, L"PigZombie", 57, eMinecraftColour_Mob_PigZombie_Colour1, eMinecraftColour_Mob_PigZombie_Colour2, IDS_PIGZOMBIE); + setId(EnderMan::create, eTYPE_ENDERMAN, L"Enderman", 58, eMinecraftColour_Mob_Enderman_Colour1, eMinecraftColour_Mob_Enderman_Colour2, IDS_ENDERMAN); + setId(CaveSpider::create, eTYPE_CAVESPIDER, L"CaveSpider", 59, eMinecraftColour_Mob_CaveSpider_Colour1, eMinecraftColour_Mob_CaveSpider_Colour2, IDS_CAVE_SPIDER); + setId(Silverfish::create, eTYPE_SILVERFISH, L"Silverfish", 60, eMinecraftColour_Mob_Silverfish_Colour1, eMinecraftColour_Mob_Silverfish_Colour2, IDS_SILVERFISH); + setId(Blaze::create, eTYPE_BLAZE, L"Blaze", 61, eMinecraftColour_Mob_Blaze_Colour1, eMinecraftColour_Mob_Blaze_Colour2, IDS_BLAZE); + setId(LavaSlime::create, eTYPE_LAVASLIME, L"LavaSlime", 62, eMinecraftColour_Mob_LavaSlime_Colour1, eMinecraftColour_Mob_LavaSlime_Colour2, IDS_LAVA_SLIME); + setId(EnderDragon::create, eTYPE_ENDERDRAGON, L"EnderDragon", 63); + + setId(Pig::create, eTYPE_PIG, L"Pig", 90, eMinecraftColour_Mob_Pig_Colour1, eMinecraftColour_Mob_Pig_Colour2, IDS_PIG); + setId(Sheep::create, eTYPE_SHEEP, L"Sheep", 91, eMinecraftColour_Mob_Sheep_Colour1, eMinecraftColour_Mob_Sheep_Colour2, IDS_SHEEP); + setId(Cow::create, eTYPE_COW, L"Cow", 92, eMinecraftColour_Mob_Cow_Colour1, eMinecraftColour_Mob_Cow_Colour2, IDS_COW); + setId(Chicken::create, eTYPE_CHICKEN, L"Chicken", 93, eMinecraftColour_Mob_Chicken_Colour1, eMinecraftColour_Mob_Chicken_Colour2, IDS_CHICKEN); + setId(Squid::create, eTYPE_SQUID, L"Squid", 94, eMinecraftColour_Mob_Squid_Colour1, eMinecraftColour_Mob_Squid_Colour2, IDS_SQUID); + setId(Wolf::create, eTYPE_WOLF, L"Wolf", 95, eMinecraftColour_Mob_Wolf_Colour1, eMinecraftColour_Mob_Wolf_Colour2, IDS_WOLF); + setId(MushroomCow::create, eTYPE_MUSHROOMCOW, L"MushroomCow", 96, eMinecraftColour_Mob_MushroomCow_Colour1, eMinecraftColour_Mob_MushroomCow_Colour2, IDS_MUSHROOM_COW); + setId(SnowMan::create, eTYPE_SNOWMAN, L"SnowMan", 97); + setId(Ozelot::create, eTYPE_OZELOT, L"Ozelot", 98, eMinecraftColour_Mob_Ocelot_Colour1, eMinecraftColour_Mob_Ocelot_Colour2, IDS_OZELOT); + setId(VillagerGolem::create, eTYPE_VILLAGERGOLEM, L"VillagerGolem", 99); + + setId(Villager::create, eTYPE_VILLAGER, L"Villager", 120, eMinecraftColour_Mob_Villager_Colour1, eMinecraftColour_Mob_Villager_Colour2, IDS_VILLAGER); + + setId(EnderCrystal::create, eTYPE_ENDER_CRYSTAL, L"EnderCrystal", 200); + + // 4J Added + setId(DragonFireball::create, eTYPE_DRAGON_FIREBALL, L"DragonFireball", 1000); +} + +shared_ptr EntityIO::newEntity(const wstring& id, Level *level) +{ + shared_ptr entity; + + AUTO_VAR(it, idCreateMap->find(id)); + if(it != idCreateMap->end() ) + { + entityCreateFn create = it->second; + if (create != NULL) entity = shared_ptr(create(level)); + } + + return entity; +} + +shared_ptr EntityIO::loadStatic(CompoundTag *tag, Level *level) +{ + shared_ptr entity; + + AUTO_VAR(it, idCreateMap->find(tag->getString(L"id"))); + if(it != idCreateMap->end() ) + { + entityCreateFn create = it->second; + if (create != NULL) entity = shared_ptr(create(level)); + } + + if (entity != NULL) + { + entity->load(tag); + } + else + { +#ifdef _DEBUG + app.DebugPrintf("Skipping Entity with id %ls\n", tag->getString(L"id").c_str() ); +#endif + } + return entity; +} + +shared_ptr EntityIO::newById(int id, Level *level) +{ + shared_ptr entity; + + AUTO_VAR(it, numCreateMap->find(id)); + if(it != numCreateMap->end() ) + { + entityCreateFn create = it->second; + if (create != NULL) entity = shared_ptr(create(level)); + } + + if (entity != NULL) + { + } + else + { + //printf("Skipping Entity with id %d\n", id ) ; + } + return entity; +} + +shared_ptr EntityIO::newByEnumType(eINSTANCEOF eType, Level *level) +{ + shared_ptr entity; + + unordered_map::iterator it = classNumMap->find( eType ); + if( it != classNumMap->end() ) + { + AUTO_VAR(it2, numCreateMap->find(it->second)); + if(it2 != numCreateMap->end() ) + { + entityCreateFn create = it2->second; + if (create != NULL) entity = shared_ptr(create(level)); + } + } + + return entity; +} + +int EntityIO::getId(shared_ptr entity) +{ + unordered_map::iterator it = classNumMap->find( entity->GetType() ); + return (*it).second; +} + +wstring EntityIO::getEncodeId(shared_ptr entity) +{ + unordered_map::iterator it = classIdMap->find( entity->GetType() ); + if( it != classIdMap->end() ) + return (*it).second; + else + return L""; +} + +int EntityIO::getId(const wstring &encodeId) +{ + AUTO_VAR(it, idNumMap->find(encodeId)); + if (it == idNumMap->end()) + { + // defaults to pig... + return 90; + } + return it->second; +} + +wstring EntityIO::getEncodeId(int entityIoValue) +{ + //Class class1 = numClassMap.get(entityIoValue); + //if (class1 != null) + //{ + //return classIdMap.get(class1); + //} + + AUTO_VAR(it, numClassMap->find(entityIoValue)); + if(it != numClassMap->end() ) + { + unordered_map::iterator classIdIt = classIdMap->find( it->second ); + if( classIdIt != classIdMap->end() ) + return (*classIdIt).second; + else + return L""; + } + + return L""; +} + +int EntityIO::getNameId(int entityIoValue) +{ + int id = -1; + + AUTO_VAR(it, idsSpawnableInCreative.find(entityIoValue)); + if(it != idsSpawnableInCreative.end()) + { + id = it->second->nameId; + } + + return id; +} + +eINSTANCEOF EntityIO::getType(const wstring &idString) +{ + AUTO_VAR(it, numClassMap->find(getId(idString))); + if(it != numClassMap->end() ) + { + return it->second; + } + return eTYPE_NOTSET; +} + +eINSTANCEOF EntityIO::getClass(int id) +{ + AUTO_VAR(it, numClassMap->find(id)); + if(it != numClassMap->end() ) + { + return it->second; + } + return eTYPE_NOTSET; +} + +int EntityIO::eTypeToIoid(eINSTANCEOF eType) +{ + unordered_map::iterator it = classNumMap->find( eType ); + if( it != classNumMap->end() ) + return it->second; + return -1; +} diff --git a/Minecraft.World/EntityIO.h b/Minecraft.World/EntityIO.h new file mode 100644 index 00000000..c9aa9f65 --- /dev/null +++ b/Minecraft.World/EntityIO.h @@ -0,0 +1,61 @@ +#pragma once +using namespace std; + +#include "Entity.h" +#include "JavaIntHash.h" + +class Level; +class CompoundTag; +typedef Entity *(*entityCreateFn)(Level *); +class EntityIO +{ +public: + class SpawnableMobInfo + { + public: + int id; + eMinecraftColour eggColor1; + eMinecraftColour eggColor2; + int nameId; // 4J Added + + SpawnableMobInfo(int id, eMinecraftColour eggColor1, eMinecraftColour eggColor2, int nameId) + { + this->id = id; + this->eggColor1 = eggColor1; + this->eggColor2 = eggColor2; + this->nameId = nameId; + } + }; + +private: + static unordered_map *idCreateMap; + static unordered_map *classIdMap; + static unordered_map *numCreateMap; + static unordered_map *numClassMap; + static unordered_map *classNumMap; + static unordered_map *idNumMap; + +public: + static unordered_map idsSpawnableInCreative; + +private: + static void setId(entityCreateFn createFn, eINSTANCEOF clas, const wstring &id, int idNum); + static void setId(entityCreateFn createFn, eINSTANCEOF clas, const wstring &id, int idNum, eMinecraftColour color1, eMinecraftColour color2, int nameId); + +public: + static void staticCtor(); + static shared_ptr newEntity(const wstring& id, Level *level); + static shared_ptr loadStatic(CompoundTag *tag, Level *level); + static shared_ptr newById(int id, Level *level); + static shared_ptr newByEnumType(eINSTANCEOF eType, Level *level); + static int getId(shared_ptr entity); + static wstring getEncodeId(shared_ptr entity); + static int getId(const wstring &encodeId); + static wstring getEncodeId(int entityIoValue); + static int getNameId(int entityIoValue); + static eINSTANCEOF getType(const wstring &idString); + static eINSTANCEOF getClass(int id); + + // 4J-JEV, added for enumerating mobs. + static int eTypeToIoid(eINSTANCEOF eType); +}; diff --git a/Minecraft.World/EntityPos.cpp b/Minecraft.World/EntityPos.cpp new file mode 100644 index 00000000..38b50573 --- /dev/null +++ b/Minecraft.World/EntityPos.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "Entity.h" +#include "EntityPos.h" + +EntityPos::EntityPos(double x, double y, double z, float yRot, float xRot) +{ + this->x = x; + this->y = y; + this->z = z; + this->yRot = yRot; + this->xRot = xRot; + rot = true; + move = true; +} + +EntityPos::EntityPos(double x, double y, double z) +{ + yRot = xRot = 0.0f; + + this->x = x; + this->y = y; + this->z = z; + move = true; + rot = false; +} + +EntityPos::EntityPos(float yRot, float xRot) +{ + x = y = z = 0.0; + + this->yRot = yRot; + this->xRot = xRot; + rot = true; + move = false; +} + +EntityPos *EntityPos::lerp(shared_ptr e, float f) +{ + double xd = e->x+(x-e->x)*f; + double yd = e->y+(y-e->y)*f; + double zd = e->z+(z-e->z)*f; + + float yrdd = Mth::wrapDegrees(yRot - e->yRot); + float xrdd = Mth::wrapDegrees(xRot - e->xRot); + + float yrd = Mth::wrapDegrees(e->yRot + yrdd * f); + float xrd = Mth::wrapDegrees(e->xRot + xrdd * f); + + if (rot && move) + { + return new EntityPos(xd, yd, zd, yrd, xrd); + } + if (move) + { + return new EntityPos(xd, yd, zd); + } + if (rot) + { + return new EntityPos(yrd, xrd); + } + return NULL; +} \ No newline at end of file diff --git a/Minecraft.World/EntityPos.h b/Minecraft.World/EntityPos.h new file mode 100644 index 00000000..5b064901 --- /dev/null +++ b/Minecraft.World/EntityPos.h @@ -0,0 +1,15 @@ +#pragma once + +class EntityPos +{ +public: + double x, y, z; + float yRot, xRot; + bool rot; + bool move; + + EntityPos(double x, double y, double z, float yRot, float xRot); + EntityPos(double x, double y, double z); + EntityPos(float yRot, float xRot); + EntityPos *lerp(shared_ptr e, float f); +}; \ No newline at end of file diff --git a/Minecraft.World/EntityTile.cpp b/Minecraft.World/EntityTile.cpp new file mode 100644 index 00000000..8dd68ff2 --- /dev/null +++ b/Minecraft.World/EntityTile.cpp @@ -0,0 +1,32 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.h" +#include "TileEntity.h" +#include "EntityTile.h" + +EntityTile::EntityTile(int id, Material *material, bool isSolidRender /*= true*/) : Tile(id, material, isSolidRender) +{ + _isEntityTile = true; +} + +void EntityTile::onPlace(Level *level, int x, int y, int z) +{ + Tile::onPlace(level, x, y, z); + level->setTileEntity(x, y, z, newTileEntity(level)); +} + +void EntityTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + Tile::onRemove(level, x, y, z, id, data); + level->removeTileEntity(x, y, z); +} + +void EntityTile::triggerEvent(Level *level, int x, int y, int z, int b0, int b1) +{ + Tile::triggerEvent(level, x, y, z, b0, b1); + shared_ptr te = level->getTileEntity(x, y, z); + if (te != NULL) + { + te->triggerEvent(b0, b1); + } +} \ No newline at end of file diff --git a/Minecraft.World/EntityTile.h b/Minecraft.World/EntityTile.h new file mode 100644 index 00000000..7f781c19 --- /dev/null +++ b/Minecraft.World/EntityTile.h @@ -0,0 +1,15 @@ +#pragma once +#include "Tile.h" + +class TileEntity; + +class EntityTile : public Tile +{ +protected: + EntityTile(int id, Material *material, bool isSolidRender = true); +public: + virtual void onPlace(Level *level, int x, int y, int z); + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); + virtual shared_ptr newTileEntity(Level *level) = 0; + virtual void triggerEvent(Level *level, int x, int y, int z, int b0, int b1); +}; \ No newline at end of file diff --git a/Minecraft.World/Exceptions.h b/Minecraft.World/Exceptions.h new file mode 100644 index 00000000..6f3ba75e --- /dev/null +++ b/Minecraft.World/Exceptions.h @@ -0,0 +1,29 @@ +#pragma once +using namespace std; + +class EOFException : public std::exception +{ + +}; + +class IllegalArgumentException : public std::exception +{ +public: + wstring information; + + IllegalArgumentException(const wstring& information); +}; + +class IOException : public std::exception +{ +public: + wstring information; + + IOException(const wstring& information); +}; + +class RuntimeException : public std::exception +{ +public: + RuntimeException(const wstring& information); +}; \ No newline at end of file diff --git a/Minecraft.World/ExperienceCommand.cpp b/Minecraft.World/ExperienceCommand.cpp new file mode 100644 index 00000000..b8ac2efc --- /dev/null +++ b/Minecraft.World/ExperienceCommand.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include "net.minecraft.commands.h" +#include "..\Minecraft.Client\MinecraftServer.h" +#include "..\Minecraft.Client\PlayerList.h" +#include "ExperienceCommand.h" + +EGameCommand ExperienceCommand::getId() +{ + return eGameCommand_Experience; +} + +void ExperienceCommand::execute(shared_ptr source, byteArray commandData) +{ + //if (args.length > 0) + //{ + // Player player; + // int amount = convertArgToInt(source, args[0], 0, 5000); + + // if (args.length > 1) { + // player = getPlayer(args[1]); + // } else { + // player = convertSourceToPlayer(source); + // } + + // player.increaseXp(amount); + // logAdminAction(source, "commands.xp.success", amount, player.getAName()); + //} +} + +shared_ptr ExperienceCommand::getPlayer(PlayerUID playerId) +{ + return nullptr; + //shared_ptr player = MinecraftServer::getInstance()->getPlayers()->getPlayer(playerId); + + //if (player == null) + //{ + // throw new PlayerNotFoundException(); + //} else { + // return player; + //} +} \ No newline at end of file diff --git a/Minecraft.World/ExperienceCommand.h b/Minecraft.World/ExperienceCommand.h new file mode 100644 index 00000000..b1dd2cbe --- /dev/null +++ b/Minecraft.World/ExperienceCommand.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Command.h" + +class CommandSender; + +class ExperienceCommand : public Command +{ +public: + virtual EGameCommand getId(); + virtual void execute(shared_ptr source, byteArray commandData); + +protected: + shared_ptr getPlayer(PlayerUID playerId); +}; \ No newline at end of file diff --git a/Minecraft.World/ExperienceItem.cpp b/Minecraft.World/ExperienceItem.cpp new file mode 100644 index 00000000..497c2385 --- /dev/null +++ b/Minecraft.World/ExperienceItem.cpp @@ -0,0 +1,32 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "SoundTypes.h" +#include "ExperienceItem.h" + +ExperienceItem::ExperienceItem(int id) : Item(id) +{ +} + +bool ExperienceItem::isFoil(shared_ptr itemInstance) +{ + return true; +} + +bool ExperienceItem::TestUse(Level *level, shared_ptr player) +{ + return true; +} + +shared_ptr ExperienceItem::use(shared_ptr itemInstance, Level *level, shared_ptr player) +{ + if (!player->abilities.instabuild) + { + itemInstance->count--; + } + level->playSound(player, eSoundType_RANDOM_BOW, 0.5f, 0.4f / (random->nextFloat() * 0.4f + 0.8f)); + if (!level->isClientSide) level->addEntity( shared_ptr( new ThrownExpBottle(level, player) )); + return itemInstance; +} \ No newline at end of file diff --git a/Minecraft.World/ExperienceItem.h b/Minecraft.World/ExperienceItem.h new file mode 100644 index 00000000..c8e95925 --- /dev/null +++ b/Minecraft.World/ExperienceItem.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Item.h" + +class ItemInstance; + +class ExperienceItem : public Item +{ +public: + ExperienceItem(int id); + + virtual bool isFoil(shared_ptr itemInstance); + virtual shared_ptr use(shared_ptr itemInstance, Level *level, shared_ptr player); + virtual bool TestUse(Level *level, shared_ptr player); +}; \ No newline at end of file diff --git a/Minecraft.World/ExperienceOrb.cpp b/Minecraft.World/ExperienceOrb.cpp new file mode 100644 index 00000000..76f2a9ae --- /dev/null +++ b/Minecraft.World/ExperienceOrb.cpp @@ -0,0 +1,330 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.damagesource.h" +#include "com.mojang.nbt.h" +#include "JavaMath.h" +#include "SharedConstants.h" +#include "ExperienceOrb.h" +#include "SoundTypes.h" + + + +const int ExperienceOrb::LIFETIME = 5 * 60 * SharedConstants::TICKS_PER_SECOND; // Five minutes! + +void ExperienceOrb::_init() +{ + tickCount = 0; + age = 0; + + throwTime = 0; + + health = 5; + value = 0; + followingPlayer = nullptr; + followingTime = 0; +} + +ExperienceOrb::ExperienceOrb(Level *level, double x, double y, double z, int count) : Entity(level) +{ + _init(); + + setSize(0.5f, 0.5f); + heightOffset = bbHeight / 2.0f; + setPos(x, y, z); + + yRot = (float) (Math::random() * 360); + + xd = (float) (Math::random() * 0.2f - 0.1f) * 2; + yd = (float) (Math::random() * 0.2) * 2; + zd = (float) (Math::random() * 0.2f - 0.1f) * 2; + + value = count; +} + +bool ExperienceOrb::makeStepSound() +{ + return false; +} + +ExperienceOrb::ExperienceOrb(Level *level) : Entity( level ) +{ + _init(); + + setSize(0.25f, 0.25f); + heightOffset = bbHeight / 2.0f; +} + +void ExperienceOrb::defineSynchedData() +{ +} + +int ExperienceOrb::getLightColor(float a) +{ + float l = 0.5f; + if (l < 0) l = 0; + if (l > 1) l = 1; + int br = Entity::getLightColor(a); + + int br1 = (br) & 0xff; + int br2 = (br >> 16) & 0xff; + br1 += (int) (l * 15 * 16); + if (br1 > 15 * 16) br1 = 15 * 16; + // br2 = 15*16; + return br1 | br2 << 16; +} + +void ExperienceOrb::tick() +{ + Entity::tick(); + if (throwTime > 0) throwTime--; + xo = x; + yo = y; + zo = z; + + yd -= 0.03f; + if (level->getMaterial(Mth::floor(x), Mth::floor(y), Mth::floor(z)) == Material::lava) + { + yd = 0.2f; + xd = (random->nextFloat() - random->nextFloat()) * 0.2f; + zd = (random->nextFloat() - random->nextFloat()) * 0.2f; + level->playSound(shared_from_this(), eSoundType_RANDOM_FIZZ, 0.4f, 2.0f + random->nextFloat() * 0.4f); + } + checkInTile(x, (bb->y0 + bb->y1) / 2, z); + + double maxDist = 8; + // 4J - PC Comment + // Usually exp orbs will get created at the same time so smoothen the lagspikes + if (followingTime < tickCount - SharedConstants::TICKS_PER_SECOND + (entityId % 100)) + { + if (followingPlayer == NULL || followingPlayer->distanceToSqr(shared_from_this()) > maxDist * maxDist) + { + followingPlayer = level->getNearestPlayer(shared_from_this(), maxDist); + } + followingTime = tickCount; + } + if (followingPlayer != NULL) + { + double xdd = (followingPlayer->x - x) / maxDist; + double ydd = (followingPlayer->y + followingPlayer->getHeadHeight() - y) / maxDist; + double zdd = (followingPlayer->z - z) / maxDist; + double dd = sqrt(xdd * xdd + ydd * ydd + zdd * zdd); + double power = 1 - dd; + if (power > 0) + { + power = power * power; + xd += xdd / dd * power * 0.1; + yd += ydd / dd * power * 0.1; + zd += zdd / dd * power * 0.1; + } + } + + move(xd, yd, zd); + + float friction = 0.98f; + if (onGround) + { + friction = 0.6f * 0.98f; + int t = level->getTile(Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z)); + if (t > 0) + { + friction = Tile::tiles[t]->friction * 0.98f; + } + } + + xd *= friction; + yd *= 0.98f; + zd *= friction; + + if (onGround) + { + yd *= -0.9f; + } + + tickCount++; + + age++; + if (age >= LIFETIME) + { + remove(); + } +} + +bool ExperienceOrb::updateInWaterState() +{ + return level->checkAndHandleWater(bb, Material::water, shared_from_this()); +} + +void ExperienceOrb::burn(int dmg) +{ + hurt(DamageSource::inFire, dmg); +} + +bool ExperienceOrb::hurt(DamageSource *source, int damage) +{ + markHurt(); + health -= damage; + if (health <= 0) + { + remove(); + } + return false; +} + +void ExperienceOrb::addAdditonalSaveData(CompoundTag *entityTag) +{ + entityTag->putShort(L"Health", (byte) health); + entityTag->putShort(L"Age", (short) age); + entityTag->putShort(L"Value", (short) value); +} + +void ExperienceOrb::readAdditionalSaveData(CompoundTag *tag) +{ + health = tag->getShort(L"Health") & 0xff; + age = tag->getShort(L"Age"); + value = tag->getShort(L"Value"); +} + +void ExperienceOrb::playerTouch(shared_ptr player) +{ + if (level->isClientSide) return; + + if (throwTime == 0 && player->takeXpDelay == 0) + { + player->takeXpDelay = 2; + // 4J - sound change brought forward from 1.2.3 + level->playSound(shared_from_this(), eSoundType_RANDOM_ORB, 0.1f, 0.5f * ((random->nextFloat() - random->nextFloat()) * 0.7f + 1.8f)); + player->take(shared_from_this(), 1); + player->increaseXp(value); + remove(); + } +} + +int ExperienceOrb::getValue() +{ + return value; +} + +int ExperienceOrb::getIcon() +{ + + if (value >= 2477) + { + return 10; + } + else if (value >= 1237) + { + return 9; + } + else if (value >= 617) + { + return 8; + } + else if (value >= 307) + { + return 7; + } + else if (value >= 149) + { + return 6; + } + else if (value >= 73) + { + return 5; + } + else if (value >= 37) + { + return 4; + } + else if (value >= 17) + { + return 3; + } + else if (value >= 7) + { + return 2; + } + else if (value >= 3) + { + return 1; + } + + return 0; +} + +/** +* Fetches the biggest possible experience orb value based on a maximum +* value. The current algorithm is next prime which is at least twice more +* than the previous one. +* +* @param maxValue +* @return +*/ +int ExperienceOrb::getExperienceValue(int maxValue) +{ + + if (maxValue >= 2477) + { + return 2477; + } + else if (maxValue >= 1237) + { + return 1237; + } + else if (maxValue >= 617) + { + return 617; + } + else if (maxValue >= 307) + { + return 307; + } + else if (maxValue >= 149) + { + return 149; + } + else if (maxValue >= 73) + { + return 73; + } + else if (maxValue >= 37) + { + return 37; + } + else if (maxValue >= 17) + { + return 17; + } + else if (maxValue >= 7) + { + return 7; + } + else if (maxValue >= 3) + { + return 3; + } + + return 1; +} + +bool ExperienceOrb::isAttackable() +{ + return false; +} + +// 4J added +bool ExperienceOrb::shouldRender(Vec3 *c) +{ + double xd = x - c->x; + double yd = y - c->y; + double zd = z - c->z; + double distance = xd * xd + yd * yd + zd * zd; + + // 4J - don't render experience orbs that are less than 2 metres away, to try and avoid large particles that are causing us problems with photosensitivity testing - issues when you go + // near a large pile of experience orbs that all rush towards the near clip plane + if( distance < 4 ) return false; + + return Entity::shouldRender(c); +} \ No newline at end of file diff --git a/Minecraft.World/ExperienceOrb.h b/Minecraft.World/ExperienceOrb.h new file mode 100644 index 00000000..524ae52a --- /dev/null +++ b/Minecraft.World/ExperienceOrb.h @@ -0,0 +1,60 @@ +#pragma once + +#include "Entity.h" + +class ExperienceOrb : public Entity +{ +public: + virtual eINSTANCEOF GetType() { return eTYPE_EXPERIENCEORB; } + static Entity *create(Level *level) { return new ExperienceOrb(level); } + +private: + static const int LIFETIME; + +public: + int tickCount; + int age; + + int throwTime; + +private: + int health; + int value; + shared_ptr followingPlayer; + int followingTime; + + void _init(); + +public: + ExperienceOrb(Level *level, double x, double y, double z, int count); + +protected: + virtual bool makeStepSound(); + +public: + ExperienceOrb(Level *level); + +protected: + virtual void defineSynchedData(); + +public: + virtual int getLightColor(float a); + virtual void tick(); + virtual bool updateInWaterState(); + +protected: + virtual void burn(int dmg); + +public: + virtual bool hurt(DamageSource *source, int damage); + virtual void addAdditonalSaveData(CompoundTag *entityTag); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual void playerTouch(shared_ptr player); + int getValue(); + int getIcon(); + + static int getExperienceValue(int maxValue); + virtual bool isAttackable(); + + virtual bool shouldRender(Vec3 *c); // 4J added +}; \ No newline at end of file diff --git a/Minecraft.World/ExplodePacket.cpp b/Minecraft.World/ExplodePacket.cpp new file mode 100644 index 00000000..aec5b261 --- /dev/null +++ b/Minecraft.World/ExplodePacket.cpp @@ -0,0 +1,136 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.level.h" +#include "PacketListener.h" +#include "ExplodePacket.h" + + + +ExplodePacket::ExplodePacket() +{ + x = 0; + y = 0; + z = 0; + r = 0.0f; + m_bKnockbackOnly = false; + knockbackX = 0.0f; + knockbackY = 0.0f; + knockbackZ = 0.0f; +} + +ExplodePacket::ExplodePacket(double x, double y, double z, float r, unordered_set *toBlow, Vec3 *knockback, bool knockBackOnly) +{ + this->x = x; + this->y = y; + this->z = z; + this->r = r; + m_bKnockbackOnly = knockBackOnly; + + if(toBlow != NULL) + { + this->toBlow.assign(toBlow->begin(),toBlow->end()); + //for( AUTO_VAR(it, toBlow->begin()); it != toBlow->end(); it++ ) + //{ + // this->toBlow.push_back(*it); + //} + } + + if (knockback != NULL) + { + knockbackX = (float) knockback->x; + knockbackY = (float) knockback->y; + knockbackZ = (float) knockback->z; + } +} + +void ExplodePacket::read(DataInputStream *dis) //throws IOException +{ + m_bKnockbackOnly = dis->readBoolean(); + + if(!m_bKnockbackOnly) + { + x = dis->readDouble(); + y = dis->readDouble(); + z = dis->readDouble(); + r = dis->readFloat(); + int count = dis->readInt(); + + int xp = (int)x; + int yp = (int)y; + int zp = (int)z; + for (int i=0; ireadByte())+xp; + int yy = ((signed char)dis->readByte())+yp; + int zz = ((signed char)dis->readByte())+zp; + toBlow.push_back( TilePos(xx, yy, zz) ); + } + } + + knockbackX = dis->readFloat(); + knockbackY = dis->readFloat(); + knockbackZ = dis->readFloat(); +} + +void ExplodePacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeBoolean(m_bKnockbackOnly); + + if(!m_bKnockbackOnly) + { + dos->writeDouble(x); + dos->writeDouble(y); + dos->writeDouble(z); + dos->writeFloat(r); + dos->writeInt((int)toBlow.size()); + + int xp = (int)x; + int yp = (int)y; + int zp = (int)z; + + //(Myset::const_iterator it = c1.begin(); + //it != c1.end(); ++it) + + for( AUTO_VAR(it, toBlow.begin()); it != toBlow.end(); it++ ) + { + TilePos tp = *it; + + int xx = tp.x-xp; + int yy = tp.y-yp; + int zz = tp.z-zp; + dos->writeByte(xx); + dos->writeByte(yy); + dos->writeByte(zz); + } + } + + dos->writeFloat(knockbackX); + dos->writeFloat(knockbackY); + dos->writeFloat(knockbackZ); +} + +void ExplodePacket::handle(PacketListener *listener) +{ + listener->handleExplosion(shared_from_this()); +} + +int ExplodePacket::getEstimatedSize() +{ + return 8*3+4+4+(int)toBlow.size()*3+12; +} + +float ExplodePacket::getKnockbackX() +{ + return knockbackX; +} + +float ExplodePacket::getKnockbackY() +{ + return knockbackY; +} + +float ExplodePacket::getKnockbackZ() +{ + return knockbackZ; +} \ No newline at end of file diff --git a/Minecraft.World/ExplodePacket.h b/Minecraft.World/ExplodePacket.h new file mode 100644 index 00000000..d4d8b3f9 --- /dev/null +++ b/Minecraft.World/ExplodePacket.h @@ -0,0 +1,37 @@ +#pragma once +using namespace std; + +#include "TilePos.h" +#include "Packet.h" + +class ExplodePacket : public Packet, public enable_shared_from_this +{ +public: + double x, y, z; + float r; + vector toBlow; // 4J - was an unorderedset but doesn't require any features of that apart from making it match the ctor toBlow type + bool m_bKnockbackOnly; + + private: + float knockbackX; + float knockbackY; + float knockbackZ; + + public: + + ExplodePacket(); + ExplodePacket(double x, double y, double z, float r, unordered_set *toBlow, Vec3 *knockback, bool knockBackOnly); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + + float getKnockbackX(); + float getKnockbackY(); + float getKnockbackZ(); + +public: + static shared_ptr create() { return shared_ptr(new ExplodePacket()); } + virtual int getId() { return 60; } +}; \ No newline at end of file diff --git a/Minecraft.World/Explosion.cpp b/Minecraft.World/Explosion.cpp new file mode 100644 index 00000000..604813b7 --- /dev/null +++ b/Minecraft.World/Explosion.cpp @@ -0,0 +1,265 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.damagesource.h" +#include "TilePos.h" +#include "Explosion.h" +#include "SoundTypes.h" + +Explosion::Explosion(Level *level, shared_ptr source, double x, double y, double z, float r) +{ + fire = false; + random = new Random(); + + this->level = level; + this->source = source; + this->r = r; + this->x = x; + this->y = y; + this->z = z; + + destroyBlocks = true; + size = 16; +} + +Explosion::~Explosion() +{ + delete random; + for(AUTO_VAR(it, hitPlayers.begin()); it != hitPlayers.end(); ++it) + { + delete it->second; + } +} + +void Explosion::explode() +{ + float oR = r; + + int size = 16; + for (int xx = 0; xx < size; xx++) + { + for (int yy = 0; yy < size; yy++) + { + for (int zz = 0; zz < size; zz++) + { + if ((xx != 0 && xx != size - 1) && (yy != 0 && yy != size - 1) && (zz != 0 && zz != size - 1)) continue; + + double xd = xx / (size - 1.0f) * 2 - 1; + double yd = yy / (size - 1.0f) * 2 - 1; + double zd = zz / (size - 1.0f) * 2 - 1; + double d = sqrt(xd * xd + yd * yd + zd * zd); + + xd /= d; + yd /= d; + zd /= d; + + float remainingPower = r * (0.7f + level->random->nextFloat() * 0.6f); + double xp = x; + double yp = y; + double zp = z; + + float stepSize = 0.3f; + while (remainingPower > 0) + { + int xt = Mth::floor(xp); + int yt = Mth::floor(yp); + int zt = Mth::floor(zp); + int t = level->getTile(xt, yt, zt); + if (t > 0) + { + remainingPower -= (Tile::tiles[t]->getExplosionResistance(source) + 0.3f) * stepSize; + } + if (remainingPower > 0) + { + toBlow.insert(TilePos(xt, yt, zt)); + } + + xp += xd * stepSize; + yp += yd * stepSize; + zp += zd * stepSize; + remainingPower -= stepSize * 0.75f; + } + // if (xd*xd+yd*yd+zd*zd>1) continue; + } + } + } + + r *= 2.0f; + int x0 = Mth::floor(x - r - 1); + int x1 = Mth::floor(x + r + 1); + int y0 = Mth::floor(y - r - 1); + int y1 = Mth::floor(y + r + 1); + int z0 = Mth::floor(z - r - 1); + int z1 = Mth::floor(z + r + 1); + + // Fix for 360 #123866 - [CRASH] TU13: Code: Compliance: Placing the TNT next to Ender Crystals will crash the title after a certain amount of time. + // If we explode something next to an EnderCrystal then it creates a new explosion that overwrites the shared vector in the level + // So copy it here instead of directly using the shared one + vector > *levelEntities = level->getEntities(source, AABB::newTemp(x0, y0, z0, x1, y1, z1)); + vector > entities(levelEntities->begin(), levelEntities->end() ); + Vec3 *center = Vec3::newTemp(x, y, z); + + AUTO_VAR(itEnd, entities.end()); + for (AUTO_VAR(it, entities.begin()); it != itEnd; it++) + { + shared_ptr e = *it; //entities->at(i); + + // 4J Stu - If the entity is not in a block that would be blown up, then they should not be damaged + // Fix for #46606 - TU5: Content: Gameplay: The player can be damaged and killed by explosions behind obsidian walls + bool canDamage = false; + for(AUTO_VAR(it2, toBlow.begin()); it2 != toBlow.end(); ++it2) + { + if(e->bb->intersects(it2->x,it2->y,it2->z,it2->x + 1,it2->y + 1,it2->z + 1)) + { + canDamage = true; + break; + } + } + + double dist = e->distanceTo(x, y, z) / r; + if (dist <= 1) + { + double xa = e->x - x; + double ya = e->y + e->getHeadHeight() - y; + double za = e->z - z; + + double da = sqrt(xa * xa + ya * ya + za * za); + + // 4J Stu - Added this check to remove divide by zero errors (or rather the issues caused by + // the values being set to NaN and used in comparisons a bit later on e.g. fireball) + if( da == 0 ) + { + xa = ya = za = 0.0; + } + else + { + xa /= da; + ya /= da; + za /= da; + } + + double sp = level->getSeenPercent(center, e->bb); + double pow = (1 - dist) * sp; + if(canDamage) e->hurt(DamageSource::explosion, (int) ((pow * pow + pow) / 2 * 8 * r + 1)); + + double push = pow; + e->xd += xa * push; + e->yd += ya * push; + e->zd += za * push; + + shared_ptr player = dynamic_pointer_cast(e); + if (player != NULL) + { + //app.DebugPrintf("Adding player knockback (%f,%f,%f)\n", xa * pow, ya * pow, za * pow); + hitPlayers.insert( playerVec3Map::value_type( player, Vec3::newPermanent(xa * pow, ya * pow, za * pow))); + } + } + } + r = oR; +} + + +void Explosion::finalizeExplosion(bool generateParticles, vector *toBlowDirect/*=NULL*/) // 4J - added toBlowDirect parameter +{ + level->playSound(x, y, z, eSoundType_RANDOM_EXPLODE, 4, (1 + (level->random->nextFloat() - level->random->nextFloat()) * 0.2f) * 0.7f); + level->addParticle(eParticleType_hugeexplosion, x, y, z, 0, 0, 0); + + // 4J - use pointer to vector directly passed in if this is available - used to speed up calling this from an incoming packet + vector *toBlowArray = toBlowDirect ? toBlowDirect : new vector( toBlow.begin(), toBlow.end() ); + //toBlowArray.addAll(toBlow); + // TODO 4J Stu - Reverse iterator + PIXBeginNamedEvent(0,"Finalizing explosion size %d",toBlow.size()); + app.DebugPrintf("Finalizing explosion size %d\n",toBlow.size()); + static const int MAX_EXPLODE_PARTICLES = 50; + // 4J - try and make at most MAX_EXPLODE_PARTICLES pairs of particles + int fraction = (int)toBlowArray->size() / MAX_EXPLODE_PARTICLES; + if( fraction == 0 ) fraction = 1; + size_t j = toBlowArray->size() - 1; + //for (size_t j = toBlowArray->size() - 1; j >= 0; j--) + for(AUTO_VAR(it,toBlowArray->rbegin()); it != toBlowArray->rend(); ++it) + { + TilePos *tp = &(*it); //&toBlowArray->at(j); + int xt = tp->x; + int yt = tp->y; + int zt = tp->z; + // if (xt >= 0 && yt >= 0 && zt >= 0 && xt < width && yt < depth && + // zt < height) { + int t = level->getTile(xt, yt, zt); + + if (generateParticles) + { + if( ( j % fraction ) == 0 ) + { + double xa = xt + level->random->nextFloat(); + double ya = yt + level->random->nextFloat(); + double za = zt + level->random->nextFloat(); + + double xd = xa - x; + double yd = ya - y; + double zd = za - z; + + double dd = sqrt(xd * xd + yd * yd + zd * zd); + + xd /= dd; + yd /= dd; + zd /= dd; + + double speed = 0.5 / (dd / r + 0.1); + speed *= (level->random->nextFloat() * level->random->nextFloat() + 0.3f); + xd *= speed; + yd *= speed; + zd *= speed; + + level->addParticle(eParticleType_explode, (xa + x * 1) / 2, (ya + y * 1) / 2, (za + z * 1) / 2, xd, yd, zd); + level->addParticle(eParticleType_smoke, xa, ya, za, xd, yd, zd); + } + } + + if (t > 0) + { + Tile::tiles[t]->spawnResources(level, xt, yt, zt, level->getData(xt, yt, zt), 0.3f, 0); + level->setTile(xt, yt, zt, 0); + Tile::tiles[t]->wasExploded(level, xt, yt, zt); + } + // } + + --j; + } + + if (fire) + { + //for (size_t j = toBlowArray->size() - 1; j >= 0; j--) + for(AUTO_VAR(it,toBlowArray->rbegin()); it != toBlowArray->rend(); ++it) + { + TilePos *tp = &(*it); //&toBlowArray->at(j); + int xt = tp->x; + int yt = tp->y; + int zt = tp->z; + int t = level->getTile(xt, yt, zt); + int b = level->getTile(xt, yt - 1, zt); + if (t == 0 && Tile::solid[b] && random->nextInt(3) == 0) + { + level->setTile(xt, yt, zt, Tile::fire_Id); + } + } + } + + PIXEndNamedEvent(); + if( toBlowDirect == NULL ) delete toBlowArray; +} + +Explosion::playerVec3Map *Explosion::getHitPlayers() +{ + return &hitPlayers; +} + +Vec3 *Explosion::getHitPlayerKnockback( shared_ptr player ) +{ + AUTO_VAR(it, hitPlayers.find(player)); + + if(it == hitPlayers.end() ) return Vec3::newTemp(0.0,0.0,0.0); + + return it->second; +} \ No newline at end of file diff --git a/Minecraft.World/Explosion.h b/Minecraft.World/Explosion.h new file mode 100644 index 00000000..c06960d2 --- /dev/null +++ b/Minecraft.World/Explosion.h @@ -0,0 +1,42 @@ +#pragma once +#include "TilePos.h" +#include "Player.h" + +class Random; +class Level; + +class Explosion +{ +public: + bool fire; + bool destroyBlocks; + +private: + int size; + + Random *random; + Level *level; + +public: + double x, y, z; + shared_ptr source; + float r; + + unordered_set toBlow; + +private: + typedef unordered_map, Vec3 * , PlayerKeyHash, PlayerKeyEq> playerVec3Map; + playerVec3Map hitPlayers; + +public: + Explosion(Level *level, shared_ptr source, double x, double y, double z, float r); + ~Explosion(); + +public: + void explode(); + +public: + void finalizeExplosion(bool generateParticles, vector *toBlowDirect = NULL); // 4J - added toBlow parameter + playerVec3Map *getHitPlayers(); + Vec3 *getHitPlayerKnockback( shared_ptr player ); +}; \ No newline at end of file diff --git a/Minecraft.World/ExtremeHillsBiome.cpp b/Minecraft.World/ExtremeHillsBiome.cpp new file mode 100644 index 00000000..7e62c7d3 --- /dev/null +++ b/Minecraft.World/ExtremeHillsBiome.cpp @@ -0,0 +1,30 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "ExtremeHillsBiome.h" + +ExtremeHillsBiome::ExtremeHillsBiome(int id) : Biome(id) +{ + friendlies.clear(); +} + +void ExtremeHillsBiome::decorate(Level *level, Random *random, int xo, int zo) { + Biome::decorate(level, random, xo, zo); + + if (GENERATE_EMERALD_ORE) + { + int emeraldCount = 3 + random->nextInt(6); + for (int d = 0; d < emeraldCount; d++) + { + int x = xo + random->nextInt(16); + int y = random->nextInt((Level::genDepth / 4) - 4) + 4; + int z = zo + random->nextInt(16); + int tile = level->getTile(x, y, z); + if (tile == Tile::rock_Id) + { + level->setTileNoUpdate(x, y, z, Tile::emeraldOre_Id); + } + } + } + +} diff --git a/Minecraft.World/ExtremeHillsBiome.h b/Minecraft.World/ExtremeHillsBiome.h new file mode 100644 index 00000000..faafa66e --- /dev/null +++ b/Minecraft.World/ExtremeHillsBiome.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Biome.h" + +class ExtremeHillsBiome : public Biome +{ + friend class Biome; +private: + static const bool GENERATE_EMERALD_ORE = true; + +protected: + ExtremeHillsBiome(int id); + +public: + void decorate(Level *level, Random *random, int xo, int zo); +}; \ No newline at end of file diff --git a/Minecraft.World/EyeOfEnderSignal.cpp b/Minecraft.World/EyeOfEnderSignal.cpp new file mode 100644 index 00000000..39d9f82e --- /dev/null +++ b/Minecraft.World/EyeOfEnderSignal.cpp @@ -0,0 +1,206 @@ +#include "stdafx.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.item.h" +#include "SharedConstants.h" +#include "JavaMath.h" +#include "EyeOfEnderSignal.h" + + + +void EyeOfEnderSignal::_init() +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // Initialisors + shakeTime = 0; + tx = ty = tz = 0.0; + life = 0; + surviveAfterDeath = false; +} + + +EyeOfEnderSignal::EyeOfEnderSignal(Level *level) : Entity(level) +{ + _init(); + setSize(0.25f, 0.25f); +} + +void EyeOfEnderSignal::defineSynchedData() +{ +} + + +bool EyeOfEnderSignal::shouldRenderAtSqrDistance(double distance) +{ + double size = bb->getSize() * 4; + size *= 64.0f; + return distance < size * size; +} + +EyeOfEnderSignal::EyeOfEnderSignal(Level *level, double x, double y, double z) : Entity(level) +{ + _init(); + life = 0; + + setSize(0.25f, 0.25f); + + this->setPos(x, y, z); + this->heightOffset = 0; +} + +void EyeOfEnderSignal::signalTo(double tx, int ty, double tz) +{ + + + double dx = tx - x, dz = tz - z; + float dist = sqrt(dx * dx + dz * dz); + + if (dist > 12) + { + this->tx = x + (dx / dist) * 12; + this->tz = z + (dz / dist) * 12; + this->ty = y + 8; + } + else + { + this->tx = tx; + this->ty = ty; + this->tz = tz; + } + + life = 0; + surviveAfterDeath = random->nextInt(5) > 0; +} + +void EyeOfEnderSignal::lerpMotion(double xd, double yd, double zd) +{ + this->xd = xd; + this->yd = yd; + this->zd = zd; + if (xRotO == 0 && yRotO == 0) + { + float sd = (float) sqrt(xd * xd + zd * zd); + yRotO = this->yRot = (float) (atan2(xd, zd) * 180 / PI); + xRotO = this->xRot = (float) (atan2(yd, (double)sd) * 180 / PI); + } +} + +void EyeOfEnderSignal::tick() +{ + xOld = x; + yOld = y; + zOld = z; + Entity::tick(); + + x += xd; + y += yd; + z += zd; + + float sd = (float) sqrt(xd * xd + zd * zd); + yRot = (float) (atan2(xd, zd) * 180 / PI); + xRot = (float) (atan2(yd, (double)sd) * 180 / PI); + + while (xRot - xRotO < -180) + xRotO -= 360; + while (xRot - xRotO >= 180) + xRotO += 360; + + while (yRot - yRotO < -180) + yRotO -= 360; + while (yRot - yRotO >= 180) + yRotO += 360; + + xRot = xRotO + (xRot - xRotO) * 0.2f; + yRot = yRotO + (yRot - yRotO) * 0.2f; + + if (!level->isClientSide) + { + double dx = tx - x, dz = tz - z; + float tdist = (float) sqrt(dx * dx + dz * dz); + float angle = (float) atan2(dz, dx); + double tspeed = (sd + (tdist - sd) * .0025); + if (tdist < 1) + { + tspeed *= .8; + yd *= .8; + } + xd = cos(angle) * tspeed; + zd = sin(angle) * tspeed; + + if (y < ty) + { + yd = yd + (1 - yd) * .015f; + } + else + { + yd = yd + (-1 - yd) * .015f; + } + + } + + float s = 1 / 4.0f; + if (isInWater()) + { + for (int i = 0; i < 4; i++) + { + level->addParticle(eParticleType_bubble, x - xd * s, y - yd * s, z - zd * s, xd, yd, zd); + } + } + else + { + level->addParticle(eParticleType_ender, x - xd * s + random->nextDouble() * .6 - .3, y - yd * s - .5, z - zd * s + random->nextDouble() * .6 - .3, xd, yd, zd); + } + + if (!level->isClientSide) + { + setPos(x, y, z); + + + life++; + if (life > SharedConstants::TICKS_PER_SECOND * 4 && !level->isClientSide) + { + remove(); + if (surviveAfterDeath) + { + level->addEntity(shared_ptr( new ItemEntity(level, x, y, z, shared_ptr(new ItemInstance(Item::eyeOfEnder))))); + } + else + { + level->levelEvent(LevelEvent::PARTICLES_EYE_OF_ENDER_DEATH, (int) Math::round(x), (int) Math::round(y), (int) Math::round(z), 0); + } + } + } +} + +void EyeOfEnderSignal::addAdditonalSaveData(CompoundTag *tag) +{ +} + +void EyeOfEnderSignal::readAdditionalSaveData(CompoundTag *tag) +{ +} + +float EyeOfEnderSignal::getShadowHeightOffs() +{ + return 0; +} + +float EyeOfEnderSignal::getBrightness(float a) +{ + return 1.0f; +} + +int EyeOfEnderSignal::getLightColor(float a) +{ + return 15 << 20 | 15 << 4; +} + +bool EyeOfEnderSignal::isAttackable() +{ + return false; +} \ No newline at end of file diff --git a/Minecraft.World/EyeOfEnderSignal.h b/Minecraft.World/EyeOfEnderSignal.h new file mode 100644 index 00000000..72d572f8 --- /dev/null +++ b/Minecraft.World/EyeOfEnderSignal.h @@ -0,0 +1,41 @@ +#pragma once + +#include "Entity.h" + +class EyeOfEnderSignal : public Entity +{ +public: + eINSTANCEOF GetType() { return eTYPE_EYEOFENDERSIGNAL; } + static Entity *create(Level *level) { return new EyeOfEnderSignal(level); } + +public: + int shakeTime; + +private: + double tx, ty, tz; + int life; + bool surviveAfterDeath; + + void _init(); + +public: + EyeOfEnderSignal(Level *level); + +protected: + virtual void defineSynchedData(); + +public: + virtual bool shouldRenderAtSqrDistance(double distance); + + EyeOfEnderSignal(Level *level, double x, double y, double z); + + void signalTo(double tx, int ty, double tz); + virtual void lerpMotion(double xd, double yd, double zd); + virtual void tick(); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual float getShadowHeightOffs(); + virtual float getBrightness(float a); + virtual int getLightColor(float a); + virtual bool isAttackable(); +}; \ No newline at end of file diff --git a/Minecraft.World/Facing.cpp b/Minecraft.World/Facing.cpp new file mode 100644 index 00000000..9df4187d --- /dev/null +++ b/Minecraft.World/Facing.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "Facing.h" + +const int Facing::OPPOSITE_FACING[6] = +{ + UP, DOWN, SOUTH, NORTH, EAST, WEST +}; + +const int Facing::STEP_X[6] = +{ + 0, 0, 0, 0, -1, 1 +}; + +const int Facing::STEP_Y[6] = +{ + -1, 1, 0, 0, 0, 0 +}; + +const int Facing::STEP_Z[6] = +{ + 0, 0, -1, 1, 0, 0 +}; diff --git a/Minecraft.World/Facing.h b/Minecraft.World/Facing.h new file mode 100644 index 00000000..1c4cfdf3 --- /dev/null +++ b/Minecraft.World/Facing.h @@ -0,0 +1,17 @@ +#pragma once + +class Facing +{ +public: + static const int DOWN = 0; + static const int UP = 1; + static const int NORTH = 2; + static const int SOUTH = 3; + static const int WEST = 4; + static const int EAST = 5; + + static const int OPPOSITE_FACING[6]; + static const int STEP_X[6]; + static const int STEP_Y[6]; + static const int STEP_Z[6]; +}; \ No newline at end of file diff --git a/Minecraft.World/FallingTile.cpp b/Minecraft.World/FallingTile.cpp new file mode 100644 index 00000000..254631ff --- /dev/null +++ b/Minecraft.World/FallingTile.cpp @@ -0,0 +1,240 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.damagesource.h" +#include "com.mojang.nbt.h" +#include "FallingTile.h" + + + +// 4J - added for common ctor code +void FallingTile::_init() +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + tile = 0; + data = 0; + time = 0; + dropItem = true; + + cancelDrop = false; + hurtEntities = false; + fallDamageMax = 40; + fallDamageAmount = 2; + + // 4J Added so that client-side falling tiles can fall through blocks + // This fixes a bug on the host where the tile update from the server comes in before the client-side falling tile + // has reached that level, causing it to stop at one block higher. + m_ignoreVerticalCollisions = level->isClientSide; +} + +FallingTile::FallingTile(Level *level) : + Entity( level ) +{ + _init(); +} + +FallingTile::FallingTile(Level *level, double x, double y, double z, int tile, int data) : Entity( level ) +{ + _init(); + + this->tile = tile; + this->data = data; + blocksBuilding = true; + setSize(0.98f, 0.98f); + heightOffset = bbHeight / 2.0f; + setPos(x, y, z); + + xd = 0; + yd = 0; + zd = 0; + + xo = x; + yo = y; + zo = z; + + // 4J added - without this newly created falling tiles weren't interpolating their render positions correctly + xOld = x; + yOld = y; + zOld = z; +} + +bool FallingTile::makeStepSound() +{ + return false; +} + +void FallingTile::defineSynchedData() +{ +} + +bool FallingTile::isPickable() +{ + return !removed; +} + +void FallingTile::tick() +{ + if (tile == 0) + { + remove(); + return; + } + + xo = x; + yo = y; + zo = z; + time++; + + yd -= 0.04f; + move(xd, yd, zd); + xd *= 0.98f; + yd *= 0.98f; + zd *= 0.98f; + + if(!level->isClientSide) + { + + int xt = Mth::floor(x); + int yt = Mth::floor(y); + int zt = Mth::floor(z); + if(time == 1) + { + if (level->getTile(xt, yt, zt) == tile) + { + level->setTile(xt, yt, zt, 0); + } + else + { + remove(); + return; + } + } + + if (onGround) + { + xd *= 0.7f; + zd *= 0.7f; + yd *= -0.5f; + + // if (HeavyTile.isFree(level, xt, yt, zt)) { + if (level->getTile(xt, yt, zt) != Tile::pistonMovingPiece_Id) + { + remove(); + if (!cancelDrop && level->mayPlace(tile, xt, yt, zt, true, 1, nullptr) && !HeavyTile::isFree(level, xt, yt - 1, zt) && level->setTileAndData(xt, yt, zt, tile, data)) + { + HeavyTile *hv = dynamic_cast(Tile::tiles[tile]); + if (hv) + { + hv->onLand(level, xt, yt, zt, data); + } + } + else + { + if(dropItem && !cancelDrop) spawnAtLocation( shared_ptr(new ItemInstance(tile, 1, Tile::tiles[tile]->getSpawnResourcesAuxValue(data))), 0); + } + } + } + else if ( (time > 20 * 5 && !level->isClientSide && (yt < 1 || yt > Level::maxBuildHeight)) || (time > 20 * 30)) + { + if(dropItem) spawnAtLocation(tile, 1); + remove(); + } + } +} + +void FallingTile::causeFallDamage(float distance) +{ + if (hurtEntities) + { + int dmg = Mth::ceil(distance - 1); + if (dmg > 0) + { + vector > *entities = level->getEntities(shared_from_this(), bb); + DamageSource *source = tile == Tile::anvil_Id ? DamageSource::anvil : DamageSource::fallingBlock; + + //for (Entity entity : entities) + for(AUTO_VAR(it,entities->begin()); it != entities->end(); ++it) + { + (*it)->hurt(source, min(Mth::floor(dmg * fallDamageAmount), fallDamageMax)); + } + if (tile == Tile::anvil_Id && random->nextFloat() < 0.05f + (dmg * 0.05)) + { + int damage = data >> 2; + int dir = data & 3; + + if (++damage > 2) + { + cancelDrop = true; + } + else + { + data = dir | (damage << 2); + } + } + } + } +} + +void FallingTile::addAdditonalSaveData(CompoundTag *tag) +{ + tag->putByte(L"Tile", (byte) tile); + tag->putByte(L"Data", (byte) data); + tag->putByte(L"Time", (byte) time); + tag->putBoolean(L"DropItem", dropItem); + tag->putBoolean(L"HurtEntities", hurtEntities); + tag->putFloat(L"FallHurtAmount", fallDamageAmount); + tag->putInt(L"FallHurtMax", fallDamageMax); +} + +void FallingTile::readAdditionalSaveData(CompoundTag *tag) +{ + tile = tag->getByte(L"Tile") & 0xff; + data = tag->getByte(L"Data") & 0xff; + time = tag->getByte(L"Time") & 0xff; + + if (tag->contains(L"HurtEntities")) + { + hurtEntities = tag->getBoolean(L"HurtEntities"); + fallDamageAmount = tag->getFloat(L"FallHurtAmount"); + fallDamageMax = tag->getInt(L"FallHurtMax"); + } + else if (tile == Tile::anvil_Id) + { + hurtEntities = true; + } + + if (tag->contains(L"DropItem")) + { + dropItem = tag->getBoolean(L"DropItem"); + } + + if (tile == 0) + { + tile = Tile::sand_Id; + } +} + + +float FallingTile::getShadowHeightOffs() +{ + return 0; +} + +Level *FallingTile::getLevel() +{ + return level; +} + +void FallingTile::setHurtsEntities(bool value) +{ + this->hurtEntities = value; +} + +bool FallingTile::displayFireAnimation() +{ + return false; +} \ No newline at end of file diff --git a/Minecraft.World/FallingTile.h b/Minecraft.World/FallingTile.h new file mode 100644 index 00000000..eefc9e3b --- /dev/null +++ b/Minecraft.World/FallingTile.h @@ -0,0 +1,49 @@ +#pragma once +#include "Entity.h" + +class Level; + +class FallingTile : public Entity +{ +public: + eINSTANCEOF GetType() { return eTYPE_FALLINGTILE;} + static Entity *create(Level *level) { return new FallingTile(level); } + + static const int serialVersionUID = 0; + int tile; + int data; + int time; + bool dropItem; + +private: + bool cancelDrop; + bool hurtEntities; + int fallDamageMax ; + float fallDamageAmount; + + // 4J - added for common ctor code + void _init(); + +public: + FallingTile(Level *level); + FallingTile(Level *level, double x, double y, double z, int tile, int data = 0); + +protected: + virtual bool makeStepSound(); + virtual void defineSynchedData(); + +public: + virtual bool isPickable(); + virtual void tick(); + +protected: + void causeFallDamage(float distance); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +public: + virtual float getShadowHeightOffs(); + Level *getLevel(); + void setHurtsEntities(bool value); + bool displayFireAnimation(); +}; diff --git a/Minecraft.World/FarmTile.cpp b/Minecraft.World/FarmTile.cpp new file mode 100644 index 00000000..ecfaf44b --- /dev/null +++ b/Minecraft.World/FarmTile.cpp @@ -0,0 +1,147 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.h" +#include "net.minecraft.world.h" +#include "FarmTile.h" + + +FarmTile::FarmTile(int id) : Tile(id, Material::dirt,isSolidRender()) +{ + iconWet = NULL; + iconDry = NULL; + + setTicking(true); + updateDefaultShape(); + setLightBlock(255); +} + +// 4J Added override +void FarmTile::updateDefaultShape() +{ + setShape(0, 0, 0, 1, 15 / 16.0f, 1); +} + +AABB *FarmTile::getAABB(Level *level, int x, int y, int z) +{ + return AABB::newTemp(x + 0, y + 0, z + 0, x + 1, y + 1, z + 1); +} + +bool FarmTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool FarmTile::isCubeShaped() +{ + return false; +} + +Icon *FarmTile::getTexture(int face, int data) +{ + if (face == Facing::UP) + { + if(data > 0) + { + return iconWet; + } + else + { + return iconDry; + } + } + return Tile::dirt->getTexture(face); +} + +void FarmTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (isNearWater(level, x, y, z) || level->isRainingAt(x, y + 1, z)) + { + level->setData(x, y, z, 7); + } else + { + int moisture = level->getData(x, y, z); + if (moisture > 0) + { + level->setData(x, y, z, moisture - 1); + } else + { + if (!isUnderCrops(level, x, y, z)) + { + level->setTile(x, y, z, Tile::dirt_Id); + } + } + } +} + +void FarmTile::fallOn(Level *level, int x, int y, int z, shared_ptr entity, float fallDistance) +{ + // 4J Stu - Fix for #86148 - Code: Gameplay: Jumping on Farmland does not always result in turning to Dirt Block + // We should not be setting tiles on the client based on random values! + if (!level->isClientSide && level->random->nextFloat() < (fallDistance - .5f)) + { + // Fix for #60547 - TU7: Content: Gameplay: Players joining a game can destroy crops even with Trust Players option disabled. + shared_ptr player = dynamic_pointer_cast(entity); + if(player == NULL || player->isAllowedToMine()) level->setTile(x, y, z, Tile::dirt_Id); + } +} + +bool FarmTile::isUnderCrops(Level *level, int x, int y, int z) +{ + int r = 0; + for (int xx = x - r; xx <= x + r; xx++) + for (int zz = z - r; zz <= z + r; zz++) + { + int tile = level->getTile(xx, y + 1, zz); + if (tile == Tile::crops_Id || tile == Tile::melonStem_Id || tile == Tile::pumpkinStem_Id || tile == Tile::potatoes_Id || tile == Tile::carrots_Id) + { + return true; + } + } + return false; +} + +bool FarmTile::isNearWater(Level *level, int x, int y, int z) +{ + for (int xx = x - 4; xx <= x + 4; xx++) + for (int yy = y; yy <= y + 1; yy++) + for (int zz = z - 4; zz <= z + 4; zz++) + { + if (level->getMaterial(xx, yy, zz) == Material::water) + { + return true; + } + } + return false; +} + +void FarmTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + Tile::neighborChanged(level, x, y, z, type); + Material *above = level->getMaterial(x, y + 1, z); + if (above->isSolid()) + { + level->setTile(x, y, z, Tile::dirt_Id); + } +} + +bool FarmTile::blocksLight() +{ + return true; +} + +int FarmTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::dirt->getResource(0, random, playerBonusLevel); +} + +int FarmTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Tile::dirt_Id; +} + +void FarmTile::registerIcons(IconRegister *iconRegister) +{ + iconWet = iconRegister->registerIcon(L"farmland_wet"); + iconDry = iconRegister->registerIcon(L"farmland_dry"); +} diff --git a/Minecraft.World/FarmTile.h b/Minecraft.World/FarmTile.h new file mode 100644 index 00000000..f9b4b031 --- /dev/null +++ b/Minecraft.World/FarmTile.h @@ -0,0 +1,36 @@ +#pragma once +#include "Tile.h" +#include "Definitions.h" + +class Random; +class ChunkRebuildData; + +class FarmTile : public Tile +{ + friend class Tile; + friend class ChunkRebuildData; +private: + Icon *iconWet; + Icon *iconDry; + +protected: + FarmTile(int id); +public: + virtual void updateDefaultShape(); // 4J Added override + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual Icon *getTexture(int face, int data); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual void fallOn(Level *level, int x, int y, int z, shared_ptr entity, float fallDistance); +private: + virtual bool isUnderCrops(Level *level, int x, int y, int z); + virtual bool isNearWater(Level *level, int x, int y, int z); +public: + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual bool blocksLight(); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int cloneTileId(Level *level, int x, int y, int z); + //@Override + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/FastNoise.cpp b/Minecraft.World/FastNoise.cpp new file mode 100644 index 00000000..91d039b3 --- /dev/null +++ b/Minecraft.World/FastNoise.cpp @@ -0,0 +1,114 @@ +#include "stdafx.h" +#include "FastNoise.h" + +FastNoise::FastNoise(int levels) +{ + Random random; + init(&random, levels); +} + +FastNoise::FastNoise(Random *random, int levels) +{ + init(random,levels); +} + +void FastNoise::init(Random *random, int levels) +{ + this->levels = levels; + noiseMaps = new byte *[levels]; + for (int i = 0; i < levels; i++) + { + noiseMaps[i] = new byte[0x100000]; + random->nextBytes(noiseMaps[i],0x100000); + } +} + +FastNoise::~FastNoise() +{ + for( int i = 0; i < levels; i++ ) + { + delete [] noiseMaps[i]; + } + delete [] noiseMaps; +} + +doubleArray FastNoise::getRegion(doubleArray buffer, double x, double y, double z, int xSize, int ySize, int zSize, double xScale, double yScale, double zScale) +{ + if (buffer.data == NULL) buffer = doubleArray(xSize * ySize * zSize); + else for (unsigned int i = 0; i < buffer.length; i++) + buffer[i] = 0; + + + double pow = 1; + int AA = 487211441; + int BB = 21771; + for (int i = 0; i < levels; i++) + { + byte *map = noiseMaps[i]; + int pp = 0; + + for (int zp = 0; zp < zSize; zp++) + { + double zz = (z + zp) * zScale; + int Z = (int) zz; + if (zz < Z) Z -= 1; + int zl = (int) ((zz - Z) * 65536); + + for (int yp = 0; yp < ySize; yp++) + { + double yy = (y + yp) * yScale; + int Y = (int) yy; + if (yy < Y) Y -= 1; + int yl = (int) ((yy - Y) * 65536); + + for (int xp = 0; xp < xSize; xp++) + { + double xx = (x + xp) * xScale; + int X = (int) xx; + if (xx < X) X -= 1; + int xl = (int) ((xx - X) * 65536); + + int X0 = (X + 0) * AA; + int X1 = (X + 1) * AA; + + int Y0 = (Y + 0); + int Y1 = (Y + 1); + int Z0 = (Z + 0); + int Z1 = (Z + 1); + + int X0Y0 = (X0 + Y0) * BB; + int X1Y0 = (X1 + Y0) * BB; + int X0Y1 = (X0 + Y1) * BB; + int X1Y1 = (X1 + Y1) * BB; + + int a = map[(X0Y0 + Z0) & 0xfffff]; + int b = map[(X1Y0 + Z0) & 0xfffff]; + int c = map[(X0Y1 + Z0) & 0xfffff]; + int d = map[(X1Y1 + Z0) & 0xfffff]; + int e = map[(X0Y0 + Z1) & 0xfffff]; + int f = map[(X1Y0 + Z1) & 0xfffff]; + int g = map[(X0Y1 + Z1) & 0xfffff]; + int h = map[(X1Y1 + Z1) & 0xfffff]; + + int ab = a + (((b - a) * xl) >> 16); + int cd = c + (((d - c) * xl) >> 16); + int ef = e + (((f - e) * xl) >> 16); + int gh = g + (((h - g) * xl) >> 16); + + int abcd = ab + (((cd - ab) * yl) >> 16); + int efgh = ef + (((gh - ef) * yl) >> 16); + + int res = abcd + (((efgh - abcd) * zl) >> 16); + + buffer[pp++] += res*pow; + } + } + } + pow /= 2; + xScale*=2; + yScale*=2; + zScale*=2; + } + + return buffer; +} diff --git a/Minecraft.World/FastNoise.h b/Minecraft.World/FastNoise.h new file mode 100644 index 00000000..f41c32e8 --- /dev/null +++ b/Minecraft.World/FastNoise.h @@ -0,0 +1,17 @@ +#pragma once + +class FastNoise +{ +private: + byte **noiseMaps; + int levels; + +public: + FastNoise(int levels); + FastNoise(Random *random, int levels); + + void init(Random *random, int levels); + ~FastNoise(); + + doubleArray getRegion(doubleArray buffer, double x, double y, double z, int xSize, int ySize, int zSize, double xScale, double yScale, double zScale); +}; \ No newline at end of file diff --git a/Minecraft.World/Feature.cpp b/Minecraft.World/Feature.cpp new file mode 100644 index 00000000..301bd26c --- /dev/null +++ b/Minecraft.World/Feature.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "Feature.h" + + +Feature::Feature() +{ + this->doUpdate = false; +} + +Feature::Feature(bool doUpdate) +{ + this->doUpdate = doUpdate; +} + +void Feature::placeBlock(Level *level, int x, int y, int z, int tile) +{ + placeBlock(level, x, y, z, tile, 0); +} + +void Feature::placeBlock(Level *level, int x, int y, int z, int tile, int data) +{ + if (doUpdate) + { + level->setTileAndData(x, y, z, tile, data); + } + else + { + level->setTileAndDataNoUpdate(x, y, z, tile, data); + } +} diff --git a/Minecraft.World/Feature.h b/Minecraft.World/Feature.h new file mode 100644 index 00000000..9b89c0a6 --- /dev/null +++ b/Minecraft.World/Feature.h @@ -0,0 +1,20 @@ +#pragma once + +class Level; + +class Feature +{ +private: + bool doUpdate; +public: + Feature(); + Feature(bool doUpdate); + virtual ~Feature() {}; + + virtual bool place(Level *level, Random *random, int x, int y, int z) = 0; + virtual bool placeWithIndex(Level *level, Random *random, int x, int y, int z,int iIndex, int iRadius) { return false;} + virtual void init(double V1, double V2, double V3) {}; +protected: + virtual void placeBlock(Level *level, int x, int y, int z, int tile); + virtual void placeBlock(Level *level, int x, int y, int z, int tile, int data); +}; diff --git a/Minecraft.World/FenceGateTile.cpp b/Minecraft.World/FenceGateTile.cpp new file mode 100644 index 00000000..6b0243ed --- /dev/null +++ b/Minecraft.World/FenceGateTile.cpp @@ -0,0 +1,147 @@ +#include "stdafx.h" +#include "FenceGateTile.h" +#include "AABB.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.h" +#include "LevelEvent.h" + +FenceGateTile::FenceGateTile(int id) : DirectionalTile(id, Material::wood, isSolidRender() ) +{ +} + +Icon *FenceGateTile::getTexture(int face, int data) +{ + return Tile::wood->getTexture(face); +} + +bool FenceGateTile::mayPlace(Level *level, int x, int y, int z) +{ + if (!level->getMaterial(x, y - 1, z)->isSolid()) return false; + return Tile::mayPlace(level, x, y, z); +} + +AABB *FenceGateTile::getAABB(Level *level, int x, int y, int z) +{ + int data = level->getData(x, y, z); + if (isOpen(data)) + { + return NULL; + } + // 4J Brought forward change from 1.2.3 to fix hit box rotation + if (data == Direction::NORTH || data == Direction::SOUTH) + { + return AABB::newTemp(x, y, z + 6.0f / 16.0f, x + 1, y + 1.5f, z + 10.0f / 16.0f); + } + return AABB::newTemp(x + 6.0f / 16.0f, y, z, x + 10.0f / 16.0f, y + 1.5f, z + 1); + //return AABB::newTemp(x, y, z, x + 1, y + 1.5f, z + 1); +} + +// 4J - Brought forward from 1.2.3 to fix hit box rotation +void FenceGateTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + int data = getDirection(level->getData(x, y, z)); + if (data == Direction::NORTH || data == Direction::SOUTH) + { + setShape(0, 0, 6.0f / 16.0f, 1, 1.0f, 10.0f / 16.0f); + } + else + { + setShape(6.0f / 16.0f, 0, 0, 10.0f / 16.0f, 1.0f, 1); + } +} + +bool FenceGateTile::blocksLight() +{ + return false; +} + +bool FenceGateTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool FenceGateTile::isCubeShaped() +{ + return false; +} + +bool FenceGateTile::isPathfindable(LevelSource *level, int x, int y, int z) +{ + return isOpen(level->getData(x, y, z)); +} + +int FenceGateTile::getRenderShape() +{ + return Tile::SHAPE_FENCE_GATE; +} + +void FenceGateTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int dir = (((Mth::floor(by->yRot * 4 / (360) + 0.5)) & 3)) % 4; + level->setData(x, y, z, dir); +} + +bool FenceGateTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if( soundOnly ) + { + // 4J - added - just do enough to play the sound + level->levelEvent(player, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); // 4J - changed event to pass player rather than NULL as the source of the event so we can filter the broadcast properly + return false; + } + + int data = level->getData(x, y, z); + if (isOpen(data)) + { + level->setData(x, y, z, data & ~OPEN_BIT); + } + else + { + // open the door from the player + int dir = (((Mth::floor(player->yRot * 4 / (360) + 0.5)) & 3)) % 4; + int current = getDirection(data); + if (current == ((dir + 2) % 4)) { + data = dir; + } + level->setData(x, y, z, data | OPEN_BIT); + } + level->levelEvent(player, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); + return true; +} + +void FenceGateTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (level->isClientSide) return; + + int data = level->getData(x, y, z); + + bool signal = level->hasNeighborSignal(x, y, z); + if (signal || ((type > 0 && Tile::tiles[type]->isSignalSource()) || type == 0)) + { + if (signal && !isOpen(data)) + { + level->setData(x, y, z, data | OPEN_BIT); + level->levelEvent(nullptr, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); + } + else if (!signal && isOpen(data)) + { + level->setData(x, y, z, data & ~OPEN_BIT); + level->levelEvent(nullptr, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); + } + } +} + +bool FenceGateTile::isOpen(int data) +{ + return (data & OPEN_BIT) != 0; +} + +void FenceGateTile::registerIcons(IconRegister *iconRegister) +{ + // None +} + +bool FenceGateTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/FenceGateTile.h b/Minecraft.World/FenceGateTile.h new file mode 100644 index 00000000..277fcd4d --- /dev/null +++ b/Minecraft.World/FenceGateTile.h @@ -0,0 +1,26 @@ +#pragma once +#include "DirectionalTile.h" + +class FenceGateTile : public DirectionalTile +{ +private: + static const int OPEN_BIT = 4; + +public: + FenceGateTile(int id); + Icon *getTexture(int face, int data); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param // Brought forward from 1.2.3 + virtual bool blocksLight(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual bool isPathfindable(LevelSource *level, int x, int y, int z); + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + virtual int getRenderShape(); + virtual void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + static bool isOpen(int data); + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/FenceTile.cpp b/Minecraft.World/FenceTile.cpp new file mode 100644 index 00000000..0d1f9bc4 --- /dev/null +++ b/Minecraft.World/FenceTile.cpp @@ -0,0 +1,132 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.h" +#include "FenceTile.h" + +FenceTile::FenceTile(int id, const wstring &texture, Material *material) : Tile( id, material, isSolidRender()) +{ + this->texture = texture; +} + +AABB *FenceTile::getAABB(Level *level, int x, int y, int z) +{ + bool n = connectsTo(level, x, y, z - 1); + bool s = connectsTo(level, x, y, z + 1); + bool w = connectsTo(level, x - 1, y, z); + bool e = connectsTo(level, x + 1, y, z); + + float west = 6.0f / 16.0f; + float east = 10.0f / 16.0f; + float north = 6.0f / 16.0f; + float south = 10.0f / 16.0f; + + if (n) + { + north = 0; + } + if (s) + { + south = 1; + } + if (w) + { + west = 0; + } + if (e) + { + east = 1; + } + + return AABB::newTemp(x + west, y, z + north, x + east, y + 1.5f, z + south); +} + +void FenceTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + bool n = connectsTo(level, x, y, z - 1); + bool s = connectsTo(level, x, y, z + 1); + bool w = connectsTo(level, x - 1, y, z); + bool e = connectsTo(level, x + 1, y, z); + + float west = 6.0f / 16.0f; + float east = 10.0f / 16.0f; + float north = 6.0f / 16.0f; + float south = 10.0f / 16.0f; + + if (n) + { + north = 0; + } + if (s) + { + south = 1; + } + if (w) + { + west = 0; + } + if (e) + { + east = 1; + } + + setShape(west, 0, north, east, 1.0f, south); +} + +bool FenceTile::blocksLight() +{ + return false; +} + +bool FenceTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool FenceTile::isCubeShaped() +{ + return false; +} + +bool FenceTile::isPathfindable(LevelSource *level, int x, int y, int z) +{ + return false; +} + +int FenceTile::getRenderShape() +{ + return Tile::SHAPE_FENCE; +} + +bool FenceTile::connectsTo(LevelSource *level, int x, int y, int z) +{ + int tile = level->getTile(x, y, z); + if (tile == id || tile == Tile::fenceGate_Id) + { + return true; + } + Tile *tileInstance = Tile::tiles[tile]; + if (tileInstance != NULL) + { + if (tileInstance->material->isSolidBlocking() && tileInstance->isCubeShaped()) + { + return tileInstance->material != Material::vegetable; + } + } + return false; +} + +bool FenceTile::isFence(int tile) +{ + return tile == Tile::fence_Id || tile == Tile::netherFence_Id; +} + +void FenceTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(texture); +} + +bool FenceTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/FenceTile.h b/Minecraft.World/FenceTile.h new file mode 100644 index 00000000..5b61fca4 --- /dev/null +++ b/Minecraft.World/FenceTile.h @@ -0,0 +1,23 @@ +#pragma once +#include "Tile.h" +#include "Definitions.h" + +class FenceTile : public Tile +{ +private: + wstring texture; + +public: + FenceTile(int id, const wstring &texture, Material *material); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual bool blocksLight(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual bool isPathfindable(LevelSource *level, int x, int y, int z); + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + virtual int getRenderShape(); + bool connectsTo(LevelSource *level, int x, int y, int z); + static bool isFence(int tile); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/File.cpp b/Minecraft.World/File.cpp new file mode 100644 index 00000000..ae0cf96c --- /dev/null +++ b/Minecraft.World/File.cpp @@ -0,0 +1,702 @@ +#include "stdafx.h" +#include "FileFilter.h" +#include "McRegionLevelStorageSource.h" +#include "File.h" + +#ifdef __PS3__ +#include +#endif +#ifdef __PSVITA__ +#include +#endif + +const wchar_t File::pathSeparator = L'\\'; +#ifdef _XBOX +const wstring File::pathRoot = L"GAME:"; // Path root after pathSeparator has been removed +#else +const wstring File::pathRoot = L""; // Path root after pathSeparator has been removed +#endif + +//Creates a new File instance from a parent abstract pathname and a child pathname string. +File::File( const File &parent, const wstring& child ) +{ + m_abstractPathName = parent.getPath() + pathSeparator + child; +} + +//Creates a new File instance by converting the given pathname string into an abstract pathname. +File::File( const wstring& pathname ) //: parent( NULL ) +{ + // #ifndef _CONTENT_PACKAGE + // char buf[256]; + // wcstombs(buf, pathname.c_str(), 256); + // printf("File::File - %s\n",buf); + // #endif + if( pathname.empty() ) + m_abstractPathName = wstring( L"" ); + else + m_abstractPathName = pathname; + +#ifdef _WINDOWS64 + string path = wstringtofilename(m_abstractPathName); + string finalPath = StorageManager.GetMountedPath(path.c_str()); + if(finalPath.size() == 0) finalPath = path; + m_abstractPathName = convStringToWstring(finalPath); +#elif defined(_DURANGO) + wstring finalPath = StorageManager.GetMountedPath(m_abstractPathName.c_str()); + if(finalPath.size() == 0) finalPath = m_abstractPathName; + m_abstractPathName = finalPath; +#endif + /* + vector path = stringSplit( pathname, pathSeparator ); + + if( path.back().compare( pathRoot ) != 0 ) + m_abstractPathName = path.back(); + else + m_abstractPathName = L""; + + path.pop_back(); + + if( path.size() > 0 ) + { + // If the last member of the vector is the root then just stop + if( path.back().compare( pathRoot ) != 0 ) + this->parent = new File( &path ); + else + this->parent = NULL; + } + */ +} + +File::File( const wstring& parent, const wstring& child ) //: m_abstractPathName( child ) +{ + m_abstractPathName = pathRoot + pathSeparator + parent + pathSeparator + child; + //this->parent = new File( parent ); +} + +//Creates a new File instance by converting the given path vector into an abstract pathname. +/* +File::File( vector *path ) : parent( NULL ) +{ +m_abstractPathName = path->back(); +path->pop_back(); + +if( path->size() > 0 ) +{ +// If the last member of the vector is the root then just stop +if( path->back().compare( pathRoot ) != 0 ) +this->parent = new File( path ); +else +this->parent = NULL; +} +} +*/ + +//Deletes the file or directory denoted by this abstract pathname. If this pathname denotes a directory, +//then the directory must be empty in order to be deleted. +//Returns: +//true if and only if the file or directory is successfully deleted; false otherwise +bool File::_delete() +{ +#ifdef _UNICODE + BOOL result = DeleteFile( getPath().c_str() ); +#else + BOOL result = DeleteFile( wstringtofilename(getPath()) ); +#endif + if( result == 0 ) + { + DWORD error = GetLastError(); +#ifndef _CONTENT_PACKAGE + printf( "File::_delete - Error code %d (%#0.8X)\n", error, error ); +#endif + return false; + } + else + return true; +} + +//Creates the directory named by this abstract pathname. +//Returns: +//true if and only if the directory was created; false otherwise +bool File::mkdir() const +{ +#ifdef _UNICODE + return CreateDirectory( getPath().c_str(), NULL) != 0; + +#else + return CreateDirectory( wstringtofilename(getPath()), NULL) != 0; +#endif +} + + +//Creates the directory named by this abstract pathname, including any +//necessary but nonexistent parent directories. Note that if this +//operation fails it may have succeeded in creating some of the necessary +//parent directories. +// +//@return true if and only if the directory was created, +// along with all necessary parent directories; false +// otherwise +// +//@throws SecurityException +// If a security manager exists and its {@link +// java.lang.SecurityManager#checkRead(java.lang.String)} +// method does not permit verification of the existence of the +// named directory and all necessary parent directories; or if +// the {@link +// java.lang.SecurityManager#checkWrite(java.lang.String)} +// method does not permit the named directory and all necessary +// parent directories to be created +// +bool File::mkdirs() const +{ + vector path = stringSplit( m_abstractPathName, pathSeparator ); + + wstring pathToHere = L""; + AUTO_VAR(itEnd, path.end()); + for( AUTO_VAR(it, path.begin()); it != itEnd; it++ ) + { + // If this member of the vector is the root then just skip to the next + if( pathRoot.compare( *it ) == 0 ) + { + pathToHere = *it; + continue; + } + + pathToHere = pathToHere + pathSeparator + *it; + + // if not exists +#ifdef _UNICODE + if( GetFileAttributes( pathToHere.c_str() ) == -1 ) + { + DWORD result = CreateDirectory( pathToHere.c_str(), NULL); + if( result == 0 ) + { + // Failed to create + return false; + } + } +#else + if( GetFileAttributes( wstringtofilename(pathToHere) ) == -1 ) + { + DWORD result = CreateDirectory( wstringtofilename(pathToHere), NULL); + if( result == 0 ) + { + // Failed to create + return false; + } + } +#endif + } + + // We should now exist + assert( exists() ); + + return true; +} + +/* +File *File::getParent() const +{ +return (File *) parent; +} +*/ + +//Tests whether the file or directory denoted by this abstract pathname exists. +//Returns: +//true if and only if the file or directory denoted by this abstract pathname exists; false otherwise +bool File::exists() const +{ + // TODO 4J Stu - Possible we could get an error result from something other than the file not existing? +#ifdef _UNICODE + return GetFileAttributes( getPath().c_str() ) != -1; + +#else + return GetFileAttributes( wstringtofilename(getPath()) ) != -1; +#endif +} + +//Tests whether the file denoted by this abstract pathname is a normal file. A file is normal if it is not a directory and, +//in addition, satisfies other system-dependent criteria. Any non-directory file created by a Java application is guaranteed to be a normal file. +//Returns: +//true if and only if the file denoted by this abstract pathname exists and is a normal file; false otherwise +bool File::isFile() const +{ + return exists() && !isDirectory(); +} + +//Renames the file denoted by this abstract pathname. +//Whether or not this method can move a file from one filesystem to another is platform-dependent. +//The return value should always be checked to make sure that the rename operation was successful. +// +//Parameters: +//dest - The new abstract pathname for the named file +//Returns: +//true if and only if the renaming succeeded; false otherwise +bool File::renameTo(File dest) +{ + // 4J Stu - The wstringtofilename function returns a pointer to the same location in memory every time it is + // called, therefore we were getting sourcePath and destPath having the same value. The solution here is to + // make a copy of the sourcePath by storing it in a std::string + string sourcePath = wstringtofilename(getPath()); + const char *destPath = wstringtofilename(dest.getPath()); +#ifdef _DURANGO + __debugbreak(); // TODO + BOOL result = false; +#else + BOOL result = MoveFile(sourcePath.c_str(), destPath); +#endif + + if( result == 0 ) + { + DWORD error = GetLastError(); +#ifndef _CONTENT_PACKAGE + printf( "File::renameTo - Error code %d (%#0.8X)\n", error, error ); +#endif + return false; + } + else + return true; +} + +//Returns an array of abstract pathnames denoting the files in the directory denoted by this abstract pathname. +//If this abstract pathname does not denote a directory, then this method returns null. Otherwise an array of File objects is returned, +//one for each file or directory in the directory. Pathnames denoting the directory itself and the directory's parent directory +//are not included in the result. Each resulting abstract pathname is constructed from this abstract pathname using +//the File(File, String) constructor. Therefore if this pathname is absolute then each resulting pathname is absolute; +//if this pathname is relative then each resulting pathname will be relative to the same directory. +// +//There is no guarantee that the name strings in the resulting array will appear in any specific order; they are not, +//in particular, guaranteed to appear in alphabetical order. +// +//Returns: +//An array of abstract pathnames denoting the files and directories in the directory denoted by this abstract pathname. +//The array will be empty if the directory is empty. Returns null if this abstract pathname does not denote a directory, +//or if an I/O error occurs. +vector *File::listFiles() const +{ + vector *vOutput = new vector(); + + // TODO 4J Stu - Also need to check for I/O errors? + if( !isDirectory() ) + return vOutput; + +#ifdef __PS3__ + const char *lpFileName=wstringtofilename(getPath()); + char filePath[256]; + + std::string mountedPath = StorageManager.GetMountedPath(lpFileName); + if(mountedPath.length() > 0) + { + strcpy(filePath, mountedPath.c_str()); + } + else if(lpFileName[0] == '/') // already fully qualified path + strcpy(filePath, lpFileName ); + else + sprintf(filePath,"%s/%s",getUsrDirPath(), lpFileName ); + int fd; + CellFsErrno err = cellFsOpendir(filePath , &fd); + + CellFsDirectoryEntry de; + uint32_t count = 0; + err = cellFsGetDirectoryEntries(fd, &de, sizeof(CellFsDirectoryEntry), &count); + if(count != 0) + { + do + { + if(de.attribute.st_mode & CELL_FS_S_IFREG) vOutput->push_back( new File( *this, filenametowstring( de.entry_name.d_name ) ) ); + err = cellFsGetDirectoryEntries(fd, &de, sizeof(CellFsDirectoryEntry), &count); + } while( count ); + } + err = cellFsClose(fd); +#elif defined __ORBIS__ || defined __PSVITA__ + const char *lpFileName=wstringtofilename(getPath()); + char filePath[256]; + + std::string mountedPath = StorageManager.GetMountedPath(lpFileName); + if(mountedPath.length() > 0) + { + strcpy(filePath, mountedPath.c_str()); + } + else if(lpFileName[0] == '/') // already fully qualified path + strcpy(filePath, lpFileName ); + else + sprintf(filePath,"%s/%s",getUsrDirPath(), lpFileName ); + + bool exists = sceFiosDirectoryExistsSync( NULL, filePath ); + if( !exists ) + { + app.DebugPrintf("\nsceFiosDirectoryExistsSync - Directory doesn't exist\n"); + } + + //CD - Vita note: sceFiosDHOpenSync returns SCE_FIOS_ERROR_UNIMPLEMENTED + //CD - The Handle also returns as 0 [dh], sceFiosDHOpen could also be failing + //CD - Hence, this fails stating 0 files in directory + + SceFiosDH dh = SCE_FIOS_DH_INVALID; + SceFiosBuffer buf; + buf.length = 0; + SceFiosOp op = sceFiosDHOpen(NULL, &dh, filePath, buf); + + int err = sceFiosOpWait(op); + if( err != SCE_FIOS_OK ) + { + app.DebugPrintf("\nsceFiosOpWait = 0x%x\n",err); + } + SceFiosSize size = sceFiosOpGetActualCount(op); + char *pBuf = new char[size]; + buf.set(pBuf, (size_t)size); + + sceFiosOpDelete( op ); + sceFiosDHClose(NULL, dh); + + err = sceFiosDHOpenSync(NULL, &dh, filePath, buf); + if( err != SCE_FIOS_OK ) + { + app.DebugPrintf("\nsceFiosDHOpenSync = 0x%x\n",err); + } + SceFiosDirEntry entry; + ZeroMemory(&entry, sizeof(SceFiosDirEntry)); + err = sceFiosDHReadSync(NULL, dh, &entry); + while( err == SCE_FIOS_OK) + { + vOutput->push_back( new File( *this, filenametowstring( entry.fullPath + entry.offsetToName ) ) ); + ZeroMemory(&entry, sizeof(SceFiosDirEntry)); + err = sceFiosDHReadSync(NULL, dh, &entry); + }; + + sceFiosDHClose(NULL, dh); + delete pBuf; +#else + + WIN32_FIND_DATA wfd; + +#ifdef _UNICODE + WCHAR path[MAX_PATH]; + swprintf( path, L"%ls\\*", getPath().c_str() ); + HANDLE hFind = FindFirstFile( path, &wfd); + if(hFind != INVALID_HANDLE_VALUE) + { + int count = 0; + do + { + //if( !(wfd.dwFileAttributes & dwAttr) ) + vOutput->push_back( new File( *this, wfd.cFileName ) ); + } + while( FindNextFile( hFind, &wfd) ); + FindClose( hFind); + } +#else + char path[MAX_PATH]; + sprintf( path, "%s\\*", wstringtofilename( getPath() ) ); + HANDLE hFind = FindFirstFile( path, &wfd); + if(hFind != INVALID_HANDLE_VALUE) + { + //int count = 0; + do + { + //if( !(wfd.dwFileAttributes & dwAttr) ) + vOutput->push_back( new File( *this, filenametowstring( wfd.cFileName ) ) ); + } + while( FindNextFile( hFind, &wfd) ); + FindClose( hFind); + } +#endif +#endif + return vOutput; +} + +//Returns an array of abstract pathnames denoting the files and directories in the directory denoted by this abstract pathname that +//satisfy the specified filter. The behavior of this method is the same as that of the listFiles() method, except that the pathnames +//in the returned array must satisfy the filter. If the given filter is null then all pathnames are accepted. Otherwise, a pathname +//satisfies the filter if and only if the value true results when the FileFilter.accept(java.io.File) method of the filter is invoked +//on the pathname. +//Parameters: +//filter - A file filter +//Returns: +//An array of abstract pathnames denoting the files and directories in the directory denoted by this abstract pathname. +//The array will be empty if the directory is empty. Returns null if this abstract pathname does not denote a directory, or if an I/O error occurs. +vector *File::listFiles(FileFilter *filter) const +{ + // TODO 4J Stu - Also need to check for I/O errors? + if( !isDirectory() ) + return NULL; + + vector *vOutput = new vector(); + +#ifdef __PS3__ + const char *lpFileName=wstringtofilename(getPath()); + char filePath[256]; + + std::string mountedPath = StorageManager.GetMountedPath(lpFileName); + if(mountedPath.length() > 0) + { + strcpy(filePath, mountedPath.c_str()); + } + else if(lpFileName[0] == '/') // already fully qualified path + strcpy(filePath, lpFileName ); + else + sprintf(filePath,"%s/%s",getUsrDirPath(), lpFileName ); + int fd; + CellFsErrno err = cellFsOpendir(filePath, &fd); + + CellFsDirectoryEntry de; + uint32_t count = 0; + err = cellFsGetDirectoryEntries(fd, &de, sizeof(CellFsDirectoryEntry), &count); + if(count != 0) + { + do + { + File thisFile = File( *this, filenametowstring( de.entry_name.d_name ) ); + if( filter->accept( &thisFile ) ) + { + File storageFile = thisFile; + if(de.attribute.st_mode & CELL_FS_S_IFREG) vOutput->push_back( &storageFile ); + } + err = cellFsGetDirectoryEntries(fd, &de, sizeof(CellFsDirectoryEntry), &count); + } while( count ); + } + err = cellFsClose(fd); +#else + +#ifdef _UNICODE + + WCHAR path[MAX_PATH]; + WIN32_FIND_DATA wfd; + DWORD dwAttr = FILE_ATTRIBUTE_DIRECTORY; + + swprintf( path, L"%ls\\*", getPath().c_str() ); + HANDLE hFind = FindFirstFile( path, &wfd); + if(hFind != INVALID_HANDLE_VALUE) + { + int count = 0; + do + { + File thisFile = File( *this, wfd.cFileName ); + if( filter->accept( &thisFile ) ) + { + File storageFile = thisFile; + vOutput->push_back( &storageFile ); + } + } while( FindNextFile( hFind, &wfd) ); + FindClose( hFind); + } +#else + char path[MAX_PATH]; + WIN32_FIND_DATA wfd; + //DWORD dwAttr = FILE_ATTRIBUTE_DIRECTORY; + + sprintf( path, "%s\\*", wstringtofilename( getPath() ) ); + HANDLE hFind = FindFirstFile( path, &wfd); + if(hFind != INVALID_HANDLE_VALUE) + { + //int count = 0; + do + { + File thisFile = File( *this, filenametowstring( wfd.cFileName ) ); + if( filter->accept( &thisFile ) ) + { + File storageFile = thisFile; + vOutput->push_back( &storageFile ); + } + } while( FindNextFile( hFind, &wfd) ); + FindClose( hFind); + } +#endif +#endif + return vOutput; +} + +//Tests whether the file denoted by this abstract pathname is a directory. +//Returns: +//true if and only if the file denoted by this abstract pathname exists and is a directory; false otherwise +bool File::isDirectory() const +{ +#ifdef _UNICODE + return exists() && ( GetFileAttributes( getPath().c_str() ) & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; +#else + return exists() && ( GetFileAttributes( wstringtofilename(getPath()) ) & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY; +#endif +} + +//Returns the length of the file denoted by this abstract pathname. The return value is unspecified if this pathname denotes a directory. +//Returns: +//The length, in bytes, of the file denoted by this abstract pathname, or 0L if the file does not exist +__int64 File::length() +{ +#ifdef __PS3__ + //extern const char* getPS3HomePath(); + CellFsErrno err=0; + const char *lpFileName=wstringtofilename(getPath()); + char filePath[256]; + + std::string mountedPath = StorageManager.GetMountedPath(lpFileName); + if(mountedPath.length() > 0) + { + strcpy(filePath, mountedPath.c_str()); + } + else if(lpFileName[0] == '/') // already fully qualified path + strcpy(filePath, lpFileName ); + else + sprintf(filePath,"%s/%s",getUsrDirPath(), lpFileName ); + +#ifndef _CONTENT_PACKAGE + //printf("+++File::length - %s\n",filePath); +#endif + // check if the file exists first + CellFsStat statData; + err=cellFsStat(filePath, &statData); + + if( err != CELL_FS_SUCCEEDED) + { + //printf("+++File::length FAILED with %d\n",err); + return 0; + } + if(statData.st_mode == CELL_FS_S_IFDIR) + { + //printf("+++File::length FAILED with %d\n",err); + return 0; + } + + //printf("+++File::length - %ll\n",statData.st_size); + + return statData.st_size; +#elif defined __ORBIS__ || defined __PSVITA__ + + char filePath[256]; + const char *lpFileName=wstringtofilename(getPath()); + + std::string mountedPath = StorageManager.GetMountedPath(lpFileName); + if(mountedPath.length() > 0) + { + strcpy(filePath, mountedPath.c_str()); + } + else if(lpFileName[0] == '/') // already fully qualified path + strcpy(filePath, lpFileName ); + else + sprintf(filePath,"%s/%s",getUsrDirPath(), lpFileName ); + + // check if the file exists first + SceFiosStat statData; + if(sceFiosStatSync(NULL, filePath, &statData) != SCE_FIOS_OK) + { + return 0; + } + if(statData.statFlags & SCE_FIOS_STATUS_DIRECTORY ) + { + return 0; + } + return statData.fileSize; +#else + WIN32_FILE_ATTRIBUTE_DATA fileInfoBuffer; + +#ifdef _UNICODE + BOOL result = GetFileAttributesEx( + getPath().c_str(), // file or directory name + GetFileExInfoStandard, // attribute + &fileInfoBuffer // attribute information + ); +#else + BOOL result = GetFileAttributesEx( + wstringtofilename(getPath()), // file or directory name + GetFileExInfoStandard, // attribute + &fileInfoBuffer // attribute information + ); +#endif + + if( result != 0 && !( (fileInfoBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) ) + { + // Success + LARGE_INTEGER liFileSize; + liFileSize.HighPart = fileInfoBuffer.nFileSizeHigh; + liFileSize.LowPart = fileInfoBuffer.nFileSizeLow; + + return liFileSize.QuadPart; + } + else + { + //Fail or a Directory + return 0l; + } +#endif +} + +//Returns the time that the file denoted by this abstract pathname was last modified. +//Returns: +//A long value representing the time the file was last modified, measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970), +//or 0L if the file does not exist or if an I/O error occurs +__int64 File::lastModified() +{ + WIN32_FILE_ATTRIBUTE_DATA fileInfoBuffer; +#ifdef _UNICODE + BOOL result = GetFileAttributesEx( + getPath().c_str(), // file or directory name + GetFileExInfoStandard, // attribute + &fileInfoBuffer // attribute information + ); +#else + BOOL result = GetFileAttributesEx( + wstringtofilename(getPath()), // file or directory name + GetFileExInfoStandard, // attribute + &fileInfoBuffer // attribute information + ); +#endif + + if( result != 0 && !( (fileInfoBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) ) + { + // Success + LARGE_INTEGER liLastModified; + liLastModified.HighPart = fileInfoBuffer.ftLastWriteTime.dwHighDateTime; + liLastModified.LowPart = fileInfoBuffer.ftLastWriteTime.dwLowDateTime; + + return liLastModified.QuadPart; + } + else + { + //Fail or a Directory + return 0l; + } +} + +const wstring File::getPath() const +{ + /* + wstring path; + if ( parent != NULL) + path = parent->getPath(); + else + path = wstring(pathRoot); + + path.push_back( pathSeparator ); + path.append(m_abstractPathName); + */ + return m_abstractPathName; +} + +wstring File::getName() const +{ + unsigned int sep = (unsigned int )(m_abstractPathName.find_last_of( this->pathSeparator )); + return m_abstractPathName.substr( sep + 1, m_abstractPathName.length() ); +} + +bool File::eq_test(const File &x, const File &y) +{ + return x.getPath().compare( y.getPath() ) == 0; +} + +// 4J TODO JEV, a better hash function may be nessesary. +int File::hash_fnct(const File &k) +{ + int hashCode = 0; + + //if (k->parent != NULL) + // hashCode = hash_fnct(k->getParent()); + + wchar_t *ref = (wchar_t *) k.m_abstractPathName.c_str(); + + for (unsigned int i = 0; i < k.m_abstractPathName.length(); i++) + { + hashCode += ((hashCode * 33) + ref[i]) % 149; + } + + return (int) hashCode; +} diff --git a/Minecraft.World/File.h b/Minecraft.World/File.h new file mode 100644 index 00000000..0b710cd4 --- /dev/null +++ b/Minecraft.World/File.h @@ -0,0 +1,56 @@ +#pragma once +using namespace std; +// 4J Stu - Represents java standard library class + +class FileFilter; + +class File +{ +public: + //The system-dependent path-separator character + static const wchar_t pathSeparator; + + // 4J Jev, the start of the file root + static const wstring pathRoot; + + File() { m_abstractPathName = L""; } + + File( const File &parent, const wstring& child ); + File( const wstring& pathname ); + File( const wstring& parent, const wstring& child ); + bool _delete(); + bool mkdir() const; + bool mkdirs() const; + bool exists() const; + bool isFile() const; + bool renameTo(File dest); + vector *listFiles() const; // Array + vector *listFiles(FileFilter *filter) const; + bool isDirectory() const; + __int64 length(); + __int64 lastModified(); + const wstring getPath() const; // 4J Jev: TODO + wstring getName() const; + + static int hash_fnct(const File &k); + static bool eq_test(const File &x, const File &y); + +private: + void _init(); + wstring m_abstractPathName; + + // 4J Jev, just helper functions, change between paths and vector + //File(vector *path); +}; + +typedef struct +{ + int operator() (const File &k) const { return File::hash_fnct(k); } + +} FileKeyHash; + +typedef struct +{ + bool operator() (const File &x, const File &y) const {return File::eq_test(x,y); } + +} FileKeyEq; \ No newline at end of file diff --git a/Minecraft.World/FileFilter.h b/Minecraft.World/FileFilter.h new file mode 100644 index 00000000..859fbb90 --- /dev/null +++ b/Minecraft.World/FileFilter.h @@ -0,0 +1,10 @@ +#pragma once + +class File; + +// 4J Jev, java library interface. +class FileFilter +{ +public: + virtual bool accept(File *dir) = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/FileHeader.cpp b/Minecraft.World/FileHeader.cpp new file mode 100644 index 00000000..59e97d9b --- /dev/null +++ b/Minecraft.World/FileHeader.cpp @@ -0,0 +1,681 @@ +#include "stdafx.h" +#include "FileHeader.h" + +//#define _DEBUG_FILE_HEADER + +FileHeader::FileHeader() +{ + lastFile = NULL; + m_saveVersion = 0; + + // New saves should have an original version set to the latest version. This will be overridden when we load a save + m_originalSaveVersion = SAVE_FILE_VERSION_NUMBER; + m_savePlatform = SAVE_FILE_PLATFORM_LOCAL; + m_saveEndian = m_localEndian; +} + +FileHeader::~FileHeader() +{ + for( unsigned int i = 0; i < fileTable.size(); ++i ) + { + delete fileTable[i]; + } +} + +FileEntry *FileHeader::AddFile( const wstring &name, unsigned int length /* = 0 */ ) +{ + assert( name.length() < 64 ); + + wchar_t filename[64]; + memset( &filename, 0, sizeof( wchar_t ) * 64 ); + memcpy( &filename, name.c_str(), min( sizeof( wchar_t ) * 64, sizeof( wchar_t ) * name.length() ) ); + + // Would a map be more efficient? Our file tables probably won't be very big so better to avoid hashing all the time? + // Does the file exist? + for( unsigned int i = 0; i < fileTable.size(); ++i ) + { + if( wcscmp( fileTable[i]->data.filename, filename ) == 0 ) + { + // If so, return it + return fileTable[i]; + } + } + + // Else, add it to our file table + fileTable.push_back( new FileEntry( filename, length, GetStartOfNextData() ) ); + lastFile = fileTable[ fileTable.size() - 1 ]; + return lastFile; +} + +void FileHeader::RemoveFile( FileEntry *file ) +{ + if( file == NULL ) return; + + AdjustStartOffsets(file, file->getFileSize(), true); + + AUTO_VAR(it, find(fileTable.begin(), fileTable.end(),file)); + + if( it < fileTable.end() ) + { + fileTable.erase( it ); + } + +#ifndef _CONTENT_PACKAGE + wprintf(L"Removed file %ls\n", file->data.filename); +#endif + + delete file; +} + +void FileHeader::WriteHeader( LPVOID saveMem ) +{ + unsigned int headerOffset = GetStartOfNextData(); + + // 4J Changed for save version 2 to be the number of files rather than the size in bytes + unsigned int headerSize = (int)(fileTable.size()); + + //DWORD numberOfBytesWritten = 0; + + // Write the offset of the header + //assert(numberOfBytesWritten == 4); + int *begin = (int *)saveMem; +#ifdef __PSVITA__ + VirtualCopyTo(begin, &headerOffset, sizeof(headerOffset)); +#else + *begin = headerOffset; +#endif + + // Write the size of the header + //assert(numberOfBytesWritten == 4); +#ifdef __PSVITA__ + VirtualCopyTo(begin + 1, &headerSize, sizeof(headerSize)); +#else + *(begin + 1) = headerSize; +#endif + + short *versions = (short *)(begin + 2); + // Write the original version number +#ifdef __PSVITA__ + VirtualCopyTo(versions, &m_originalSaveVersion, sizeof(m_originalSaveVersion)); +#else + *versions = m_originalSaveVersion; +#endif + + // Write the version number + short versionNumber = SAVE_FILE_VERSION_NUMBER; + //assert(numberOfBytesWritten == 4); + //*(begin + 2) = versionNumber; +#ifdef __PSVITA__ + VirtualCopyTo(versions + 1, &versionNumber, sizeof(versionNumber)); +#else + *(versions + 1) = versionNumber; +#endif + +#ifdef _DEBUG_FILE_HEADER + app.DebugPrintf("Write save file with original version: %d, and current version %d\n", m_originalSaveVersion, versionNumber); +#endif + + char *headerPosition = (char *)saveMem + headerOffset; + +#ifdef _DEBUG_FILE_HEADER + app.DebugPrintf("\n\nWrite file Header: Offset = %d, Size = %d\n", headerOffset, headerSize); +#endif + + // Write the header + for( unsigned int i = 0; i < fileTable.size(); ++i ) + { + //wprintf(L"File: %ls, Start = %d, Length = %d, End = %d\n", fileTable[i]->data.filename, fileTable[i]->data.startOffset, fileTable[i]->data.length, fileTable[i]->data.startOffset + fileTable[i]->data.length); +#ifdef __PSVITA__ + VirtualCopyTo((void *)headerPosition, &fileTable[i]->data, sizeof(FileEntrySaveData)); +#else + memcpy( (void *)headerPosition, &fileTable[i]->data, sizeof(FileEntrySaveData) ); +#endif + //assert(numberOfBytesWritten == sizeof(FileEntrySaveData)); + headerPosition += sizeof(FileEntrySaveData); + } +} + +void FileHeader::ReadHeader( LPVOID saveMem, ESavePlatform plat /*= SAVE_FILE_PLATFORM_LOCAL */ ) +{ + unsigned int headerOffset; + unsigned int headerSize; + + m_savePlatform = plat; + + switch(m_savePlatform) + { + case SAVE_FILE_PLATFORM_X360: + case SAVE_FILE_PLATFORM_PS3: + m_saveEndian = BIGENDIAN; + break; + case SAVE_FILE_PLATFORM_XBONE: + case SAVE_FILE_PLATFORM_WIN64: + case SAVE_FILE_PLATFORM_PS4: + case SAVE_FILE_PLATFORM_PSVITA: + m_saveEndian = LITTLEENDIAN; + break; + default: + assert(0); + m_savePlatform = SAVE_FILE_PLATFORM_LOCAL; + m_saveEndian = m_localEndian; + break; + } + + + // Read the offset of the header + //assert(numberOfBytesRead == 4); + int *begin = (int *)saveMem; +#ifdef __PSVITA__ + VirtualCopyFrom(&headerOffset, begin, sizeof(headerOffset)); +#else + headerOffset = *begin; +#endif + if(isSaveEndianDifferent()) System::ReverseULONG(&headerOffset); + + // Read the size of the header + //assert(numberOfBytesRead == 4); +#ifdef __PSVITA__ + VirtualCopyFrom(&headerSize, begin + 1, sizeof(headerSize)); +#else + headerSize = *(begin + 1); +#endif + if(isSaveEndianDifferent()) System::ReverseULONG(&headerSize); + + + short *versions = (short *)(begin + 2); + // Read the original save version number +#ifdef __PSVITA__ + VirtualCopyFrom(&m_originalSaveVersion, versions, sizeof(m_originalSaveVersion)); +#else + m_originalSaveVersion = *(versions); +#endif + if(isSaveEndianDifferent()) System::ReverseSHORT(&m_originalSaveVersion); + + // Read the save version number + //m_saveVersion = *(begin + 2); +#ifdef __PSVITA__ + VirtualCopyFrom(&m_saveVersion, versions + 1, sizeof(m_saveVersion)); +#else + m_saveVersion = *(versions + 1); +#endif + if(isSaveEndianDifferent()) System::ReverseSHORT(&m_saveVersion); + +#ifdef _DEBUG_FILE_HEADER + app.DebugPrintf("Read save file with orignal version: %d, and current version %d\n", m_originalSaveVersion, m_saveVersion); + app.DebugPrintf("\n\nRead file Header: Offset = %d, Size = %d\n", headerOffset, headerSize); +#endif + + char *headerPosition = (char *)saveMem + headerOffset; + + switch( m_saveVersion ) + { + //case SAVE_FILE_VERSION_NUMBER: + //case 8: // 4J Stu - SAVE_FILE_VERSION_NUMBER 2,3,4,5,6,7,8 are the same, but: + // : Bumped it to 3 in TU5 to force older builds (ie 0062) to generate a new world when trying to load new saves + // : Bumped it to 4 in TU9 to delete versions of The End that were generated in builds prior to TU9 + // : Bumped it to 5 in TU9 to update the map data that was only using 1 bit to determine dimension + // : Bumped it to 6 for PS3 v1 to update map data mappings to use larger PlayerUID + // : Bumped it to 7 for Durango v1 to update map data mappings to use string based PlayerUID + // : Bumped it to 8 for Durango v1 when to save the chunks in a different compressed format + case SAVE_FILE_VERSION_COMPRESSED_CHUNK_STORAGE: + case SAVE_FILE_VERSION_DURANGO_CHANGE_MAP_DATA_MAPPING_SIZE: + case SAVE_FILE_VERSION_CHANGE_MAP_DATA_MAPPING_SIZE: + case SAVE_FILE_VERSION_MOVED_STRONGHOLD: + case SAVE_FILE_VERSION_NEW_END: + case SAVE_FILE_VERSION_POST_LAUNCH: + case SAVE_FILE_VERSION_LAUNCH: + { + // Changes for save file version 2: + // headerSize is now a count of elements rather than a count of bytes + // The FileEntrySaveData struct has a lastModifiedTime member + + // Read the header + FileEntrySaveData *fesdHeaderPosition = (FileEntrySaveData *)headerPosition; + for(unsigned int i = 0; i < headerSize; ++i) + { + FileEntry *entry = new FileEntry(); + //assert(numberOfBytesRead == sizeof(FileEntrySaveData)); + +#ifdef __PSVITA__ + VirtualCopyFrom( &entry->data, fesdHeaderPosition, sizeof(FileEntrySaveData) ); +#else + memcpy( &entry->data, fesdHeaderPosition, sizeof(FileEntrySaveData) ); +#endif + + if(isSaveEndianDifferent()) + { + // Reverse bytes + System::ReverseWCHARA(entry->data.filename,64); + System::ReverseULONG(&entry->data.length); + System::ReverseULONG(&entry->data.startOffset); + System::ReverseULONGLONG(&entry->data.lastModifiedTime); + } + + + entry->currentFilePointer = entry->data.startOffset; + lastFile = entry; + fileTable.push_back( entry ); +#ifdef _DEBUG_FILE_HEADER + app.DebugPrintf("File: %ls, Start = %d, Length = %d, End = %d, Timestamp = %lld\n", entry->data.filename, entry->data.startOffset, entry->data.length, entry->data.startOffset + entry->data.length, entry->data.lastModifiedTime); +#endif + + fesdHeaderPosition++; + } + } + break; + + // Legacy save versions, with updated code to convert the FileEntrySaveData to the latest version + // 4J Stu - At time of writing, the tutorial save is V1 so need to keep this for compatibility + case SAVE_FILE_VERSION_PRE_LAUNCH: + { + // Read the header + // We can then make headerPosition a FileEntrySaveData pointer and just increment by one up to the number + unsigned int i = 0; + while( i < headerSize ) + { + FileEntry *entry = new FileEntry(); + //assert(numberOfBytesRead == sizeof(FileEntrySaveData)); + +#ifdef __PSVITA__ + VirtualCopyFrom( &entry->data, headerPosition, sizeof(FileEntrySaveDataV1) ); +#else + memcpy( &entry->data, headerPosition, sizeof(FileEntrySaveDataV1) ); +#endif + + entry->currentFilePointer = entry->data.startOffset; + lastFile = entry; + fileTable.push_back( entry ); +#ifdef _DEBUG_FILE_HEADER + app.DebugPrintf("File: %ls, Start = %d, Length = %d, End = %d\n", entry->data.filename, entry->data.startOffset, entry->data.length, entry->data.startOffset + entry->data.length); +#endif + + i += sizeof(FileEntrySaveDataV1); + headerPosition += sizeof(FileEntrySaveDataV1); + } + } + break; + default: +#ifndef _CONTENT_PACKAGE + app.DebugPrintf("********** Invalid save version %d\n",m_saveVersion); + __debugbreak(); +#endif + break; + } +} + +unsigned int FileHeader::GetStartOfNextData() +{ + // The first 4 bytes is the location of the header (the header itself is at the end of the file) + // Then 4 bytes for the size of the header + // Then 2 bytes for the version number at which this save was first generated + // Then 2 bytes for the version number that the save should now be at + unsigned int totalBytesSoFar = SAVE_FILE_HEADER_SIZE; + for( unsigned int i = 0; i < fileTable.size(); ++i ) + { + if( fileTable[i]->getFileSize() > 0 ) + totalBytesSoFar += fileTable[i]->getFileSize(); + } + return totalBytesSoFar; +} + +unsigned int FileHeader::GetFileSize() +{ + return GetStartOfNextData() + ( sizeof(FileEntrySaveData) * (unsigned int)fileTable.size() ); +} + +void FileHeader::AdjustStartOffsets(FileEntry *file, DWORD nNumberOfBytesToWrite, bool subtract /*= false*/) +{ + bool found = false; + for( unsigned int i = 0; i < fileTable.size(); ++i ) + { + if( found == true ) + { + if(subtract) + { + fileTable[i]->data.startOffset -= nNumberOfBytesToWrite; + fileTable[i]->currentFilePointer -= nNumberOfBytesToWrite; + } + else + { + fileTable[i]->data.startOffset += nNumberOfBytesToWrite; + fileTable[i]->currentFilePointer += nNumberOfBytesToWrite; + } + } + else if( fileTable[i] == file ) + { + found = true; + } + } +} + +bool FileHeader::fileExists( const wstring &name ) +{ + for( unsigned int i = 0; i < fileTable.size(); ++i ) + { + if( wcscmp( fileTable[i]->data.filename, name.c_str() ) == 0 ) + { + // If so, return it + return true; + } + } + return false; +} + +vector *FileHeader::getFilesWithPrefix(const wstring &prefix) +{ + vector *files = NULL; + + for( unsigned int i = 0; i < fileTable.size(); ++i ) + { + if( wcsncmp( fileTable[i]->data.filename, prefix.c_str(), prefix.size() ) == 0 ) + { + if( files == NULL ) + { + files = new vector(); + } + + files->push_back(fileTable[i]); + } + } + + return files; +} + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + +static bool isHexChar(wchar_t wc) +{ + if(wc >= L'0' && wc <= L'9') + return true; + if(wc >= L'a' && wc <= L'f') + return true; + return false; +} +static bool isHexString(wchar_t* ws, int size) +{ + for(int i=0;i= L'0' && wc <= L'9') + return true; + return false; +} +static bool isDecimalString(wchar_t* ws, int size) +{ + for(int i=0;i* pFiles = getDatFilesWithOnlineID(pUID); + if(!pFiles) + { + // we didn't find a matching online dat file, so look for an offline version + pFiles = getDatFilesWithMacAndUserID(pUID); + } + if(!pFiles) + { + // and we didn't find an offline file, so check if we're the primary user, and grab the primary file if we are + if(pUID.isPrimaryUser()) + { + pFiles= getDatFilesWithPrimaryUser(); + } + } + if(pFiles) + { + // we've got something to load +// assert(pFiles->size() == 1); + retVal = pFiles->at(0)->data.filename; + delete pFiles; + } + return retVal; +} + +wstring FileHeader::getPlayerDataFilenameForSave(const PlayerUID& pUID) +{ + // check if we're online first + if(pUID.isSignedIntoPSN() == false) + { + // OK, we're not online, see if we can find another data file with matching mac and userID + vector* pFiles = getDatFilesWithMacAndUserID(pUID); + if(pFiles) + { + // we've found a previous save, use the filename from it, as it might have the online part too +// assert(pFiles->size() == 1); + wstring retVal = pFiles->at(0)->data.filename; + delete pFiles; + return retVal; + } + } + + // we're either online, or we can't find a previous save, so use the standard filename + wstring retVal = pUID.toString() + L".dat"; + return retVal; +} + + +vector *FileHeader::getValidPlayerDatFiles() +{ + vector *files = NULL; + + // find filenames that match this pattern + // P_5e7ff8372ea9_00000004_Mark_4J + + for( unsigned int i = 0; i < fileTable.size(); ++i ) + { + wchar_t* filenameOnly = findFilenameStart(fileTable[i]->data.filename); + + int nameLen = wcslen(filenameOnly); + if(nameLen <= 4) + continue; + + // make sure it's a ".dat" file + if( wcsncmp( &filenameOnly[nameLen-4], L".dat", 4 ) != 0 ) + continue; + // make sure we start with "P_" or "N_" + if( ( wcsncmp(&filenameOnly[0], L"P_", 2) != 0 ) && ( wcsncmp( &filenameOnly[0], L"N_", 2 ) != 0 ) ) + continue; + //check the next 12 chars are hex + if(!isHexString(&filenameOnly[2], 12)) + continue; + // make sure character 14 is '_' + if(filenameOnly[14] != L'_') + continue; + //check the next 8 chars are decimal + if(!isDecimalString(&filenameOnly[15], 8)) + continue; + + // if we get here, it must be a valid filename + if( files == NULL ) + { + files = new vector(); + } + files->push_back(fileTable[i]); + } + + return files; +} + + + +vector *FileHeader::getDatFilesWithOnlineID(const PlayerUID& pUID) +{ + if(pUID.isSignedIntoPSN() == false) + return NULL; + + vector* datFiles = getValidPlayerDatFiles(); + if(datFiles == NULL) + return NULL; + + // we're looking for the online name from the pUID in these types of filenames - + // P_5e7ff8372ea9_00000004_Mark_4J + wchar_t onlineIDW[64]; + mbstowcs(onlineIDW, pUID.getOnlineID(), 64); + + vector *files = NULL; + int onlineIDSize = wcslen(onlineIDW); + if(onlineIDSize == 0) + return NULL; + + wcscat(onlineIDW, L".dat"); + +#ifdef __ORBIS__ + onlineIDSize = wcslen(onlineIDW); +#else + static const int onlineIDStart = 24; // 24 characters into the filename +#endif + + char tempStr[128]; + for( unsigned int i = 0; i < datFiles->size(); ++i ) + { + wchar_t* filenameOnly = findFilenameStart(datFiles->at(i)->data.filename); + wcstombs(tempStr,filenameOnly, 128); + app.DebugPrintf("file : %s\n", tempStr); + +#ifdef __ORBIS__ + int onlineIDStart = wcslen(filenameOnly) - onlineIDSize; + if(onlineIDStart > 0) +#else + if(wcslen(filenameOnly) > onlineIDStart) +#endif + { + if(wcsncmp(&filenameOnly[onlineIDStart], onlineIDW, onlineIDSize) == 0) + { + if( files == NULL ) + { + files = new vector(); + } + files->push_back(datFiles->at(i)); + } + } + } + delete datFiles; + + if(files) sort(files->begin(), files->end(), FileEntry::newestFirst ); + return files; +} + +vector *FileHeader::getDatFilesWithMacAndUserID(const PlayerUID& pUID) +{ + + vector* datFiles = getValidPlayerDatFiles(); + if(datFiles == NULL) + return NULL; + + // we're looking for the mac address and userIDfrom the pUID in these types of filenames - + // P_5e7ff8372ea9_00000004_Mark_4J + std::wstring macStr = pUID.macAddressStr(); + std::wstring userStr = pUID.userIDStr(); + const wchar_t* pMacStr = macStr.c_str(); + const wchar_t* pUserStr = userStr.c_str(); + + vector *files = NULL; + static const int macAddrStart = 2; // 2 characters into the filename + static const int userIDStart = 15; // 15 characters into the filename + + char tempStr[128]; + for( unsigned int i = 0; i < datFiles->size(); ++i ) + { + wchar_t* filenameOnly = findFilenameStart(datFiles->at(i)->data.filename); + wcstombs(tempStr,filenameOnly, 128); + app.DebugPrintf("file : %s\n", tempStr); + + // check the mac address matches + if(wcsncmp(&filenameOnly[macAddrStart], pMacStr, macStr.size()) == 0) + { + // check the userID matches + if(wcsncmp(&filenameOnly[userIDStart], pUserStr, userStr.size()) == 0) + { + if( files == NULL ) + { + files = new vector(); + } + files->push_back(datFiles->at(i)); + } + } + } + delete datFiles; + if(files) sort(files->begin(), files->end(), FileEntry::newestFirst ); + return files; +} + + +vector *FileHeader::getDatFilesWithPrimaryUser() +{ + + vector* datFiles = getValidPlayerDatFiles(); + if(datFiles == NULL) + return NULL; + + // we're just looking for filenames starting with "P_" in these types of filenames - + // P_5e7ff8372ea9_00000004_Mark_4J + vector *files = NULL; + + char tempStr[128]; + for( unsigned int i = 0; i < datFiles->size(); ++i ) + { + wchar_t* filenameOnly = findFilenameStart(datFiles->at(i)->data.filename); + wcstombs(tempStr,filenameOnly, 128); + app.DebugPrintf("file : %s\n", tempStr); + + // check for "P_" prefix + if(wcsncmp(&filenameOnly[0], L"P_", 2) == 0) + { + if( files == NULL ) + { + files = new vector(); + } + files->push_back(datFiles->at(i)); + } + } + delete datFiles; + if(files) sort(files->begin(), files->end(), FileEntry::newestFirst ); + return files; +} +#endif // __PS3__ || __ORBIS__ + +ByteOrder FileHeader::getEndian( ESavePlatform plat ) +{ + ByteOrder platEndian; + switch(plat) + { + case SAVE_FILE_PLATFORM_X360: + case SAVE_FILE_PLATFORM_PS3: + return BIGENDIAN; + break; + + case SAVE_FILE_PLATFORM_NONE: + case SAVE_FILE_PLATFORM_XBONE: + case SAVE_FILE_PLATFORM_PS4: + case SAVE_FILE_PLATFORM_PSVITA: + case SAVE_FILE_PLATFORM_WIN64: + return LITTLEENDIAN; + break; + default: + assert(0); + break; + } + return LITTLEENDIAN; +} \ No newline at end of file diff --git a/Minecraft.World/FileHeader.h b/Minecraft.World/FileHeader.h new file mode 100644 index 00000000..203ec322 --- /dev/null +++ b/Minecraft.World/FileHeader.h @@ -0,0 +1,203 @@ +#pragma once +using namespace std; + +#include "System.h" + +// The first 4 bytes is the location of the header (the header itself is at the end of the file) +// Then 4 bytes for the size of the header +// Then 2 bytes for the version number at which this save was first generated +// Then 2 bytes for the version number that the save should now be at +// ( the rest of the header is actually a footer ) +#define SAVE_FILE_HEADER_SIZE 12 + +enum ESaveVersions +{ + // Pre-release version + SAVE_FILE_VERSION_PRE_LAUNCH = 1, + + // This is the version at which we launched the Xbox360 version + SAVE_FILE_VERSION_LAUNCH = 2, + + // This is the version at which we had made changes that broke older saves + SAVE_FILE_VERSION_POST_LAUNCH = 3, + + // This is the version at which we introduced the End, and any saves older than this will have their End data deleted + SAVE_FILE_VERSION_NEW_END = 4, + + // This is the version at which we change the stronghold generation, and any saves older than this should should the original version + SAVE_FILE_VERSION_MOVED_STRONGHOLD = 5, + + // This is the version at which we changed the playeruid format for PS3 + SAVE_FILE_VERSION_CHANGE_MAP_DATA_MAPPING_SIZE = 6, + + // This is the version at which we changed the playeruid format for Xbox One + SAVE_FILE_VERSION_DURANGO_CHANGE_MAP_DATA_MAPPING_SIZE = 7, + + // This is the version at which we changed the chunk format to directly save the compressed storage formats + SAVE_FILE_VERSION_COMPRESSED_CHUNK_STORAGE, + + SAVE_FILE_VERSION_NEXT, +}; + +// This is the version at which we changed the playeruid format for Xbox One +#define SAVE_FILE_VERSION_DURANGO_CHANGE_MAP_DATA_MAPPING_SIZE 7 + +enum ESavePlatform +{ + SAVE_FILE_PLATFORM_NONE = MAKE_FOURCC('N', 'O', 'N', 'E') , + SAVE_FILE_PLATFORM_X360 = MAKE_FOURCC('X', '3', '6', '0') , + SAVE_FILE_PLATFORM_XBONE = MAKE_FOURCC('X', 'B', '1', '_') , + SAVE_FILE_PLATFORM_PS3 = MAKE_FOURCC('P', 'S', '3', '_') , + SAVE_FILE_PLATFORM_PS4 = MAKE_FOURCC('P', 'S', '4', '_') , + SAVE_FILE_PLATFORM_PSVITA = MAKE_FOURCC('P', 'S', 'V', '_') , + SAVE_FILE_PLATFORM_WIN64 = MAKE_FOURCC('W', 'I', 'N', '_') , + +#if defined _XBOX + SAVE_FILE_PLATFORM_LOCAL = SAVE_FILE_PLATFORM_X360 +#elif defined _DURANGO + SAVE_FILE_PLATFORM_LOCAL = SAVE_FILE_PLATFORM_XBONE +#elif defined __PS3__ + SAVE_FILE_PLATFORM_LOCAL = SAVE_FILE_PLATFORM_PS3 +#elif defined __ORBIS__ + SAVE_FILE_PLATFORM_LOCAL = SAVE_FILE_PLATFORM_PS4 +#elif defined __PSVITA__ + SAVE_FILE_PLATFORM_LOCAL = SAVE_FILE_PLATFORM_PSVITA +#elif defined _WINDOWS64 + SAVE_FILE_PLATFORM_LOCAL = SAVE_FILE_PLATFORM_WIN64 +#endif +}; +#define SAVE_FILE_VERSION_NUMBER (SAVE_FILE_VERSION_NEXT - 1) + +struct FileEntrySaveDataV1 +{ +public: + wchar_t filename[64]; // 64 * 2B + unsigned int length; // In bytes // 4B + + // This is only valid once the save file has been written/loaded at least once + unsigned int startOffset; // 4B +}; + +// It's important that we keep the order and size of the data here to smooth updating +// 4J Stu - As of writing the tutorial level uses a V1 save file +struct FileEntrySaveDataV2 +{ +public: + wchar_t filename[64]; // 64 * 2B + unsigned int length; // In bytes // 4B + + union + { + // This is only valid once the save file has been written/loaded at least once + unsigned int startOffset; // 4B + // For region files stored via ConsolveSaveFileSplit, these aren't stored within the normal save file, identified by not having a name (filename[0] is 0). + // Note: These won't be read or written as part of a file header, and should only exist wrapped up in a FileEntry class + unsigned int regionIndex; // 4B + + }; + + __int64 lastModifiedTime; // 8B +}; + +typedef FileEntrySaveDataV2 FileEntrySaveData; + +class FileEntry +{ +public: + FileEntrySaveData data; + + unsigned int currentFilePointer; + + FileEntry() { ZeroMemory(&data, sizeof(FileEntrySaveData)); } + + FileEntry( wchar_t name[64], unsigned int length, unsigned int startOffset ) + { + data.length = length; + data.startOffset = startOffset; + memset( &data.filename, 0, sizeof( wchar_t ) * 64 ); + memcpy( &data.filename, name, sizeof( wchar_t ) * 64 ); + + data.lastModifiedTime = 0; + + currentFilePointer = data.startOffset; + } + + unsigned int getFileSize() { return data.length; } + bool isRegionFile() { return data.filename[0] == 0; } // When using ConsoleSaveFileSplit only + unsigned int getRegionFileIndex() { return data.regionIndex; } // When using ConsoleSaveFileSplit only + + void updateLastModifiedTime() { data.lastModifiedTime = System::currentRealTimeMillis(); } + + /* + Comparison function object that returns true if the first argument goes before the second argument in the specific strict weak ordering it defines, and false otherwise. + Used in a call to std::sort in DirectoryLevelStorage.cpp + */ + static bool newestFirst( FileEntry *a, FileEntry *b ) { return a->data.lastModifiedTime > b->data.lastModifiedTime; } +}; + +// A class the represents the header of the save file +class FileHeader +{ + friend class ConsoleSaveFileOriginal; + friend class ConsoleSaveFileSplit; +private: + vector fileTable; + ESavePlatform m_savePlatform; + ByteOrder m_saveEndian; +#if defined(__PS3__) || defined(_XBOX) + static const ByteOrder m_localEndian = BIGENDIAN; +#else + static const ByteOrder m_localEndian = LITTLEENDIAN; +#endif + + short m_saveVersion; + short m_originalSaveVersion; + +public: + FileEntry *lastFile; + +public: + FileHeader(); + ~FileHeader(); + +protected: + FileEntry *AddFile( const wstring &name, unsigned int length = 0 ); + void RemoveFile( FileEntry * ); + void WriteHeader( LPVOID saveMem ); + void ReadHeader( LPVOID saveMem, ESavePlatform plat = SAVE_FILE_PLATFORM_LOCAL ); + + unsigned int GetStartOfNextData(); + + unsigned int GetFileSize(); + + void AdjustStartOffsets(FileEntry *file, DWORD nNumberOfBytesToWrite, bool subtract = false); + + bool fileExists( const wstring &name ); + + vector *getFilesWithPrefix(const wstring &prefix); + + vector *getValidPlayerDatFiles(); + +#if defined(__PS3__) || defined(__ORBIS__) || defined(__PSVITA__) + wstring getPlayerDataFilenameForLoad(const PlayerUID& pUID); + wstring getPlayerDataFilenameForSave(const PlayerUID& pUID); + vector *getDatFilesWithOnlineID(const PlayerUID& pUID); + vector *getDatFilesWithMacAndUserID(const PlayerUID& pUID); + vector *getDatFilesWithPrimaryUser(); +#endif + + void setSaveVersion(int version) { m_saveVersion = version; } + int getSaveVersion() { return m_saveVersion; } + void setOriginalSaveVersion(int version) { m_originalSaveVersion = version; } + int getOriginalSaveVersion() { return m_originalSaveVersion; } + ESavePlatform getSavePlatform() { return m_savePlatform; } + void setPlatform(ESavePlatform plat) { m_savePlatform = plat; } + bool isSaveEndianDifferent() { return m_saveEndian != m_localEndian; } + void setLocalPlatform() { m_savePlatform = SAVE_FILE_PLATFORM_LOCAL; m_saveEndian = m_localEndian; } + ByteOrder getSaveEndian() { return m_saveEndian; } + static ByteOrder getLocalEndian() { return m_localEndian; } + void setEndian(ByteOrder endian) { m_saveEndian = endian; } + static ByteOrder getEndian(ESavePlatform plat); + bool isLocalEndianDifferent(ESavePlatform plat){return m_localEndian != getEndian(plat); } + +}; diff --git a/Minecraft.World/FileInputStream.cpp b/Minecraft.World/FileInputStream.cpp new file mode 100644 index 00000000..7c0e844f --- /dev/null +++ b/Minecraft.World/FileInputStream.cpp @@ -0,0 +1,204 @@ +#include "stdafx.h" + +#include "File.h" +#include "FileInputStream.h" + +//Creates a FileInputStream by opening a connection to an actual file, the file named by the File object file in the file system. +//A new FileDescriptor object is created to represent this file connection. +//First, if there is a security manager, its checkRead method is called with the path represented by the file argument as its argument. +// +//If the named file does not exist, is a directory rather than a regular file, or for some other reason cannot be opened for reading +//then a FileNotFoundException is thrown. +// +//Parameters: +//file - the file to be opened for reading. +//Throws: +//FileNotFoundException - if the file does not exist, is a directory rather than a regular file, or for some other reason cannot be +//opened for reading. +//SecurityException - if a security manager exists and its checkRead method denies read access to the file. +FileInputStream::FileInputStream(const File &file) +{ + const char *pchFilename=wstringtofilename(file.getPath()); +#ifdef _UNICODE + m_fileHandle = CreateFile( + file.getPath().c_str(), // file name + GENERIC_READ, // access mode + 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... + NULL, // Unused + OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it + FILE_FLAG_SEQUENTIAL_SCAN, // file attributes + NULL // Unsupported + ); +#else + m_fileHandle = CreateFile( + pchFilename, // file name + GENERIC_READ, // access mode + 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... + NULL, // Unused + OPEN_EXISTING , // how to create // TODO 4J Stu - Assuming that the file already exists if we are opening to read from it + FILE_FLAG_SEQUENTIAL_SCAN, // file attributes + NULL // Unsupported + ); +#endif + + if( m_fileHandle == INVALID_HANDLE_VALUE ) + { + // TODO 4J Stu - Any form of error/exception handling + //__debugbreak(); + app.FatalLoadError(); + } +} + +FileInputStream::~FileInputStream() +{ + if( m_fileHandle != INVALID_HANDLE_VALUE ) + CloseHandle( m_fileHandle ); +} + +//Reads a byte of data from this input stream. This method blocks if no input is yet available. +//Returns: +//the next byte of data, or -1 if the end of the file is reached. +int FileInputStream::read() +{ + byte byteRead = 0; + DWORD numberOfBytesRead; + + BOOL bSuccess = ReadFile( + m_fileHandle, // handle to file + &byteRead, // data buffer + 1, // number of bytes to read + &numberOfBytesRead, // number of bytes read + NULL // overlapped buffer + ); + + if( bSuccess==FALSE ) + { + // TODO 4J Stu - Some kind of error handling + app.FatalLoadError(); + //return -1; + } + else if( numberOfBytesRead == 0 ) + { + // File pointer is past the end of the file + return -1; + } + + return byteRead; +} + +//Reads up to b.length bytes of data from this input stream into an array of bytes. This method blocks until some input is available. +//Parameters: +//b - the buffer into which the data is read. +//Returns: +//the total number of bytes read into the buffer, or -1 if there is no more data because the end of the file has been reached. +int FileInputStream::read(byteArray b) +{ + DWORD numberOfBytesRead; + + BOOL bSuccess = ReadFile( + m_fileHandle, // handle to file + b.data, // data buffer + b.length, // number of bytes to read + &numberOfBytesRead, // number of bytes read + NULL // overlapped buffer + ); + + if( bSuccess==FALSE ) + { + // TODO 4J Stu - Some kind of error handling + app.FatalLoadError(); + //return -1; + } + else if( numberOfBytesRead == 0 ) + { + // File pointer is past the end of the file + return -1; + } + + return numberOfBytesRead; +} + +//Reads up to len bytes of data from this input stream into an array of bytes. If len is not zero, the method blocks until some input +//is available; otherwise, no bytes are read and 0 is returned. +//Parameters: +//b - the buffer into which the data is read. +//off - the start offset in the destination array b +//len - the maximum number of bytes read. +//Returns: +//the total number of bytes read into the buffer, or -1 if there is no more data because the end of the file has been reached. +int FileInputStream::read(byteArray b, unsigned int offset, unsigned int length) +{ + // 4J Stu - We don't want to read any more than the array buffer can hold + assert( length <= ( b.length - offset ) ); + + DWORD numberOfBytesRead; + + BOOL bSuccess = ReadFile( + m_fileHandle, // handle to file + &b[offset], // data buffer + length, // number of bytes to read + &numberOfBytesRead, // number of bytes read + NULL // overlapped buffer + ); + + if( bSuccess==FALSE ) + { + // TODO 4J Stu - Some kind of error handling + app.FatalLoadError(); + //return -1; + } + else if( numberOfBytesRead == 0 ) + { + // File pointer is past the end of the file + return -1; + } + + return numberOfBytesRead; +} + +//Closes this file input stream and releases any system resources associated with the stream. +//If this stream has an associated channel then the channel is closed as well. +void FileInputStream::close() +{ + if(m_fileHandle==INVALID_HANDLE_VALUE) + { + //printf("\n\nFileInputStream::close - TRYING TO CLOSE AN INVALID FILE HANDLE\n\n"); + return; + } + + BOOL result = CloseHandle( m_fileHandle ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + } + + // Stop the dtor from trying to close it again + m_fileHandle = INVALID_HANDLE_VALUE; +} + + +//Skips n bytes of input from this input stream. Fewer bytes might be skipped if the end of the input stream is reached. The actual number k of bytes to be skipped is equal to the smaller of n and count-pos. The value k is added into pos and k is returned. +//Overrides: +//skip in class InputStream +//Parameters: +//n - the number of bytes to be skipped. +//Returns: +//the actual number of bytes skipped. +__int64 FileInputStream::skip(__int64 n) +{ +#ifdef _XBOX + LARGE_INTEGER li; + li.QuadPart = n; + li.LowPart = SetFilePointer(m_fileHandle, li.LowPart, &li.HighPart, FILE_CURRENT); + + if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) + { + li.QuadPart = 0; + } + + return li.QuadPart; +#else + return 0; +#endif +} \ No newline at end of file diff --git a/Minecraft.World/FileInputStream.h b/Minecraft.World/FileInputStream.h new file mode 100644 index 00000000..e855a2d7 --- /dev/null +++ b/Minecraft.World/FileInputStream.h @@ -0,0 +1,20 @@ +#pragma once +// 4J Stu - Represents Java standard library class + +#include "InputStream.h" + +class FileInputStream : public InputStream +{ +public: + FileInputStream(const File &file); + virtual ~FileInputStream(); + virtual int read(); + virtual int read(byteArray b); + virtual int read(byteArray b, unsigned int offset, unsigned int length); + virtual void close(); + virtual __int64 skip(__int64 n); + +private: + HANDLE m_fileHandle; + +}; \ No newline at end of file diff --git a/Minecraft.World/FileOutputStream.cpp b/Minecraft.World/FileOutputStream.cpp new file mode 100644 index 00000000..557ba812 --- /dev/null +++ b/Minecraft.World/FileOutputStream.cpp @@ -0,0 +1,153 @@ +#include "stdafx.h" +#include "File.h" +#include "FileOutputStream.h" + +//Creates a file output stream to write to the file represented by the specified File object. A new FileDescriptor object is +//created to represent this file connection. +//First, if there is a security manager, its checkWrite method is called with the path represented by the file argument as its argument. +// +//If the file exists but is a directory rather than a regular file, does not exist but cannot be created, or cannot be opened +//for any other reason then a FileNotFoundException is thrown. +// +//Parameters: +//file - the file to be opened for writing. +FileOutputStream::FileOutputStream(const File &file) : m_fileHandle( INVALID_HANDLE_VALUE ) +{ + if( file.exists() && file.isDirectory()) + { + // TODO 4J Stu - FileNotFoundException + return; + } + +#ifdef _DURANGO + m_fileHandle = CreateFile( + file.getPath().c_str() , // file name + GENERIC_WRITE, // access mode + 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... + NULL, // Unused + OPEN_ALWAYS , // how to create + FILE_ATTRIBUTE_NORMAL , // file attributes + NULL // Unsupported + ); +#else + m_fileHandle = CreateFile( + wstringtofilename(file.getPath()) , // file name + GENERIC_WRITE, // access mode + 0, // share mode // TODO 4J Stu - Will we need to share file? Probably not but... + NULL, // Unused + OPEN_ALWAYS , // how to create + FILE_ATTRIBUTE_NORMAL , // file attributes + NULL // Unsupported + ); +#endif + + if( m_fileHandle == INVALID_HANDLE_VALUE ) + { + DWORD error = GetLastError(); + // TODO 4J Stu - Any form of error/exception handling + } +} + +FileOutputStream::~FileOutputStream() +{ + if( m_fileHandle != INVALID_HANDLE_VALUE ) + CloseHandle( m_fileHandle ); +} + +//Writes the specified byte to this file output stream. Implements the write method of OutputStream. +//Parameters: +//b - the byte to be written. +void FileOutputStream::write(unsigned int b) +{ + DWORD numberOfBytesWritten; + + byte value = (byte) b; + + BOOL result = WriteFile( + m_fileHandle, // handle to file + &value, // data buffer + 1, // number of bytes to write + &numberOfBytesWritten, // number of bytes written + NULL // overlapped buffer + ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + } + else if( numberOfBytesWritten == 0 ) + { + // File pointer is past the end of the file + } +} + +//Writes b.length bytes from the specified byte array to this file output stream. +//Parameters: +//b - the data. +void FileOutputStream::write(byteArray b) +{ + DWORD numberOfBytesWritten; + + BOOL result = WriteFile( + m_fileHandle, // handle to file + &b.data, // data buffer + b.length, // number of bytes to write + &numberOfBytesWritten, // number of bytes written + NULL // overlapped buffer + ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + } + else if( numberOfBytesWritten == 0 || numberOfBytesWritten != b.length ) + { + // File pointer is past the end of the file + } +} + +//Writes len bytes from the specified byte array starting at offset off to this file output stream. +//Parameters: +//b - the data. +//off - the start offset in the data. +//len - the number of bytes to write. +void FileOutputStream::write(byteArray b, unsigned int offset, unsigned int length) +{ + // 4J Stu - We don't want to write any more than the array buffer holds + assert( length <= ( b.length - offset ) ); + + DWORD numberOfBytesWritten; + + BOOL result = WriteFile( + m_fileHandle, // handle to file + &b[offset], // data buffer + length, // number of bytes to write + &numberOfBytesWritten, // number of bytes written + NULL // overlapped buffer + ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + } + else if( numberOfBytesWritten == 0 || numberOfBytesWritten != length ) + { + // File pointer is past the end of the file + } +} +// +//Closes this file output stream and releases any system resources associated with this stream. +//This file output stream may no longer be used for writing bytes. +//If this stream has an associated channel then the channel is closed as well. +void FileOutputStream::close() +{ + BOOL result = CloseHandle( m_fileHandle ); + + if( result == 0 ) + { + // TODO 4J Stu - Some kind of error handling + } + + // Stop the dtor from trying to close it again + m_fileHandle = INVALID_HANDLE_VALUE; +} diff --git a/Minecraft.World/FileOutputStream.h b/Minecraft.World/FileOutputStream.h new file mode 100644 index 00000000..bb8df059 --- /dev/null +++ b/Minecraft.World/FileOutputStream.h @@ -0,0 +1,19 @@ +#pragma once +// 4J Stu - Represents Java standard lib abstract + +#include "OutputStream.h" + +class FileOutputStream : public OutputStream +{ +public: + FileOutputStream(const File &file); + virtual ~FileOutputStream(); + virtual void write(unsigned int b); + virtual void write(byteArray b); + virtual void write(byteArray b, unsigned int offset, unsigned int length); + virtual void close(); + virtual void flush() {} + +private: + HANDLE m_fileHandle; +}; \ No newline at end of file diff --git a/Minecraft.World/FilenameFilter.h b/Minecraft.World/FilenameFilter.h new file mode 100644 index 00000000..2c86c261 --- /dev/null +++ b/Minecraft.World/FilenameFilter.h @@ -0,0 +1,13 @@ +#pragma once +using namespace std; + +#include "stdafx.h" + +class File; + +// 4J Jev, java lirary interface. +class FilenameFilter +{ +public: + virtual bool accept(File *dir, const wstring& name) = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/FireAspectEnchantment.cpp b/Minecraft.World/FireAspectEnchantment.cpp new file mode 100644 index 00000000..084782ee --- /dev/null +++ b/Minecraft.World/FireAspectEnchantment.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "FireAspectEnchantment.h" + +FireAspectEnchantment::FireAspectEnchantment(int id, int frequency) : Enchantment(id, frequency, EnchantmentCategory::weapon) +{ + setDescriptionId(IDS_ENCHANTMENT_FIRE); +} + +int FireAspectEnchantment::getMinCost(int level) +{ + return 10 + 20 * (level - 1); +} + +int FireAspectEnchantment::getMaxCost(int level) +{ + return Enchantment::getMinCost(level) + 50; +} + +int FireAspectEnchantment::getMaxLevel() +{ + return 2; +} \ No newline at end of file diff --git a/Minecraft.World/FireAspectEnchantment.h b/Minecraft.World/FireAspectEnchantment.h new file mode 100644 index 00000000..78b53611 --- /dev/null +++ b/Minecraft.World/FireAspectEnchantment.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Enchantment.h" + +class FireAspectEnchantment : public Enchantment +{ +public: + FireAspectEnchantment(int id, int frequency); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); +}; \ No newline at end of file diff --git a/Minecraft.World/FireChargeItem.cpp b/Minecraft.World/FireChargeItem.cpp new file mode 100644 index 00000000..c4e47b0c --- /dev/null +++ b/Minecraft.World/FireChargeItem.cpp @@ -0,0 +1,67 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.h" +#include "SoundTypes.h" +#include "FireChargeItem.h" +#include "tile.h" + +FireChargeItem::FireChargeItem(int id) : Item(id) +{ + m_dragonFireballIcon = NULL; +} + +bool FireChargeItem::useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + if (level->isClientSide) + { + return true; + } + + if (face == 0) y--; + if (face == 1) y++; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + + if (!player->mayBuild(x, y, z)) + { + return false; + } + + // 4J-PB - Adding a test only version to allow tooltips to be displayed + if(bTestUseOnOnly) + { + return true; + } + + int targetType = level->getTile(x, y, z); + + if (targetType == 0) + { + level->playSound( x + 0.5, y + 0.5, z + 0.5,eSoundType_FIRE_IGNITE, 1, random->nextFloat() * 0.4f + 0.8f); + level->setTile(x, y, z, Tile::fire_Id); + } + + if (!player->abilities.instabuild) + { + itemInstance->count--; + } + return true; +} + +Icon *FireChargeItem::getIcon(int itemAuxValue) +{ + if(itemAuxValue > 0) return m_dragonFireballIcon; + return Item::getIcon(itemAuxValue); +} + +void FireChargeItem::registerIcons(IconRegister *iconRegister) +{ + Item::registerIcons(iconRegister); + m_dragonFireballIcon = iconRegister->registerIcon(L"dragonFireball"); +} + diff --git a/Minecraft.World/FireChargeItem.h b/Minecraft.World/FireChargeItem.h new file mode 100644 index 00000000..c0890a46 --- /dev/null +++ b/Minecraft.World/FireChargeItem.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Item.h" + +class ItemInstance; +class Player; +class Level; + +class FireChargeItem : public Item +{ +private: + Icon *m_dragonFireballIcon; +public: + FireChargeItem(int id); + + virtual bool useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly); + + virtual Icon *getIcon(int itemAuxValue); + virtual void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/FireTile.cpp b/Minecraft.World/FireTile.cpp new file mode 100644 index 00000000..60f062db --- /dev/null +++ b/Minecraft.World/FireTile.cpp @@ -0,0 +1,407 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.level.tile.h" +#include "FireTile.h" +#include "SoundTypes.h" +#include "..\Minecraft.Client\MinecraftServer.h" +#include "..\Minecraft.Client\PlayerList.h" + +// AP - added for Vita to set Alpha Cut out +#include "IntBuffer.h" +#include "..\Minecraft.Client\Tesselator.h" + + +const wstring FireTile::TEXTURE_FIRST = L"fire_0"; +const wstring FireTile::TEXTURE_SECOND = L"fire_1"; + +FireTile::FireTile(int id) : Tile(id, Material::fire,isSolidRender()) +{ + flameOdds = new int[256]; + memset( flameOdds,0,sizeof(int)*256); + + burnOdds = new int[256]; + memset( burnOdds,0,sizeof(int)*256); + + icons = NULL; + + setTicking(true); +} + +FireTile::~FireTile() +{ + delete [] flameOdds; + delete [] burnOdds; +} + +void FireTile::init() +{ + setFlammable(Tile::wood_Id, FLAME_HARD, BURN_MEDIUM); + setFlammable(Tile::woodSlab_Id, FLAME_HARD, BURN_MEDIUM); + setFlammable(Tile::woodSlabHalf_Id, FLAME_HARD, BURN_MEDIUM); + setFlammable(Tile::fence_Id, FLAME_HARD, BURN_MEDIUM); + setFlammable(Tile::stairs_wood_Id, FLAME_HARD, BURN_MEDIUM); + setFlammable(Tile::stairs_birchwood_Id, FLAME_HARD, BURN_MEDIUM); + setFlammable(Tile::stairs_sprucewood_Id, FLAME_HARD, BURN_MEDIUM); + setFlammable(Tile::stairs_junglewood_Id, FLAME_HARD, BURN_MEDIUM); + setFlammable(Tile::treeTrunk_Id, FLAME_HARD, BURN_HARD); + setFlammable(Tile::leaves_Id, FLAME_EASY, BURN_EASY); + setFlammable(Tile::bookshelf_Id, FLAME_EASY, BURN_MEDIUM); + setFlammable(Tile::tnt_Id, FLAME_MEDIUM, BURN_INSTANT); + setFlammable(Tile::tallgrass_Id, FLAME_INSTANT, BURN_INSTANT); + setFlammable(Tile::cloth_Id, FLAME_EASY, BURN_EASY); + setFlammable(Tile::vine_Id, FLAME_MEDIUM, BURN_INSTANT); +} + +void FireTile::setFlammable(int id, int flame, int burn) +{ + flameOdds[id] = flame; + burnOdds[id] = burn; +} + +AABB *FireTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +bool FireTile::blocksLight() +{ + return false; +} + +bool FireTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool FireTile::isCubeShaped() +{ + return false; +} + +int FireTile::getRenderShape() +{ + return Tile::SHAPE_FIRE; +} + +int FireTile::getResourceCount(Random *random) +{ + return 0; +} + +int FireTile::getTickDelay() +{ + return 30; +} + +void FireTile::tick(Level *level, int x, int y, int z, Random *random) +{ + // 4J added - we don't want fire to do anything that might create new fire, or destroy this fire, if we aren't actually tracking (for network) the chunk this is in in the player + // chunk map. If we did change something in that case, then the change wouldn't get sent to any player that had already received that full chunk, and so we'd just become desynchronised. + // Seems safest just to do an addToTickNextTick here instead with a decent delay, to make sure that we will get ticked again in the future, when we might again be in a chunk + // that is being tracked. + if( !level->isClientSide ) // Note - should only be being ticked on the server + { + if( !MinecraftServer::getInstance()->getPlayers()->isTrackingTile(x, y, z, level->dimension->id) ) + { + level->addToTickNextTick(x, y, z, id, getTickDelay() * 5); + return; + } + } + + + bool infiniBurn = level->getTile(x, y - 1, z) == Tile::hellRock_Id; + if (level->dimension->id == 1) // 4J - was == instanceof TheEndDimension + { + if (level->getTile(x, y - 1, z) == Tile::unbreakable_Id) infiniBurn = true; + } + + if (!mayPlace(level, x, y, z)) + { + level->setTile(x, y, z, 0); + } + + if (!infiniBurn && level->isRaining()) + { + if (level->isRainingAt(x, y, z) || level->isRainingAt(x - 1, y, z) || level->isRainingAt(x + 1, y, z) || level->isRainingAt(x, y, z - 1) || level->isRainingAt(x, y, z + 1)) { + + level->setTile(x, y, z, 0); + return; + } + } + + int age = level->getData(x, y, z); + if (age < 15) + { + level->setDataNoUpdate(x, y, z, age + random->nextInt(3) / 2); + } + level->addToTickNextTick(x, y, z, id, getTickDelay() + random->nextInt(10)); + + if (!infiniBurn && !isValidFireLocation(level, x, y, z)) + { + if (!level->isTopSolidBlocking(x, y - 1, z) || age > 3) level->setTile(x, y, z, 0); + return; + } + + if (!infiniBurn && !canBurn(level, x, y - 1, z)) + { + if (age == 15 && random->nextInt(4) == 0) + { + level->setTile(x, y, z, 0); + return; + } + } + + bool isHumid = level->isHumidAt(x, y, z); + int extra = 0; + if (isHumid) + { + extra = -50; + } + checkBurnOut(level, x + 1, y, z, 300 + extra, random, age); + checkBurnOut(level, x - 1, y, z, 300 + extra, random, age); + checkBurnOut(level, x, y - 1, z, 250 + extra, random, age); + checkBurnOut(level, x, y + 1, z, 250 + extra, random, age); + checkBurnOut(level, x, y, z - 1, 300 + extra, random, age); + checkBurnOut(level, x, y, z + 1, 300 + extra, random, age); + if( app.GetGameHostOption(eGameHostOption_FireSpreads) ) + { + for (int xx = x - 1; xx <= x + 1; xx++) + { + for (int zz = z - 1; zz <= z + 1; zz++) + { + for (int yy = y - 1; yy <= y + 4; yy++) + { + if (xx == x && yy == y && zz == z) continue; + + int rate = 100; + if (yy > y + 1) + { + rate += ((yy - (y + 1)) * 100); + } + + int fodds = getFireOdds(level, xx, yy, zz); + if (fodds > 0) { + int odds = (fodds + 40) / (age + 30); + if (isHumid) + { + odds /= 2; + } + if (odds > 0 && random->nextInt(rate) <= odds) + { + if ((level->isRaining() && level->isRainingAt(xx, yy, zz)) || level->isRainingAt(xx - 1, yy, z) || level->isRainingAt(xx + 1, yy, zz) || level->isRainingAt(xx, yy, zz - 1) + || level->isRainingAt(xx, yy, zz + 1)) + { + // DO NOTHING, rain! + + } else { + int tAge = age + random->nextInt(5) / 4; + if (tAge > 15) tAge = 15; + level->setTileAndData(xx, yy, zz, this->id, tAge); + + } + } + } + } + } + } + } +} + +void FireTile::checkBurnOut(Level *level, int x, int y, int z, int chance, Random *random, int age) +{ + int odds = burnOdds[level->getTile(x, y, z)]; + if (random->nextInt(chance) < odds) + { + bool wasTnt = level->getTile(x, y, z) == Tile::tnt_Id; + if (random->nextInt(age + 10) < 5 && !level->isRainingAt(x, y, z) && app.GetGameHostOption(eGameHostOption_FireSpreads)) + { + int tAge = age + random->nextInt(5) / 4; + if (tAge > 15) tAge = 15; + level->setTileAndData(x, y, z, this->id, tAge); + } else + { + level->setTile(x, y, z, 0); + } + if (wasTnt) + { + Tile::tnt->destroy(level, x, y, z, TntTile::EXPLODE_BIT); + } + } +} + +bool FireTile::isValidFireLocation(Level *level, int x, int y, int z) +{ + if (canBurn(level, x + 1, y, z)) return true; + if (canBurn(level, x - 1, y, z)) return true; + if (canBurn(level, x, y - 1, z)) return true; + if (canBurn(level, x, y + 1, z)) return true; + if (canBurn(level, x, y, z - 1)) return true; + if (canBurn(level, x, y, z + 1)) return true; + + return false; +} + +int FireTile::getFireOdds(Level *level, int x, int y, int z) +{ + int odds = 0; + if (!level->isEmptyTile(x, y, z)) return 0; + + odds = getFlammability(level, x + 1, y, z, odds); + odds = getFlammability(level, x - 1, y, z, odds); + odds = getFlammability(level, x, y - 1, z, odds); + odds = getFlammability(level, x, y + 1, z, odds); + odds = getFlammability(level, x, y, z - 1, odds); + odds = getFlammability(level, x, y, z + 1, odds); + + return odds; +} + +bool FireTile::mayPick() +{ + return false; +} + +bool FireTile::canBurn(LevelSource *level, int x, int y, int z) +{ + return flameOdds[level->getTile(x, y, z)] > 0; +} + +int FireTile::getFlammability(Level *level, int x, int y, int z, int odds) +{ + int f = flameOdds[level->getTile(x, y, z)]; + if (f > odds) return f; + return odds; +} + +bool FireTile::mayPlace(Level *level, int x, int y, int z) +{ + return level->isTopSolidBlocking(x, y - 1, z) || isValidFireLocation(level, x, y, z); +} + +void FireTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (!level->isTopSolidBlocking(x, y - 1, z) && !isValidFireLocation(level, x, y, z)) + { + level->setTile(x, y, z, 0); + return; + } +} + +void FireTile::onPlace(Level *level, int x, int y, int z) +{ + if (level->dimension->id <= 0 && level->getTile(x, y - 1, z) == Tile::obsidian_Id) + { + if (Tile::portalTile->trySpawnPortal(level, x, y, z, true)) + { + return; + } + } + if (!level->isTopSolidBlocking(x, y - 1, z) && !isValidFireLocation(level, x, y, z)) + { + level->setTile(x, y, z, 0); + return; + } + level->addToTickNextTick(x, y, z, id, getTickDelay() + level->random->nextInt(10)); +} + +bool FireTile::isFlammable(int tile) +{ + return flameOdds[tile] > 0; +} + +void FireTile::animateTick(Level *level, int x, int y, int z, Random *random) +{ + if (random->nextInt(24) == 0) + { + level->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f,eSoundType_FIRE_FIRE, 1 + random->nextFloat(), random->nextFloat() * 0.7f + 0.3f); + } + + if (level->isTopSolidBlocking(x, y - 1, z) || Tile::fire->canBurn(level, x, y - 1, z)) + { + for (int i = 0; i < 3; i++) + { + float xx = x + random->nextFloat(); + float yy = y + random->nextFloat() * 0.5f + 0.5f; + float zz = z + random->nextFloat(); + level->addParticle(eParticleType_largesmoke, xx, yy, zz, 0, 0, 0); + } + } + else + { + if (Tile::fire->canBurn(level, x - 1, y, z)) + { + for (int i = 0; i < 2; i++) + { + float xx = x + random->nextFloat() * 0.1f; + float yy = y + random->nextFloat(); + float zz = z + random->nextFloat(); + level->addParticle(eParticleType_largesmoke, xx, yy, zz, 0, 0, 0); + } + } + if (Tile::fire->canBurn(level, x + 1, y, z)) + { + for (int i = 0; i < 2; i++) + { + float xx = x + 1 - random->nextFloat() * 0.1f; + float yy = y + random->nextFloat(); + float zz = z + random->nextFloat(); + level->addParticle(eParticleType_largesmoke, xx, yy, zz, 0, 0, 0); + } + } + if (Tile::fire->canBurn(level, x, y, z - 1)) + { + for (int i = 0; i < 2; i++) + { + float xx = x + random->nextFloat(); + float yy = y + random->nextFloat(); + float zz = z + random->nextFloat() * 0.1f; + level->addParticle(eParticleType_largesmoke, xx, yy, zz, 0, 0, 0); + } + } + if (Tile::fire->canBurn(level, x, y, z + 1)) + { + for (int i = 0; i < 2; i++) + { + float xx = x + random->nextFloat(); + float yy = y + random->nextFloat(); + float zz = z + 1 - random->nextFloat() * 0.1f; + level->addParticle(eParticleType_largesmoke, xx, yy, zz, 0, 0, 0); + } + } + if (Tile::fire->canBurn(level, x, y + 1, z)) + { + for (int i = 0; i < 2; i++) + { + float xx = x + random->nextFloat(); + float yy = y + 1 - random->nextFloat() * 0.1f; + float zz = z + random->nextFloat(); + level->addParticle(eParticleType_largesmoke, xx, yy, zz, 0, 0, 0); + } + } + } +} + +void FireTile::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[2]; + icons[0] = iconRegister->registerIcon(TEXTURE_FIRST); + icons[1] = iconRegister->registerIcon(TEXTURE_SECOND); +} + +Icon *FireTile::getTextureLayer(int layer) +{ +#ifdef __PSVITA__ + // AP - alpha cut out is expensive on vita. Set the Alpha Cut out flag + Tesselator* t = Tesselator::getInstance(); + t->setAlphaCutOut( true ); +#endif + + return icons[layer]; +} + +Icon *FireTile::getTexture(int face, int data) +{ + return icons[0]; +} diff --git a/Minecraft.World/FireTile.h b/Minecraft.World/FireTile.h new file mode 100644 index 00000000..af079a6a --- /dev/null +++ b/Minecraft.World/FireTile.h @@ -0,0 +1,63 @@ +#pragma once +#include "Tile.h" +#include "Definitions.h" + +class Random; +class ChunkRebuildData; +class FireTile : public Tile +{ + friend class Tile; + friend class ChunkRebuildData; +public: + static const wstring TEXTURE_FIRST; + static const wstring TEXTURE_SECOND; + + static const int FLAME_INSTANT = 60; + static const int FLAME_EASY = 30; + static const int FLAME_MEDIUM = 15; + static const int FLAME_HARD = 5; + + static const int BURN_INSTANT = 100; + static const int BURN_EASY = 60; + static const int BURN_MEDIUM = 20; + static const int BURN_HARD = 5; + static const int BURN_NEVER = 0; + +private: + int *flameOdds; + int *burnOdds; + Icon **icons; +protected: + FireTile(int id); + virtual ~FireTile(); +public: + void init(); +private: + void setFlammable(int id, int flame, int burn); +public: + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual bool blocksLight(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual int getRenderShape(); + virtual int getResourceCount(Random *random); + virtual int getTickDelay(); + virtual void tick(Level *level, int x, int y, int z, Random *random); +private: + void checkBurnOut(Level *level, int x, int y, int z, int chance, Random *random, int age); + bool isValidFireLocation(Level *level, int x, int y, int z); + int getFireOdds(Level *level, int x, int y, int z); +public: + virtual bool mayPick(); + bool canBurn(LevelSource *level, int x, int y, int z); + int getFlammability(Level *level, int x, int y, int z, int odds); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual void onPlace(Level *level, int x, int y, int z); + bool isFlammable(int tile); + virtual void animateTick(Level *level, int x, int y, int z, Random *random); +public: + void registerIcons(IconRegister *iconRegister); + Icon *getTextureLayer(int layer); + Icon *getTexture(int face, int data); +}; \ No newline at end of file diff --git a/Minecraft.World/Fireball.cpp b/Minecraft.World/Fireball.cpp new file mode 100644 index 00000000..46be68f7 --- /dev/null +++ b/Minecraft.World/Fireball.cpp @@ -0,0 +1,415 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.damagesource.h" +#include "com.mojang.nbt.h" +#include "Fireball.h" +#include "net.minecraft.world.level.dimension.h" +#include "SharedConstants.h" + + +// 4J - added common ctor code. +void Fireball::_init() +{ + xTile = -1; + yTile = -1; + zTile = -1; + lastTile = 0; + inGround = false; + flightTime = 0; + + life = 0; + owner = nullptr; + xPower = 0.0; + yPower = 0.0; + zPower = 0.0; +} + +Fireball::Fireball(Level *level) : Entity( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + _init(); + + setSize(16 / 16.0f, 16 / 16.0f); + +} + +void Fireball::defineSynchedData() +{ + +} + +bool Fireball::shouldRenderAtSqrDistance(double distance) +{ + double size = bb->getSize() * 4; + size *= 64.0f; + return distance < size * size; +} + + +Fireball::Fireball(Level *level, double x, double y, double z, double xa, double ya, double za) : Entity( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + _init(); + + setSize(16 / 16.0f, 16 / 16.0f); + + this->moveTo(x, y, z, yRot, xRot); + this->setPos(x, y, z); + + double dd = sqrt(xa * xa + ya * ya + za * za); + + // Fix for #69150 - [CRASH] TU8: Code: Gameplay: Nether portal mechanics can become permanently broken, causing a hard lock upon usage. + // IF xa, ya and za are 0 then dd is 0 and the xa/dd etc return NAN + if(dd == 0.0) + { + xPower = 0.0; + yPower = 0.0; + zPower = 0.0; + } + else + { + xPower = xa / dd * 0.10; + yPower = ya / dd * 0.10; + zPower = za / dd * 0.10; + } +} + +Fireball::Fireball(Level *level, shared_ptr mob, double xa, double ya, double za) : Entity ( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + _init(); + + this->owner = mob; + + setSize(16 / 16.0f, 16 / 16.0f); + + this->moveTo(mob->x, mob->y, mob->z, mob->yRot, mob->xRot); + this->setPos(x, y, z); + this->heightOffset = 0; + + + xd = yd = zd = 0.0; + + xa += random->nextGaussian() * 0.4; + ya += random->nextGaussian() * 0.4; + za += random->nextGaussian() * 0.4; + double dd = sqrt(xa * xa + ya * ya + za * za); + + // Fix for #69150 - [CRASH] TU8: Code: Gameplay: Nether portal mechanics can become permanently broken, causing a hard lock upon usage. + // IF xa, ya and za are 0 then dd is 0 and the xa/dd etc return NAN + if(dd == 0.0) + { + xPower = 0.0; + yPower = 0.0; + zPower = 0.0; + } + else + { + xPower = xa / dd * 0.10; + yPower = ya / dd * 0.10; + zPower = za / dd * 0.10; + } +} + +void Fireball::tick() +{ + // 4J-PB - Moved forward from 1.2.3 + //if (!level->isClientSide && (owner == NULL || owner->removed)) + if (!level->isClientSide) + { + if((owner != NULL && owner->removed) || !level->hasChunkAt((int) x, (int) y, (int) z)) + { + app.DebugPrintf("Fireball removed - owner is null or removed is true for owner\n"); + remove(); + return; + } + else + { + // 4J-PB - TU9 bug fix - fireballs can hit the edge of the world, and stay there + int minXZ = - (level->dimension->getXZSize() * 16 ) / 2; + int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1; + + if ((x<=minXZ) || (x>=maxXZ) || (z<=minXZ) || (z>=maxXZ)) + { + remove(); + app.DebugPrintf("Fireball removed - end of world\n"); + return; + } + } + } + + Entity::tick(); + + //app.DebugPrintf("Fireball x %d, y %d, z%d\n",(int)x,(int)y,(int)z); + + if(shouldBurn()) setOnFire(1); + + if (inGround) + { + int tile = level->getTile(xTile, yTile, zTile); + if (tile == lastTile) + { + life++; + if (life == SharedConstants::TICKS_PER_SECOND * 30) + { + remove(); + app.DebugPrintf("Fireball removed - life is 20*60\n"); + } + return; + } + else + { + inGround = false; + + xd *= random->nextFloat() * 0.2f; + yd *= random->nextFloat() * 0.2f; + zd *= random->nextFloat() * 0.2f; + life = 0; + flightTime = 0; + } + } + else + { + flightTime++; + } + + MemSect(41); + Vec3 *from = Vec3::newTemp(x, y, z); + Vec3 *to = Vec3::newTemp(x + xd, y + yd, z + zd); + HitResult *res = level->clip(from, to); + + from = Vec3::newTemp(x, y, z); + to = Vec3::newTemp(x + xd, y + yd, z + zd); + if (res != NULL) + { + to = Vec3::newTemp(res->pos->x, res->pos->y, res->pos->z); + } + shared_ptr hitEntity = nullptr; + vector > *objects = level->getEntities(shared_from_this(), this->bb->expand(xd, yd, zd)->grow(1, 1, 1)); + double nearest = 0; + AUTO_VAR(itEnd, objects->end()); + for (AUTO_VAR(it, objects->begin()); it != itEnd; it++) + { + shared_ptr e = *it; //objects->at(i); + if (!e->isPickable() || (e->is(owner) )) continue; //4J Stu - Never collide with the owner (Enderdragon) // && flightTime < 25)) continue; + + float rr = 0.3f; + AABB *bb = e->bb->grow(rr, rr, rr); + HitResult *p = bb->clip(from, to); + if (p != NULL) + { + double dd = from->distanceTo(p->pos); + if (dd < nearest || nearest == 0) + { + hitEntity = e; + nearest = dd; + } + delete p; + } + + } + + if (hitEntity != NULL) + { + delete res; + res = new HitResult(hitEntity); + } + MemSect(0); + + if (res != NULL) + { + onHit(res); + } + delete res; + x += xd; + y += yd; + z += zd; + + double sd = sqrt(xd * xd + zd * zd); + yRot = (float) (atan2(xd, zd) * 180 / PI); + xRot = (float) (atan2(yd, sd) * 180 / PI); + + while (xRot - xRotO < -180) + xRotO -= 360; + while (xRot - xRotO >= 180) + xRotO += 360; + + while (yRot - yRotO < -180) + yRotO -= 360; + while (yRot - yRotO >= 180) + yRotO += 360; + + xRot = xRotO + (xRot - xRotO) * 0.2f; + yRot = yRotO + (yRot - yRotO) * 0.2f; + + + float inertia = 0.95f; + if (isInWater()) + { + for (int i = 0; i < 4; i++) + { + float s = 1 / 4.0f; + level->addParticle(eParticleType_bubble, x - xd * s, y - yd * s, z - zd * s, xd, yd, zd); + } + inertia = 0.80f; + } + + xd += xPower; + yd += yPower; + zd += zPower; + xd *= inertia; + yd *= inertia; + zd *= inertia; + + // 4J-PB - bug fix for the fireballs in a saved game - they are saved with no/very small velocity, so end up hanging around in the air + if (!level->isClientSide) + { + if((abs(xd)<0.002) && (abs(yd)<0.002) && (abs(zd)<0.002)) + { + xd=0.0; + zd=0.0; + yd=0.0; + app.DebugPrintf("Removing a fireball with zero velocity\n"); + remove(); + } + } + + level->addParticle(getTrailParticleType(), x, y + 0.5f, z, 0, 0.01, 0); + + setPos(x, y, z); +} + +void Fireball::onHit(HitResult *res) +{ + if (!level->isClientSide) + { + if (res->entity != NULL) + { + DamageSource *damageSource = DamageSource::fireball(dynamic_pointer_cast( shared_from_this() ), owner); + if (res->entity->hurt(damageSource, 6)) + { + } + else + { + } + delete damageSource; + } + + bool destroyBlocks = true;//level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING); + level->explode(nullptr, x, y, z, 1, true, destroyBlocks); + + remove(); + } +} + +void Fireball::addAdditonalSaveData(CompoundTag *tag) +{ + tag->putShort(L"xTile", (short) xTile); + tag->putShort(L"yTile", (short) yTile); + tag->putShort(L"zTile", (short) zTile); + tag->putByte(L"inTile", (byte) lastTile); + tag->putByte(L"inGround", (byte) (inGround ? 1 : 0)); + tag->put(L"direction", this->newDoubleList(3, this->xd, this->yd, this->zd)); +} + +void Fireball::readAdditionalSaveData(CompoundTag *tag) +{ + xTile = tag->getShort(L"xTile"); + yTile = tag->getShort(L"yTile"); + zTile = tag->getShort(L"zTile"); + lastTile = tag->getByte(L"inTile") & 0xff; + inGround = tag->getByte(L"inGround") == 1; + + // Load the stored direction and apply it to the fireball + // if it has no stored direction, remove it. + if (tag->contains(L"direction")) + { + ListTag *listTag = (ListTag *)tag->getList(L"direction"); + this->xd = ((DoubleTag *) listTag->get(0))->data; + this->yd = ((DoubleTag *) listTag->get(1))->data; + this->zd = ((DoubleTag *) listTag->get(2))->data; + } + else + { + this->remove(); + } +} + +bool Fireball::isPickable() +{ + return true; +} + +float Fireball::getPickRadius() +{ + return 1; +} + +bool Fireball::hurt(DamageSource *source, int damage) +{ + markHurt(); + + if (source->getEntity() != NULL) + { + Vec3 *lookAngle = source->getEntity()->getLookAngle(); + if (lookAngle != NULL) + { + xd = lookAngle->x; + yd = lookAngle->y; + zd = lookAngle->z; + xPower = xd * 0.1; + yPower = yd * 0.1; + zPower = zd * 0.1; + } + shared_ptr mob = dynamic_pointer_cast( source->getEntity() ); + if (mob != NULL) + { + owner = mob; + } + return true; + } + return false; +} + +float Fireball::getShadowHeightOffs() +{ + return 0; +} + +float Fireball::getBrightness(float a) +{ + return 1.0f; +} + +int Fireball::getLightColor(float a) +{ + return 15 << 20 | 15 << 4; +} + +bool Fireball::shouldBurn() +{ + return true; +} + +int Fireball::getIcon() +{ + return 14 + 2 * 16; +} + +ePARTICLE_TYPE Fireball::getTrailParticleType() +{ + return eParticleType_smoke; +} \ No newline at end of file diff --git a/Minecraft.World/Fireball.h b/Minecraft.World/Fireball.h new file mode 100644 index 00000000..c9471625 --- /dev/null +++ b/Minecraft.World/Fireball.h @@ -0,0 +1,73 @@ +#pragma once +using namespace std; + +#include "Entity.h" +#include "ParticleTypes.h" + +class HitResult; + +class Fireball : public Entity +{ +public: + eINSTANCEOF GetType() { return eTYPE_FIREBALL; } + static Entity *create(Level *level) { return new Fireball(level); } + +private: + int xTile; + int yTile; + int zTile; + int lastTile; + +private: + bool inGround; + +public: + shared_ptr owner; + +private: + int life; + int flightTime; + + // 4J - added common ctor code. + void _init(); + +public: + double xPower, yPower, zPower; + + Fireball(Level *level); + +protected: + virtual void defineSynchedData(); + +public: + virtual bool shouldRenderAtSqrDistance(double distance); + + Fireball(Level *level, double x, double y, double z, double xa, double ya, double za); + Fireball(Level *level, shared_ptr mob, double xa, double ya, double za); + +public: + virtual void tick(); + +protected: + virtual void onHit(HitResult *res); + +public: + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual bool isPickable(); + virtual float getPickRadius(); + virtual bool hurt(DamageSource *source, int damage); + virtual float getShadowHeightOffs(); + virtual float getBrightness(float a); + virtual int getLightColor(float a); + + // 4J Added TU9 + virtual bool shouldBurn(); + virtual int getIcon(); + +protected: +// 4J Added TU9 + virtual ePARTICLE_TYPE getTrailParticleType(); +}; + + diff --git a/Minecraft.World/FishingHook.cpp b/Minecraft.World/FishingHook.cpp new file mode 100644 index 00000000..efa2dbc6 --- /dev/null +++ b/Minecraft.World/FishingHook.cpp @@ -0,0 +1,450 @@ +#include "stdafx.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.damagesource.h" +#include "com.mojang.nbt.h" +#include "FishingHook.h" +#include "SoundTypes.h" + + + +// 4J - added common ctor code. +void FishingHook::_init() +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + xTile = -1; + yTile = -1; + zTile = -1; + lastTile = 0; + inGround = false; + shakeTime = 0; + flightTime = 0; + nibble = 0; + hookedIn = nullptr; + + lSteps = 0; + lx = 0.0; + ly = 0.0; + lz = 0.0; + lyr = 0.0; + lxr = 0.0; + lxd = 0.0; + lyd = 0.0; + lzd = 0.0; + owner = nullptr; + life = 0; + + setSize(0.25f, 0.25f); + noCulling = true; +} + +FishingHook::FishingHook(Level *level) : Entity( level ) +{ + _init(); +} + +FishingHook::FishingHook(Level *level, double x, double y, double z, shared_ptr owner) : Entity( level ) +{ + _init(); + + this->owner = owner; + // 4J Stu - Moved this outside the ctor + //owner->fishing = dynamic_pointer_cast( shared_from_this() ); + + setPos(x, y, z); +} + +FishingHook::FishingHook(Level *level, shared_ptr mob) : Entity( level ) +{ + _init(); + + this->owner = mob; + // 4J Stu - Moved this outside the ctor + //owner->fishing = dynamic_pointer_cast( shared_from_this() ); + + this->moveTo(mob->x, mob->y + 1.62 - mob->heightOffset, mob->z, mob->yRot, mob->xRot); + + + x -= Mth::cos(yRot / 180 * PI) * 0.16f; + y -= 0.1f; + z -= Mth::sin(yRot / 180 * PI) * 0.16f; + this->setPos(x, y, z); + this->heightOffset = 0; + + + float speed = 0.4f; + xd = (-Mth::sin(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI)) * speed; + zd = (Mth::cos(yRot / 180 * PI) * Mth::cos(xRot / 180 * PI)) * speed; + yd = (-Mth::sin(xRot / 180 * PI)) * speed; + + shoot(xd, yd, zd, 1.5f, 1); +} + +void FishingHook::defineSynchedData() +{ +} + +bool FishingHook::shouldRenderAtSqrDistance(double distance) +{ + double size = bb->getSize() * 4; + size *= 64.0f; + return distance < size * size; +} + +void FishingHook::shoot(double xd, double yd, double zd, float pow, float uncertainty) +{ + float dist = (float) sqrt(xd * xd + yd * yd + zd * zd); + + xd /= dist; + yd /= dist; + zd /= dist; + + xd += (random->nextGaussian()) * 0.0075f * uncertainty; + yd += (random->nextGaussian()) * 0.0075f * uncertainty; + zd += (random->nextGaussian()) * 0.0075f * uncertainty; + + xd *= pow; + yd *= pow; + zd *= pow; + + this->xd = xd; + this->yd = yd; + this->zd = zd; + + double sd = sqrt(xd * xd + zd * zd); + + yRotO = this->yRot = (float) (atan2(xd, zd) * 180 / PI); + xRotO = this->xRot = (float) (atan2(yd, sd) * 180 / PI); + life = 0; +} + +void FishingHook::lerpTo(double x, double y, double z, float yRot, float xRot, int steps) +{ + lx = x; + ly = y; + lz = z; + lyr = yRot; + lxr = xRot; + + lSteps = steps; + + this->xd = lxd; + this->yd = lyd; + this->zd = lzd; +} + +void FishingHook::lerpMotion(double xd, double yd, double zd) +{ + lxd = this->xd = xd; + lyd = this->yd = yd; + lzd = this->zd = zd; +} + +void FishingHook::tick() +{ + Entity::tick(); + + if (lSteps > 0) + { + double xt = x + (lx - x) / lSteps; + double yt = y + (ly - y) / lSteps; + double zt = z + (lz - z) / lSteps; + + double yrd = Mth::wrapDegrees(lyr - yRot); + + yRot += (float) ( (yrd) / lSteps ); + xRot += (float) ( (lxr - xRot) / lSteps ); + + lSteps--; + this->setPos(xt, yt, zt); + this->setRot(yRot, xRot); + return; + } + + if (!level->isClientSide) + { + shared_ptr selectedItem = owner->getSelectedItem(); + if (owner->removed || !owner->isAlive() || selectedItem == NULL || selectedItem->getItem() != Item::fishingRod || this->distanceToSqr(owner) > 32 * 32) + { + remove(); + owner->fishing = nullptr; + return; + } + + if (hookedIn != NULL) + { + if (hookedIn->removed) hookedIn = nullptr; + else + { + x = hookedIn->x; + y = hookedIn->bb->y0 + hookedIn->bbHeight * 0.8; + z = hookedIn->z; + return; + } + } + } + + if (shakeTime > 0) shakeTime--; + + if (inGround) + { + int tile = level->getTile(xTile, yTile, zTile); + if (tile != lastTile) + { + life++; + if (life == 20 * 60) remove(); + return; + } + else + { + inGround = false; + + xd *= random->nextFloat() * 0.2f; + yd *= random->nextFloat() * 0.2f; + zd *= random->nextFloat() * 0.2f; + life = 0; + flightTime = 0; + } + } + else + { + flightTime++; + } + + Vec3 *from = Vec3::newTemp(x, y, z); + Vec3 *to = Vec3::newTemp(x + xd, y + yd, z + zd); + HitResult *res = level->clip(from, to); + + from = Vec3::newTemp(x, y, z); + to = Vec3::newTemp(x + xd, y + yd, z + zd); + if (res != NULL) + { + to = Vec3::newTemp(res->pos->x, res->pos->y, res->pos->z); + } + shared_ptr hitEntity = nullptr; + vector > *objects = level->getEntities(shared_from_this(), this->bb->expand(xd, yd, zd)->grow(1, 1, 1)); + double nearest = 0; + AUTO_VAR(itEnd, objects->end()); + for (AUTO_VAR(it, objects->begin()); it != itEnd; it++) + { + shared_ptr e = *it; // objects->at(i); + if (!e->isPickable() || (e == owner && flightTime < 5)) continue; + + float rr = 0.3f; + AABB *bb = e->bb->grow(rr, rr, rr); + HitResult *p = bb->clip(from, to); + if (p != NULL) + { + double dd = from->distanceTo(p->pos); + if (dd < nearest || nearest == 0) + { + hitEntity = e; + nearest = dd; + } + delete p; + } + } + + if (hitEntity != NULL) + { + delete res; + res = new HitResult(hitEntity); + } + + if (res != NULL) + { + if (res->entity != NULL) + { + // 4J Stu Move fix for : fix for #48587 - CRASH: Code: Gameplay: Hitting another player with the fishing bobber crashes the game. [Fishing pole, line] + // Incorrect dynamic_pointer_cast used around the shared_from_this() + DamageSource *damageSource = DamageSource::thrown(shared_from_this(), owner); + if (res->entity->hurt(damageSource, 0)) + { + hookedIn = res->entity; + } + delete damageSource; + } + else + { + inGround = true; + } + } + delete res; + + if (inGround) return; + + move(xd, yd, zd); + + double sd = sqrt(xd * xd + zd * zd); + yRot = (float) (atan2(xd, zd) * 180 / PI); + xRot = (float) (atan2(yd, sd) * 180 / PI); + + while (xRot - xRotO < -180) + xRotO -= 360; + while (xRot - xRotO >= 180) + xRotO += 360; + + while (yRot - yRotO < -180) + yRotO -= 360; + while (yRot - yRotO >= 180) + yRotO += 360; + + xRot = xRotO + (xRot - xRotO) * 0.2f; + yRot = yRotO + (yRot - yRotO) * 0.2f; + + + float inertia = 0.92f; + + if (onGround || horizontalCollision) + { + inertia = 0.5f; + } + + int steps = 5; + double waterPercentage = 0; + for (int i = 0; i < steps; i++) + { + double y0 = bb->y0 + (bb->y1 - bb->y0) * (i + 0) / steps - 2 / 16.0f + 2 / 16.0f; + double y1 = bb->y0 + (bb->y1 - bb->y0) * (i + 1) / steps - 2 / 16.0f + 2 / 16.0f; + AABB *bb2 = AABB::newTemp(bb->x0, y0, bb->z0, bb->x1, y1, bb->z1); + if (level->containsLiquid(bb2, Material::water)) + { + waterPercentage += 1.0 / steps; + } + } + + if (waterPercentage > 0) + { + if (nibble > 0) + { + nibble--; + } + else + { + int nibbleOdds = 500; + if (level->isRainingAt( Mth::floor(x), Mth::floor(y) + 1, Mth::floor(z))) nibbleOdds = 300; + + if (random->nextInt(nibbleOdds) == 0) + { + nibble = random->nextInt(30) + 10; + yd -= 0.2f; + level->playSound(shared_from_this(), eSoundType_RANDOM_SPLASH, 0.25f, 1 + (random->nextFloat() - random->nextFloat()) * 0.4f); + float yt = (float) Mth::floor(bb->y0); + for (int i = 0; i < 1 + bbWidth * 20; i++) + { + float xo = (random->nextFloat() * 2 - 1) * bbWidth; + float zo = (random->nextFloat() * 2 - 1) * bbWidth; + level->addParticle(eParticleType_bubble, x + xo, yt + 1, z + zo, xd, yd - random->nextFloat() * 0.2f, zd); + } + for (int i = 0; i < 1 + bbWidth * 20; i++) + { + float xo = (random->nextFloat() * 2 - 1) * bbWidth; + float zo = (random->nextFloat() * 2 - 1) * bbWidth; + level->addParticle(eParticleType_splash, x + xo, yt + 1, z + zo, xd, yd, zd); + } + } + } + + } + + if (nibble > 0) + { + yd -= random->nextFloat() * random->nextFloat() * random->nextFloat() * 0.2; + } + + double bob = waterPercentage * 2 - 1; + yd += 0.04f * bob; + if (waterPercentage > 0) + { + inertia *= 0.9; + yd *= 0.8; + } + + xd *= inertia; + yd *= inertia; + zd *= inertia; + + setPos(x, y, z); +} + +void FishingHook::addAdditonalSaveData(CompoundTag *tag) +{ + tag->putShort(L"xTile", (short) xTile); + tag->putShort(L"yTile", (short) yTile); + tag->putShort(L"zTile", (short) zTile); + tag->putByte(L"inTile", (byte) lastTile); + tag->putByte(L"shake", (byte) shakeTime); + tag->putByte(L"inGround", (byte) (inGround ? 1 : 0)); +} + +void FishingHook::readAdditionalSaveData(CompoundTag *tag) +{ + xTile = tag->getShort(L"xTile"); + yTile = tag->getShort(L"yTile"); + zTile = tag->getShort(L"zTile"); + lastTile = tag->getByte(L"inTile") & 0xff; + shakeTime = tag->getByte(L"shake") & 0xff; + inGround = tag->getByte(L"inGround") == 1; +} + +float FishingHook::getShadowHeightOffs() +{ + return 0; +} + +int FishingHook::retrieve() +{ + if (level->isClientSide) return 0; + + int dmg = 0; + if (hookedIn != NULL) + { + double xa = owner->x - x; + double ya = owner->y - y; + double za = owner->z - z; + + double dist = sqrt(xa * xa + ya * ya + za * za); + double speed = 0.1; + hookedIn->xd += xa * speed; + hookedIn->yd += ya * speed + sqrt(dist) * 0.08; + hookedIn->zd += za * speed; + dmg = 3; + } + else if (nibble > 0) + { + shared_ptr ie = shared_ptr( new ItemEntity(this->Entity::level, x, y, z, shared_ptr( new ItemInstance(Item::fish_raw) ) ) ); + double xa = owner->x - x; + double ya = owner->y - y; + double za = owner->z - z; + + double dist = sqrt(xa * xa + ya * ya + za * za); + double speed = 0.1; + ie->Entity::xd = xa * speed; + ie->Entity::yd = ya * speed + sqrt(dist) * 0.08; + ie->Entity::zd = za * speed; + level->addEntity(ie); + owner->level->addEntity( shared_ptr( new ExperienceOrb(owner->level, owner->x, owner->y + 0.5f, owner->z + 0.5f, random->nextInt(3) + 1) ) ); // 4J Stu brought forward from 1.4 + dmg = 1; + } + if (inGround) dmg = 2; + + remove(); + owner->fishing = nullptr; + return dmg; +} + +// 4J Stu - Brought forward from 1.4 +void FishingHook::remove() +{ + Entity::remove(); + if (owner != NULL) owner->fishing = nullptr; +} \ No newline at end of file diff --git a/Minecraft.World/FishingHook.h b/Minecraft.World/FishingHook.h new file mode 100644 index 00000000..ec91e630 --- /dev/null +++ b/Minecraft.World/FishingHook.h @@ -0,0 +1,63 @@ +#pragma once +using namespace std; + +#include "Entity.h" + +class Player; + +class FishingHook : public Entity +{ +public: + eINSTANCEOF GetType() { return eTYPE_FISHINGHOOK; } + +private: + int xTile; + int yTile; + int zTile; + int lastTile; + bool inGround; + +public: + int shakeTime; + shared_ptr owner; + +private: + int life; + int flightTime; + int nibble; + +public: + shared_ptr hookedIn; + +private: + void _init(); + +public: + FishingHook(Level *level); + FishingHook(Level *level, double x, double y, double z, shared_ptr owner); + FishingHook(Level *level, shared_ptr mob); + +protected: + virtual void defineSynchedData(); + +public: + bool shouldRenderAtSqrDistance(double distance); + void shoot(double xd, double yd, double zd, float pow, float uncertainty); + +private: + int lSteps; + double lx, ly, lz, lyr, lxr; + double lxd, lyd, lzd; + +public: + virtual void lerpTo(double x, double y, double z, float yRot, float xRot, int steps); + virtual void lerpMotion(double xd, double yd, double zd); + virtual void tick(); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual float getShadowHeightOffs(); + int retrieve(); + + // 4J Stu - Brought forward from 1.4 + virtual void remove(); +}; diff --git a/Minecraft.World/FishingRodItem.cpp b/Minecraft.World/FishingRodItem.cpp new file mode 100644 index 00000000..858005e0 --- /dev/null +++ b/Minecraft.World/FishingRodItem.cpp @@ -0,0 +1,67 @@ +#include "stdafx.h" +#include "net.minecraft.network.packet.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.chunk.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.level.saveddata.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.h" +#include "ItemInstance.h" +#include "FishingRodItem.h" +#include "SoundTypes.h" + +const wstring FishingRodItem::TEXTURE_EMPTY = L"fishingRod_empty"; + +FishingRodItem::FishingRodItem(int id) : Item(id) +{ + setMaxDamage(64); + setMaxStackSize(1); + emptyIcon = NULL; +} + +bool FishingRodItem::isHandEquipped() +{ + return true; +} + +bool FishingRodItem::isMirroredArt() +{ + return true; +} + +shared_ptr FishingRodItem::use(shared_ptr instance, Level *level, shared_ptr player) +{ + if (player->fishing != NULL) + { + int dmg = player->fishing->retrieve(); + instance->hurt(dmg, player); + player->swing(); + } + else + { + level->playSound(player, eSoundType_RANDOM_BOW, 0.5f, 0.4f / (random->nextFloat() * 0.4f + 0.8f)); + if (!level->isClientSide) + { + // 4J Stu - Move the player->fishing out of the ctor as we cannot reference 'this' + shared_ptr hook = shared_ptr( new FishingHook(level, player) ); + player->fishing = hook; + level->addEntity( shared_ptr( hook ) ); + } + player->swing(); + } + return instance; +} + +void FishingRodItem::registerIcons(IconRegister *iconRegister) +{ + Item::registerIcons(iconRegister); + emptyIcon = iconRegister->registerIcon(TEXTURE_EMPTY); +} + +Icon *FishingRodItem::getEmptyIcon() +{ + return emptyIcon; +} diff --git a/Minecraft.World/FishingRodItem.h b/Minecraft.World/FishingRodItem.h new file mode 100644 index 00000000..a6df606d --- /dev/null +++ b/Minecraft.World/FishingRodItem.h @@ -0,0 +1,27 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class Player; +class Level; + +class FishingRodItem : public Item +{ +public: + static const wstring TEXTURE_EMPTY; + +private: + Icon *emptyIcon; + +public: + FishingRodItem(int id); + + virtual bool isHandEquipped(); + virtual bool isMirroredArt(); + virtual shared_ptr use(shared_ptr instance, Level *level, shared_ptr player); + + //@Override + void registerIcons(IconRegister *iconRegister); + Icon *getEmptyIcon(); +}; diff --git a/Minecraft.World/FixedBiomeSource.cpp b/Minecraft.World/FixedBiomeSource.cpp new file mode 100644 index 00000000..35708cb2 --- /dev/null +++ b/Minecraft.World/FixedBiomeSource.cpp @@ -0,0 +1,153 @@ +#include "stdafx.h" +#include "Arrays.h" +#include "FixedBiomeSource.h" + +FixedBiomeSource::FixedBiomeSource(Biome *fixed, float temperature, float downfall) +{ + this->biome = fixed; + this->temperature = temperature; + this->downfall = downfall; +} + +Biome *FixedBiomeSource::getBiome(ChunkPos *cp) +{ + return biome; +} + +Biome *FixedBiomeSource::getBiome(int x, int z) +{ + return biome; +} + +float FixedBiomeSource::getTemperature(int x, int z) +{ + return temperature; +} + +void FixedBiomeSource::getTemperatureBlock(floatArray& temperatures, int x, int z, int w, int h) const +{ + if (temperatures.data == NULL || temperatures.length < w * h) + { + if(temperatures.data != NULL) delete [] temperatures.data; + temperatures = floatArray(w * h); + } + + Arrays::fill(temperatures, 0, w * h, temperature); +} + +floatArray FixedBiomeSource::getTemperatureBlock(int x, int z, int w, int h) const +{ + floatArray temps(w*h); + getTemperatureBlock(temps, x, z, w, h); + return temps; +} + +// 4J - note that caller is responsible for deleting returned array. temperatures array is for output only. +void FixedBiomeSource::getTemperatureBlock(doubleArray& temperatures, int x, int z, int w, int h) const +{ + temperatures = doubleArray(w * h); + + Arrays::fill(temperatures, 0, w * h, (double)temperature); +} + +void FixedBiomeSource::getDownfallBlock(floatArray &downfalls, int x, int z, int w, int h) const +{ + if (downfalls.data == NULL || downfalls.length < w * h) + { + if(downfalls.data != NULL) delete [] downfalls.data; + downfalls = floatArray(w * h); + } + Arrays::fill(downfalls, 0, w * h, downfall); +} + +floatArray FixedBiomeSource::getDownfallBlock(int x, int z, int w, int h) const +{ + floatArray downfalls(w*h); + getDownfallBlock(downfalls, x, z, w, h); + return downfalls; +} + +float FixedBiomeSource::getDownfall(int x, int z) const +{ + return downfall; +} + +void FixedBiomeSource::getDownfallBlock(doubleArray downfalls, int x, int z, int w, int h) +{ + if (downfalls.data == NULL || downfalls.length < w * h) + { + if(downfalls.data != NULL) delete [] downfalls.data; + downfalls = doubleArray(w * h); + } + Arrays::fill(downfalls, 0, w * h, (double) downfall); +} + +// 4J - caller is responsible for deleting biomes array, plus any optional arrays output if pointers are passed in (_temperatures, _downfalls) +void FixedBiomeSource::getBiomeBlock(BiomeArray& biomes, int x, int z, int w, int h, bool useCache) const +{ + MemSect(36); + biomes = BiomeArray(w * h); + MemSect(0); + + Arrays::fill(biomes, 0, w * h, biome); +} + +// 4J - caller is responsible for deleting biomes array, plus any optional arrays output if pointers are passed in (_temperatures, _downfalls) +void FixedBiomeSource::getBiomeIndexBlock(byteArray& biomeIndices, int x, int z, int w, int h, bool useCache) const +{ + MemSect(36); + biomeIndices = byteArray(w * h); + MemSect(0); + int biomeIndex = biome->id; + Arrays::fill(biomeIndices, 0, w * h, biomeIndex); +} + +// 4J-PB added in from beyond 1.8.2 +// 4J - caller is responsible for deleting biomes array, plus any optional arrays output if pointers are passed in (_temperatures, _downfalls) +void FixedBiomeSource::getRawBiomeBlock(BiomeArray& biomes,int x, int z, int w, int h) const +{ + MemSect(36); + biomes = BiomeArray(w * h); + MemSect(0); + + Arrays::fill(biomes, 0, w * h, biome); +} + +// 4J-PB added in from beyond 1.8.2 +// 4J - caller is responsible for deleting biomes array, plus any optional arrays output if pointers are passed in (_temperatures, _downfalls) +BiomeArray FixedBiomeSource::getRawBiomeBlock( int x, int z, int w, int h) const +{ + BiomeArray biomes; + getRawBiomeBlock(biomes, x, z, w, h); + return biomes; +} + +TilePos *FixedBiomeSource::findBiome(int x, int z, int r, Biome *toFind, Random *random) +{ + if (toFind == biome) + { + return new TilePos(x - r + random->nextInt(r * 2 + 1), 0, z - r + random->nextInt(r * 2 + 1)); + } + + return NULL; +} + +TilePos *FixedBiomeSource::findBiome(int x, int z, int r, vector allowed, Random *random) +{ + if (find(allowed.begin(), allowed.end(), biome) != allowed.end()) + { + return new TilePos(x - r + random->nextInt(r * 2 + 1), 0, z - r + random->nextInt(r * 2 + 1)); + } + + return NULL; +} + +bool FixedBiomeSource::containsOnly(int x, int z, int r, Biome *allowed) +{ + return allowed == biome; +} + +bool FixedBiomeSource::containsOnly(int x, int z, int r, vector allowed) +{ + return find(allowed.begin(), allowed.end(), biome) != allowed.end(); +} \ No newline at end of file diff --git a/Minecraft.World/FixedBiomeSource.h b/Minecraft.World/FixedBiomeSource.h new file mode 100644 index 00000000..ecddbad6 --- /dev/null +++ b/Minecraft.World/FixedBiomeSource.h @@ -0,0 +1,37 @@ +#pragma once +#include "BiomeSource.h" + +class FixedBiomeSource : public BiomeSource +{ +private: + Biome *biome; + float temperature, downfall; + +public: + using BiomeSource::getTemperature; + + FixedBiomeSource(Biome *fixed, float temperature, float downfall); + + virtual Biome *getBiome(ChunkPos *cp); + virtual Biome *getBiome(int x, int z); + virtual float getTemperature(int x, int z); + virtual void getTemperatureBlock(floatArray& temperatures, int x, int z, int w, int h) const; + virtual floatArray getTemperatureBlock(int x, int z, int w, int h) const; + virtual void getTemperatureBlock(doubleArray& temperatures, int x, int z, int w, int h) const; + virtual void getDownfallBlock(floatArray &downfalls, int x, int z, int w, int h) const; + virtual floatArray getDownfallBlock(int x, int z, int w, int h) const; + virtual float getDownfall(int x, int z) const; + virtual void getDownfallBlock(doubleArray downfalls, int x, int z, int w, int h); + virtual void getBiomeBlock(BiomeArray& biomes, int x, int z, int w, int h, bool useCache) const; + virtual void getBiomeIndexBlock(byteArray& biomeIndices, int x, int z, int w, int h, bool useCache) const; + + // 4J-PB added in from beyond 1.8.2 + virtual BiomeArray getRawBiomeBlock(int x, int z, int w, int h) const; + virtual void getRawBiomeBlock(BiomeArray &biomes, int x, int z, int w, int h) const; + + //////////////////////////////////// + virtual TilePos *findBiome(int x, int z, int r, Biome *toFind, Random *random); + virtual TilePos *findBiome(int x, int z, int r, vector allowed, Random *random); + virtual bool containsOnly(int x, int z, int r, Biome *allowed); + virtual bool containsOnly(int x, int z, int r, vector allowed); +}; diff --git a/Minecraft.World/FlatLayer.cpp b/Minecraft.World/FlatLayer.cpp new file mode 100644 index 00000000..3d19dc4d --- /dev/null +++ b/Minecraft.World/FlatLayer.cpp @@ -0,0 +1,20 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" + +FlatLayer::FlatLayer(int val) : Layer(0) +{ + this->val = val; +} + +intArray FlatLayer::getArea(int xo, int yo, int w, int h) +{ + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + result[x + y * w] = val; + } + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/FlatLayer.h b/Minecraft.World/FlatLayer.h new file mode 100644 index 00000000..a4592fb0 --- /dev/null +++ b/Minecraft.World/FlatLayer.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Layer.h" + +class FlatLayer : public Layer +{ +private: + int val; + +public: + FlatLayer(int val); + intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/FlatLevelSource.cpp b/Minecraft.World/FlatLevelSource.cpp new file mode 100644 index 00000000..6435bfb4 --- /dev/null +++ b/Minecraft.World/FlatLevelSource.cpp @@ -0,0 +1,153 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.levelgen.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.level.levelgen.structure.h" +#include "net.minecraft.world.level.levelgen.synth.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.storage.h" +#include "FlatLevelSource.h" + + +//FlatLevelSource::villageFeature = new VillageFeature(1); + +FlatLevelSource::FlatLevelSource(Level *level, __int64 seed, bool generateStructures) +{ + m_XZSize = level->getLevelData()->getXZSize(); + + this->level = level; + this->generateStructures = generateStructures; + this->random = new Random(seed); + this->pprandom = new Random(seed); // 4J - added, so that we can have a separate random for doing post-processing in parallel with creation + + villageFeature = new VillageFeature(0,m_XZSize); + + +} + +FlatLevelSource::~FlatLevelSource() +{ + delete random; + delete pprandom; + delete villageFeature; +} + +void FlatLevelSource::prepareHeights(byteArray blocks) +{ + int height = blocks.length / (16 * 16); + + for (int xc = 0; xc < 16; xc++) + { + for (int zc = 0; zc < 16; zc++) + { + for (int yc = 0; yc < height; yc++) + { + int block = 0; + if (yc == 0) + { + block = Tile::unbreakable_Id; + } + else if (yc <= 2) + { + block = Tile::dirt_Id; + } + else if (yc == 3) + { + block = Tile::grass_Id; + } + blocks[xc << 11 | zc << 7 | yc] = (byte) block; + } + } + } +} + +LevelChunk *FlatLevelSource::create(int x, int z) +{ + return getChunk(x, z); +} + +LevelChunk *FlatLevelSource::getChunk(int xOffs, int zOffs) +{ + // 4J - now allocating this with a physical alloc & bypassing general memory management so that it will get cleanly freed + int chunksSize = Level::genDepth * 16 * 16; + byte *tileData = (byte *)XPhysicalAlloc(chunksSize, MAXULONG_PTR, 4096, PAGE_READWRITE); + XMemSet128(tileData,0,chunksSize); + byteArray blocks = byteArray(tileData,chunksSize); +// byteArray blocks = byteArray(16 * level->depth * 16); + prepareHeights(blocks); + +// LevelChunk *levelChunk = new LevelChunk(level, blocks, xOffs, zOffs); // 4J - moved below + // double[] temperatures = level.getBiomeSource().temperatures; + + + if (generateStructures) + { + villageFeature->apply(this, level, xOffs, zOffs, blocks); + } + + // 4J - this now creates compressed block data from the blocks array passed in, so moved it until after the blocks are actually finalised. We also + // now need to free the passed in blocks as the LevelChunk doesn't use the passed in allocation anymore. + LevelChunk *levelChunk = new LevelChunk(level, blocks, xOffs, zOffs); + XPhysicalFree(tileData); + + levelChunk->recalcHeightmap(); + + return levelChunk; +} + + +bool FlatLevelSource::hasChunk(int x, int y) +{ + return true; +} + +void FlatLevelSource::postProcess(ChunkSource *parent, int xt, int zt) +{ + // 4J - changed from random to pprandom so we can run in parallel with getChunk etc. + pprandom->setSeed(level->getSeed()); + __int64 xScale = pprandom->nextLong() / 2 * 2 + 1; + __int64 zScale = pprandom->nextLong() / 2 * 2 + 1; + pprandom->setSeed(((xt * xScale) + (zt * zScale)) ^ level->getSeed()); + + if (generateStructures) + { + villageFeature->postProcess(level, pprandom, xt, zt); + } + + app.processSchematics(parent->getChunk(xt,zt)); +} + +bool FlatLevelSource::save(bool force, ProgressListener *progressListener) +{ + return true; +} + +bool FlatLevelSource::tick() +{ + return false; +} + +bool FlatLevelSource::shouldSave() +{ + return true; +} + +wstring FlatLevelSource::gatherStats() +{ + return L"FlatLevelSource"; +} + +vector *FlatLevelSource::getMobsAt(MobCategory *mobCategory, int x, int y, int z) +{ + Biome *biome = level->getBiome(x, z); + if (biome == NULL) + { + return NULL; + } + return biome->getMobs(mobCategory); +} + +TilePos *FlatLevelSource::findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z) +{ + return NULL; +} diff --git a/Minecraft.World/FlatLevelSource.h b/Minecraft.World/FlatLevelSource.h new file mode 100644 index 00000000..804a7b5a --- /dev/null +++ b/Minecraft.World/FlatLevelSource.h @@ -0,0 +1,44 @@ +#pragma once +using namespace std; + +#include "ChunkSource.h" + +class ProgressListener; +class LargeFeature; +class StrongholdFeature; +class VillageFeature; +class MineShaftFeature; +class PerlinNoise; + +class FlatLevelSource : public ChunkSource +{ +public: + static const int CHUNK_HEIGHT = 8; + static const int CHUNK_WIDTH = 4; + +private: + Level *level; + Random *random; + Random *pprandom; + + boolean generateStructures; + VillageFeature *villageFeature;// = new VillageFeature(1); + +public: + FlatLevelSource(Level *level, __int64 seed, bool generateStructures); + ~FlatLevelSource(); + +private: void prepareHeights(byteArray blocks); + +public: + virtual LevelChunk *create(int x, int z); + virtual LevelChunk *getChunk(int xOffs, int zOffs); + virtual bool hasChunk(int x, int y); + virtual void postProcess(ChunkSource *parent, int xt, int zt); + virtual bool save(bool force, ProgressListener *progressListener); + virtual bool tick(); + virtual bool shouldSave(); + virtual wstring gatherStats(); + virtual vector *getMobsAt(MobCategory *mobCategory, int x, int y, int z); + virtual TilePos *findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z); +}; diff --git a/Minecraft.World/FleeSunGoal.cpp b/Minecraft.World/FleeSunGoal.cpp new file mode 100644 index 00000000..a9799038 --- /dev/null +++ b/Minecraft.World/FleeSunGoal.cpp @@ -0,0 +1,52 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "FleeSunGoal.h" + +FleeSunGoal::FleeSunGoal(PathfinderMob *mob, float speed) +{ + this->mob = mob; + this->speed = speed; + this->level = mob->level; + setRequiredControlFlags(Control::MoveControlFlag); +} + +bool FleeSunGoal::canUse() +{ + if (!level->isDay()) return false; + if (!mob->isOnFire()) return false; + if (!level->canSeeSky(Mth::floor(mob->x), (int) mob->bb->y0, Mth::floor(mob->z))) return false; + + Vec3 *pos = getHidePos(); + if (pos == NULL) return false; + wantedX = pos->x; + wantedY = pos->y; + wantedZ = pos->z; + return true; +} + +bool FleeSunGoal::canContinueToUse() +{ + return !mob->getNavigation()->isDone(); +} + +void FleeSunGoal::start() +{ + mob->getNavigation()->moveTo(wantedX, wantedY, wantedZ, speed); +} + +Vec3 *FleeSunGoal::getHidePos() +{ + Random *random = mob->getRandom(); + for (int i = 0; i < 10; i++) + { + int xt = Mth::floor(mob->x + random->nextInt(20) - 10); + int yt = Mth::floor(mob->bb->y0 + random->nextInt(6) - 3); + int zt = Mth::floor(mob->z + random->nextInt(20) - 10); + if (!level->canSeeSky(xt, yt, zt) && mob->getWalkTargetValue(xt, yt, zt) < 0) return Vec3::newTemp(xt, yt, zt); + } + return NULL; +} \ No newline at end of file diff --git a/Minecraft.World/FleeSunGoal.h b/Minecraft.World/FleeSunGoal.h new file mode 100644 index 00000000..9d26c9f6 --- /dev/null +++ b/Minecraft.World/FleeSunGoal.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Goal.h" + +class FleeSunGoal : public Goal +{ +private: + PathfinderMob *mob; // Owner of this goal + double wantedX, wantedY, wantedZ; + float speed; + Level *level; + +public: + FleeSunGoal(PathfinderMob *mob, float speed); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + +private: + Vec3 *getHidePos(); + +public: + // 4J Added override to update ai elements when loading entity from schematics + virtual void setLevel(Level *level) { this->level = level; } +}; \ No newline at end of file diff --git a/Minecraft.World/FlintAndSteelItem.cpp b/Minecraft.World/FlintAndSteelItem.cpp new file mode 100644 index 00000000..5ebdad2b --- /dev/null +++ b/Minecraft.World/FlintAndSteelItem.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.stats.h" +#include "Material.h" +#include "ItemInstance.h" +#include "FlintAndSteelItem.h" +#include "SoundTypes.h" + +FlintAndSteelItem::FlintAndSteelItem(int id) : Item( id ) +{ + maxStackSize = 1; + setMaxDamage(64); +} + +bool FlintAndSteelItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + // 4J-PB - Adding a test only version to allow tooltips to be displayed + if (face == 0) y--; + if (face == 1) y++; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + + if (!player->mayBuild(x, y, z)) return false; + + int targetType = level->getTile(x, y, z); + + if(!bTestUseOnOnly) + { + if (targetType == 0) + { + if( level->getTile(x, y-1, z) == Tile::obsidian_Id ) + { + if( Tile::portalTile->trySpawnPortal(level, x, y, z, false) ) + { + player->awardStat( + GenericStats::portalsCreated(), + GenericStats::param_noArgs() + ); + + // 4J : WESTY : Added for achievement. + player->awardStat(GenericStats::InToTheNether(),GenericStats::param_InToTheNether()); + } + } + + level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_FIRE_IGNITE, 1, random->nextFloat() * 0.4f + 0.8f); + level->setTile(x, y, z, Tile::fire_Id); + } + + instance->hurt(1, player); + } + else + { + if(targetType == 0) + { + return true; + } + else + { + return false; + } + } + + // 4J-PB - this function shouldn't really return true all the time, but I've added a special case for my test use for the tooltips display + // and will leave it as is for the game use + + return true; +} diff --git a/Minecraft.World/FlintAndSteelItem.h b/Minecraft.World/FlintAndSteelItem.h new file mode 100644 index 00000000..4456330a --- /dev/null +++ b/Minecraft.World/FlintAndSteelItem.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Item.h" + +class Player; +class Level; + +class FlintAndSteelItem : public Item +{ +public: + FlintAndSteelItem(int id); + + virtual bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); +}; diff --git a/Minecraft.World/FlippedIcon.cpp b/Minecraft.World/FlippedIcon.cpp new file mode 100644 index 00000000..559423d7 --- /dev/null +++ b/Minecraft.World/FlippedIcon.cpp @@ -0,0 +1,88 @@ +#include "stdafx.h" +#include "FlippedIcon.h" +#include "SharedConstants.h" + +FlippedIcon::FlippedIcon(Icon *base, bool horizontal, bool vertical) : base(base), horizontal(horizontal), vertical(vertical) +{ +} + +int FlippedIcon::getX() const +{ + return base->getX(); +} + +int FlippedIcon::getY() const +{ + return base->getY(); +} + +int FlippedIcon::getWidth() const +{ + return base->getWidth(); +} + +int FlippedIcon::getHeight() const +{ + return base->getHeight(); +} + +float FlippedIcon::getU0(bool adjust/*=false*/) const +{ + if (horizontal) return base->getU1(adjust); + return base->getU0(adjust); +} + +float FlippedIcon::getU1(bool adjust/*=false*/) const +{ + if (horizontal) return base->getU0(adjust); + return base->getU1(adjust); +} + +float FlippedIcon::getU(double offset, bool adjust/*=false*/) const +{ + float diff = getU1(adjust) - getU0(adjust); + return getU0(adjust) + (diff * ((float) offset / SharedConstants::WORLD_RESOLUTION)); +} + +float FlippedIcon::getV0(bool adjust/*=false*/) const +{ + if (vertical) return base->getV0(adjust); + return base->getV0(adjust); +} + +float FlippedIcon::getV1(bool adjust/*=false*/) const +{ + if (vertical) return base->getV0(adjust); + return base->getV1(adjust); +} + +float FlippedIcon::getV(double offset, bool adjust/*=false*/) const +{ + float diff = getV1(adjust) - getV0(adjust); + return getV0(adjust) + (diff * ((float) offset / SharedConstants::WORLD_RESOLUTION)); +} + +wstring FlippedIcon::getName() const +{ + return base->getName(); +} + +int FlippedIcon::getSourceWidth() const +{ + return base->getSourceWidth(); +} + +int FlippedIcon::getSourceHeight() const +{ + return base->getSourceHeight(); +} + +int FlippedIcon::getFlags() const +{ + return base->getFlags(); +} + +void FlippedIcon::setFlags(int flags) +{ + base->setFlags(flags); +} diff --git a/Minecraft.World/FlippedIcon.h b/Minecraft.World/FlippedIcon.h new file mode 100644 index 00000000..169ceafc --- /dev/null +++ b/Minecraft.World/FlippedIcon.h @@ -0,0 +1,31 @@ +#pragma once +using namespace std; + +#include "Icon.h" + +class FlippedIcon : public Icon +{ +private: + Icon *base; + const bool horizontal; + const bool vertical; + +public: + FlippedIcon(Icon *base, bool horizontal, bool vertical); + + int getX() const; + int getY() const; + int getWidth() const; + int getHeight() const; + float getU0(bool adjust = false) const; + float getU1(bool adjust = false) const; + float getU(double offset, bool adjust = false) const; + float getV0(bool adjust = false) const; + float getV1(bool adjust = false) const; + float getV(double offset, bool adjust = false) const; + wstring getName() const; + int getSourceWidth() const; + int getSourceHeight() const; + int getFlags() const; // 4J added + void setFlags(int flags); // 4J added +}; \ No newline at end of file diff --git a/Minecraft.World/FloatBuffer.cpp b/Minecraft.World/FloatBuffer.cpp new file mode 100644 index 00000000..6fe0fd02 --- /dev/null +++ b/Minecraft.World/FloatBuffer.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include "FloatBuffer.h" + +//Allocates a new float buffer. +//The new buffer's position will be zero, its limit will be its capacity, and its mark will be undefined. +//It will have a backing array, and its array offset will be zero. +// +//Parameters: +//capacity - The new buffer's capacity, in floats +FloatBuffer::FloatBuffer(unsigned int capacity) : Buffer( capacity ) +{ + buffer = new float[capacity]; + memset( buffer,0,sizeof(float)*capacity); +} + +FloatBuffer::FloatBuffer( unsigned int capacity, float *backingArray ) : Buffer( capacity ) +{ + hasBackingArray = true; + buffer = backingArray; +} + +FloatBuffer::~FloatBuffer() +{ + if( !hasBackingArray ) + delete[] buffer; +} + +//Flips this buffer. The limit is set to the current position and then the position is set to zero. +//If the mark is defined then it is discarded. +// +//Returns: +//This buffer +FloatBuffer *FloatBuffer::flip() +{ + m_limit = m_position; + m_position = 0; + return this; +} + +//Relative put method (optional operation). +//Writes the given float into this buffer at the current position, and then increments the position. +// +//Parameters: +//f - The float to be written +//Returns: +//This buffer +FloatBuffer *FloatBuffer::put(float f) +{ + buffer[m_position++] = f; + return this; +} + +//Relative bulk get method. +//This method transfers floats from this buffer into the given destination array. +//An invocation of this method of the form src.get(a) behaves in exactly the same way as the invocation +// +// src.get(a, 0, a.length) +//Returns: +//This buffer +void FloatBuffer::get(floatArray *dst) +{ + assert( dst->length <= m_capacity ); + + for (unsigned int i = 0; i < dst->length; i++) + dst->data[i] = buffer[i]; +} \ No newline at end of file diff --git a/Minecraft.World/FloatBuffer.h b/Minecraft.World/FloatBuffer.h new file mode 100644 index 00000000..bb9870ae --- /dev/null +++ b/Minecraft.World/FloatBuffer.h @@ -0,0 +1,18 @@ +#pragma once +#include "Buffer.h" + +class FloatBuffer : public Buffer +{ +private: + float *buffer; + +public: + FloatBuffer(unsigned int capacity); + FloatBuffer( unsigned int capacity, float *backingArray ); + virtual ~FloatBuffer(); + + FloatBuffer *flip(); + FloatBuffer *put(float f); + void get(floatArray *dst); + float *_getDataPointer() {return buffer;} +}; \ No newline at end of file diff --git a/Minecraft.World/FloatGoal.cpp b/Minecraft.World/FloatGoal.cpp new file mode 100644 index 00000000..a5fb9255 --- /dev/null +++ b/Minecraft.World/FloatGoal.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "FloatGoal.h" + +FloatGoal::FloatGoal(Mob *mob) +{ + this->mob = mob; + setRequiredControlFlags(Control::JumpControlFlag); + mob->getNavigation()->setCanFloat(true); +} + +bool FloatGoal::canUse() +{ + return (mob->isInWater() || mob->isInLava()); +} + +void FloatGoal::tick() +{ + if (mob->getRandom()->nextFloat() < 0.8f) mob->getJumpControl()->jump(); +} \ No newline at end of file diff --git a/Minecraft.World/FloatGoal.h b/Minecraft.World/FloatGoal.h new file mode 100644 index 00000000..0a24ced9 --- /dev/null +++ b/Minecraft.World/FloatGoal.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Goal.h" + +class Mob; + +class FloatGoal : public Goal +{ +private: + Mob *mob; + +public: + FloatGoal(Mob *mob); + + virtual bool canUse(); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/FloatTag.h b/Minecraft.World/FloatTag.h new file mode 100644 index 00000000..266db4d3 --- /dev/null +++ b/Minecraft.World/FloatTag.h @@ -0,0 +1,37 @@ +#pragma once +#include "InputOutputStream.h" +#include "Tag.h" + +class FloatTag : public Tag +{ +public: + float data; + FloatTag(const wstring &name) : Tag(name) {} + FloatTag(const wstring &name, float data) : Tag(name) {this->data = data; } + + void write(DataOutput *dos) { dos->writeFloat(data); } + void load(DataInput *dis) { data = dis->readFloat(); } + + byte getId() { return TAG_Float; } + wstring toString() + { + static wchar_t buf[32]; + swprintf(buf, 32, L"%f",data); + return wstring( buf ); + } + + Tag *copy() + { + return new FloatTag(getName(), data); + } + + bool equals(Tag *obj) + { + if (Tag::equals(obj)) + { + FloatTag *o = (FloatTag *) obj; + return data == o->data; + } + return false; + } +}; \ No newline at end of file diff --git a/Minecraft.World/FlowerFeature.cpp b/Minecraft.World/FlowerFeature.cpp new file mode 100644 index 00000000..3fd8bac4 --- /dev/null +++ b/Minecraft.World/FlowerFeature.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "FlowerFeature.h" +#include "net.minecraft.world.level.tile.h" + +FlowerFeature::FlowerFeature(int tile) +{ + this->tile = tile; +} + +bool FlowerFeature::place(Level *level, Random *random, int x, int y, int z) +{ + // 4J Stu Added to stop tree features generating areas previously place by game rule generation + if(app.getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + bool intersects = levelGenOptions->checkIntersects(x - 8, y - 4, z - 8, x + 8, y + 4, z + 8); + if(intersects) + { + //app.DebugPrintf("Skipping reeds feature generation as it overlaps a game rule structure\n"); + return false; + } + } + + for (int i = 0; i < 64; i++) + { + int x2 = x + random->nextInt(8) - random->nextInt(8); + int y2 = y + random->nextInt(4) - random->nextInt(4); + int z2 = z + random->nextInt(8) - random->nextInt(8); + if (level->isEmptyTile(x2, y2, z2)) + { + if (Tile::tiles[tile]->canSurvive(level, x2, y2, z2)) + { + level->setTileNoUpdate(x2, y2, z2, tile); + } + } + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/FlowerFeature.h b/Minecraft.World/FlowerFeature.h new file mode 100644 index 00000000..47a0c3bc --- /dev/null +++ b/Minecraft.World/FlowerFeature.h @@ -0,0 +1,14 @@ +#pragma once +#include "Feature.h" + +class Level; + +class FlowerFeature : public Feature +{ +private: + int tile; + +public: + FlowerFeature (int tile); + bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/FlowerPotTile.cpp b/Minecraft.World/FlowerPotTile.cpp new file mode 100644 index 00000000..b245e56d --- /dev/null +++ b/Minecraft.World/FlowerPotTile.cpp @@ -0,0 +1,192 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "FlowerPotTile.h" + +FlowerPotTile::FlowerPotTile(int id) : Tile(id, Material::decoration, isSolidRender() ) +{ + updateDefaultShape(); + sendTileData(); +} + +void FlowerPotTile::updateDefaultShape() +{ + float size = 6.0f / 16.0f; + float half = size / 2; + setShape(0.5f - half, 0, 0.5f - half, 0.5f + half, size, 0.5f + half); +} + +bool FlowerPotTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +int FlowerPotTile::getRenderShape() +{ + return SHAPE_FLOWER_POT; +} + +bool FlowerPotTile::isCubeShaped() +{ + return false; +} + +bool FlowerPotTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly) +{ + shared_ptr item = player->inventory->getSelected(); + if (item == NULL) return false; + if (level->getData(x, y, z) != 0) return false; + int type = getTypeFromItem(item); + + if (type > 0) + { + level->setData(x, y, z, type); + + if (!player->abilities.instabuild) + { + if (--item->count <= 0) + { + player->inventory->setItem(player->inventory->selected, nullptr); + } + } + + return true; + } + + return false; +} + +int FlowerPotTile::cloneTileId(Level *level, int x, int y, int z) +{ + shared_ptr item = getItemFromType(level->getData(x, y, z)); + + if (item == NULL) + { + return Item::flowerPot_Id; + } + else + { + return item->id; + } +} + +int FlowerPotTile::cloneTileData(Level *level, int x, int y, int z) +{ + shared_ptr item = getItemFromType(level->getData(x, y, z)); + + if (item == NULL) + { + return Item::flowerPot_Id; + } + else + { + return item->getAuxValue(); + } +} + +bool FlowerPotTile::useOwnCloneData() +{ + return true; +} + +bool FlowerPotTile::mayPlace(Level *level, int x, int y, int z) +{ + return Tile::mayPlace(level, x, y, z) && level->isTopSolidBlocking(x, y - 1, z); +} + +void FlowerPotTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (!level->isTopSolidBlocking(x, y - 1, z)) + { + spawnResources(level, x, y, z, level->getData(x, y, z), 0); + + level->setTile(x, y, z, 0); + } +} + +void FlowerPotTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel) +{ + Tile::spawnResources(level, x, y, z, data, odds, playerBonusLevel); + + if (data > 0) + { + shared_ptr item = getItemFromType(data); + if (item != NULL) popResource(level, x, y, z, item); + } +} + +int FlowerPotTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::flowerPot_Id; +} + +shared_ptr FlowerPotTile::getItemFromType(int type) +{ + switch (type) + { + case TYPE_FLOWER_RED: + return shared_ptr( new ItemInstance(Tile::rose) ); + case TYPE_FLOWER_YELLOW: + return shared_ptr( new ItemInstance(Tile::flower) ); + case TYPE_CACTUS: + return shared_ptr( new ItemInstance(Tile::cactus) ); + case TYPE_MUSHROOM_BROWN: + return shared_ptr( new ItemInstance(Tile::mushroom1) ); + case TYPE_MUSHROOM_RED: + return shared_ptr( new ItemInstance(Tile::mushroom2) ); + case TYPE_DEAD_BUSH: + return shared_ptr( new ItemInstance(Tile::deadBush) ); + case TYPE_SAPLING_DEFAULT: + return shared_ptr( new ItemInstance(Tile::sapling, 1, Sapling::TYPE_DEFAULT) ); + case TYPE_SAPLING_BIRCH: + return shared_ptr( new ItemInstance(Tile::sapling, 1, Sapling::TYPE_BIRCH) ); + case TYPE_SAPLING_EVERGREEN: + return shared_ptr( new ItemInstance(Tile::sapling, 1, Sapling::TYPE_EVERGREEN) ); + case TYPE_SAPLING_JUNGLE: + return shared_ptr( new ItemInstance(Tile::sapling, 1, Sapling::TYPE_JUNGLE) ); + case TYPE_FERN: + return shared_ptr( new ItemInstance(Tile::tallgrass, 1, TallGrass::FERN) ); + } + + return nullptr; +} + +int FlowerPotTile::getTypeFromItem(shared_ptr item) +{ + int id = item->getItem()->id; + + if (id == Tile::rose_Id) return TYPE_FLOWER_RED; + if (id == Tile::flower_Id) return TYPE_FLOWER_YELLOW; + if (id == Tile::cactus_Id) return TYPE_CACTUS; + if (id == Tile::mushroom1_Id) return TYPE_MUSHROOM_BROWN; + if (id == Tile::mushroom2_Id) return TYPE_MUSHROOM_RED; + if (id == Tile::deadBush_Id) return TYPE_DEAD_BUSH; + + if (id == Tile::sapling_Id) + { + switch (item->getAuxValue()) + { + case Sapling::TYPE_DEFAULT: + return TYPE_SAPLING_DEFAULT; + case Sapling::TYPE_BIRCH: + return TYPE_SAPLING_BIRCH; + case Sapling::TYPE_EVERGREEN: + return TYPE_SAPLING_EVERGREEN; + case Sapling::TYPE_JUNGLE: + return TYPE_SAPLING_JUNGLE; + } + } + + if (id == Tile::tallgrass_Id) + { + switch (item->getAuxValue()) + { + case TallGrass::FERN: + return TYPE_FERN; + } + } + + return 0; +} \ No newline at end of file diff --git a/Minecraft.World/FlowerPotTile.h b/Minecraft.World/FlowerPotTile.h new file mode 100644 index 00000000..50dc18c4 --- /dev/null +++ b/Minecraft.World/FlowerPotTile.h @@ -0,0 +1,37 @@ +#pragma once + +#include "Tile.h" + +class FlowerPotTile : public Tile +{ +public: + static const int TYPE_FLOWER_RED = 1; + static const int TYPE_FLOWER_YELLOW = 2; + static const int TYPE_SAPLING_DEFAULT = 3; + static const int TYPE_SAPLING_EVERGREEN = 4; + static const int TYPE_SAPLING_BIRCH = 5; + static const int TYPE_SAPLING_JUNGLE = 6; + static const int TYPE_MUSHROOM_RED = 7; + static const int TYPE_MUSHROOM_BROWN = 8; + static const int TYPE_CACTUS = 9; + static const int TYPE_DEAD_BUSH = 10; + static const int TYPE_FERN = 11; + + FlowerPotTile(int id); + + void updateDefaultShape(); + bool isSolidRender(bool isServerLevel = false); + int getRenderShape(); + bool isCubeShaped(); + bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); + int cloneTileId(Level *level, int x, int y, int z); + int cloneTileData(Level *level, int x, int y, int z); + bool useOwnCloneData(); + bool mayPlace(Level *level, int x, int y, int z); + void neighborChanged(Level *level, int x, int y, int z, int type); + using Tile::spawnResources; + void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel); + int getResource(int data, Random *random, int playerBonusLevel); + static shared_ptr getItemFromType(int type); + static int getTypeFromItem(shared_ptr item); +}; \ No newline at end of file diff --git a/Minecraft.World/FlyingMob.cpp b/Minecraft.World/FlyingMob.cpp new file mode 100644 index 00000000..b7a01678 --- /dev/null +++ b/Minecraft.World/FlyingMob.cpp @@ -0,0 +1,81 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "FlyingMob.h" + +FlyingMob::FlyingMob(Level *level) : Mob( level ) +{ +} + +void FlyingMob::causeFallDamage(float distance) +{ + // this method is empty because flying creatures should + // not trigger the "fallOn" tile calls (such as trampling crops) +} + +void FlyingMob::travel(float xa, float ya) +{ + if (isInWater()) + { + moveRelative(xa, ya, 0.02f); + move(xd, yd, zd); + + xd *= 0.80f; + yd *= 0.80f; + zd *= 0.80f; + } + else if (isInLava()) + { + moveRelative(xa, ya, 0.02f); + move(xd, yd, zd); + xd *= 0.50f; + yd *= 0.50f; + zd *= 0.50f; + } + else + { + float friction = 0.91f; + if (onGround) + { + friction = 0.6f * 0.91f; + int t = level->getTile( Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z)); + if (t > 0) + { + friction = Tile::tiles[t]->friction * 0.91f; + } + } + + float friction2 = (0.6f * 0.6f * 0.91f * 0.91f * 0.6f * 0.91f) / (friction * friction * friction); + moveRelative(xa, ya, (onGround ? 0.1f * friction2 : 0.02f)); + + friction = 0.91f; + if (onGround) + { + friction = 0.6f * 0.91f; + int t = level->getTile( Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z)); + if (t > 0) + { + friction = Tile::tiles[t]->friction * 0.91f; + } + } + + move(xd, yd, zd); + + xd *= friction; + yd *= friction; + zd *= friction; + } + walkAnimSpeedO = walkAnimSpeed; + double xxd = x - xo; + double zzd = z - zo; + float wst = (float) sqrt(xxd * xxd + zzd * zzd) * 4; + if (wst > 1) wst = 1; + walkAnimSpeed += (wst - walkAnimSpeed) * 0.4f; + walkAnimPos += walkAnimSpeed; +} + +bool FlyingMob::onLadder() +{ + return false; +} \ No newline at end of file diff --git a/Minecraft.World/FlyingMob.h b/Minecraft.World/FlyingMob.h new file mode 100644 index 00000000..f3bb2a03 --- /dev/null +++ b/Minecraft.World/FlyingMob.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Mob.h" + +class Level; + +class FlyingMob : public Mob +{ +public: + FlyingMob(Level *level); + +protected: + virtual void causeFallDamage(float distance); + +public: + virtual void travel(float xa, float ya); + virtual bool onLadder(); +}; \ No newline at end of file diff --git a/Minecraft.World/FoliageColor.cpp b/Minecraft.World/FoliageColor.cpp new file mode 100644 index 00000000..276bf929 --- /dev/null +++ b/Minecraft.World/FoliageColor.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "FoliageColor.h" + +// 4J Stu - Don't use this any more +//intArray FoliageColor::pixels; +// +//void FoliageColor::init(intArray pixels) +//{ +// int *oldData = FoliageColor::pixels.data; +// FoliageColor::pixels = pixels; +// delete[] oldData; +//} +// +//int FoliageColor::get(double temp, double rain) +//{ +// rain *= temp; +// int x = (int) ((1 - temp) * 255); +// int y = (int) ((1 - rain) * 255); +// int returnVal = pixels[y << 8 | x]; +// return returnVal; +//} + +int FoliageColor::getEvergreenColor() +{ + return Minecraft::GetInstance()->getColourTable()->getColor(eMinecraftColour_Foliage_Evergreen); + //return 0x619961; +} + +int FoliageColor::getBirchColor() +{ + return Minecraft::GetInstance()->getColourTable()->getColor(eMinecraftColour_Foliage_Birch); + //return 0x80a755; +} + +int FoliageColor::getDefaultColor() +{ + return Minecraft::GetInstance()->getColourTable()->getColor(eMinecraftColour_Foliage_Default); + //return 0x48b518; +} \ No newline at end of file diff --git a/Minecraft.World/FoliageColor.h b/Minecraft.World/FoliageColor.h new file mode 100644 index 00000000..140a0673 --- /dev/null +++ b/Minecraft.World/FoliageColor.h @@ -0,0 +1,17 @@ +#pragma once + +class FoliageColor +{ + // 4J Stu - We don't want to use this any more +//private: +// static intArray pixels; +// +//public: +// static void init(intArray pixels); +// static int get(double temp, double rain); + +public: + static int getEvergreenColor(); + static int getBirchColor(); + static int getDefaultColor(); +}; \ No newline at end of file diff --git a/Minecraft.World/FollowOwnerGoal.cpp b/Minecraft.World/FollowOwnerGoal.cpp new file mode 100644 index 00000000..faba2226 --- /dev/null +++ b/Minecraft.World/FollowOwnerGoal.cpp @@ -0,0 +1,85 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "FollowOwnerGoal.h" + +FollowOwnerGoal::FollowOwnerGoal(TamableAnimal *tamable, float speed, float startDistance, float stopDistance) +{ + owner = weak_ptr(); + timeToRecalcPath = 0; + oldAvoidWater = false; + + this->tamable = tamable; + this->level = tamable->level; + this->speed = speed; + this->navigation = tamable->getNavigation(); + this->startDistance = startDistance; + this->stopDistance = stopDistance; + setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag); +} + +bool FollowOwnerGoal::canUse() +{ + shared_ptr owner = tamable->getOwner(); + if (owner == NULL) return false; + if (tamable->isSitting()) return false; + if (tamable->distanceToSqr(owner) < startDistance * startDistance) return false; + this->owner = weak_ptr(owner); + return true; +} + +bool FollowOwnerGoal::canContinueToUse() +{ + return owner.lock() != NULL && !navigation->isDone() && tamable->distanceToSqr(owner.lock()) > stopDistance * stopDistance && !tamable->isSitting(); +} + +void FollowOwnerGoal::start() +{ + timeToRecalcPath = 0; + oldAvoidWater = tamable->getNavigation()->getAvoidWater(); + tamable->getNavigation()->setAvoidWater(false); +} + +void FollowOwnerGoal::stop() +{ + owner = weak_ptr(); + navigation->stop(); + tamable->getNavigation()->setAvoidWater(oldAvoidWater); +} + +void FollowOwnerGoal::tick() +{ + tamable->getLookControl()->setLookAt(owner.lock(), 10, tamable->getMaxHeadXRot()); + if (tamable->isSitting()) return; + + if (--timeToRecalcPath > 0) return; + timeToRecalcPath = 10; + + if (navigation->moveTo(owner.lock(), speed)) return; + if (tamable->distanceToSqr(owner.lock()) < TeleportDistance * TeleportDistance) return; + + // find a good spawn position nearby the owner + int sx = Mth::floor(owner.lock()->x) - 2; + int sz = Mth::floor(owner.lock()->z) - 2; + int y = Mth::floor(owner.lock()->bb->y0); + for (int x = 0; x <= 4; x++) + { + for (int z = 0; z <= 4; z++) + { + if (x >= 1 && z >= 1 && x <= 3 && z <= 3) + { + continue; + } + if (level->isTopSolidBlocking(sx + x, y - 1, sz + z) && !level->isSolidBlockingTile(sx + x, y, sz + z) && !level->isSolidBlockingTile(sx + x, y + 1, sz + z)) + { + tamable->moveTo(sx + x + .5f, y, sz + z + .5f, tamable->yRot, tamable->xRot); + navigation->stop(); + return; + } + } + } +} \ No newline at end of file diff --git a/Minecraft.World/FollowOwnerGoal.h b/Minecraft.World/FollowOwnerGoal.h new file mode 100644 index 00000000..e7b598fa --- /dev/null +++ b/Minecraft.World/FollowOwnerGoal.h @@ -0,0 +1,34 @@ +#pragma once + +#include "Goal.h" + +class PathNavigation; +class TamableAnimal; + +class FollowOwnerGoal : public Goal +{ +public: + static const int TeleportDistance = 12; + +private: + TamableAnimal *tamable; // Owner of this goal + weak_ptr owner; + Level *level; + float speed; + PathNavigation *navigation; + int timeToRecalcPath; + float stopDistance, startDistance; + bool oldAvoidWater; + +public: + FollowOwnerGoal(TamableAnimal *tamable, float speed, float startDistance, float stopDistance); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + virtual void stop(); + virtual void tick(); + + // 4J Added override to update ai elements when loading entity from schematics + virtual void setLevel(Level *level) { this->level = level; } +}; \ No newline at end of file diff --git a/Minecraft.World/FollowParentGoal.cpp b/Minecraft.World/FollowParentGoal.cpp new file mode 100644 index 00000000..3e347d62 --- /dev/null +++ b/Minecraft.World/FollowParentGoal.cpp @@ -0,0 +1,65 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "BasicTypeContainers.h" +#include "FollowParentGoal.h" + +FollowParentGoal::FollowParentGoal(Animal *animal, float speed) +{ + timeToRecalcPath = 0; + + this->animal = animal; + this->speed = speed; +} + +bool FollowParentGoal::canUse() +{ + if (animal->getAge() >= 0) return false; + + vector > *parents = animal->level->getEntitiesOfClass(typeid(*animal), animal->bb->grow(8, 4, 8)); + + shared_ptr closest = nullptr; + double closestDistSqr = Double::MAX_VALUE; + for(AUTO_VAR(it, parents->begin()); it != parents->end(); ++it) + { + shared_ptr parent = dynamic_pointer_cast(*it); + if (parent->getAge() < 0) continue; + double distSqr = animal->distanceToSqr(parent); + if (distSqr > closestDistSqr) continue; + closestDistSqr = distSqr; + closest = parent; + } + delete parents; + + if (closest == NULL) return false; + if (closestDistSqr < 3 * 3) return false; + parent = weak_ptr(closest); + return true; +} + +bool FollowParentGoal::canContinueToUse() +{ + if (parent.lock() == NULL || !parent.lock()->isAlive()) return false; + double distSqr = animal->distanceToSqr(parent.lock()); + if (distSqr < 3 * 3 || distSqr > 16 * 16) return false; + return true; +} + +void FollowParentGoal::start() +{ + timeToRecalcPath = 0; +} + +void FollowParentGoal::stop() +{ + parent = weak_ptr(); +} + +void FollowParentGoal::tick() +{ + if (--timeToRecalcPath > 0) return; + timeToRecalcPath = 10; + animal->getNavigation()->moveTo(parent.lock(), speed); +} \ No newline at end of file diff --git a/Minecraft.World/FollowParentGoal.h b/Minecraft.World/FollowParentGoal.h new file mode 100644 index 00000000..0aa64bbf --- /dev/null +++ b/Minecraft.World/FollowParentGoal.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Goal.h" + +class Animal; + +class FollowParentGoal : public Goal +{ +private: + Animal *animal; // Owner of this goal + weak_ptr parent; + float speed; + int timeToRecalcPath; + +public: + FollowParentGoal(Animal *animal, float speed); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + virtual void stop(); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/FoodConstants.cpp b/Minecraft.World/FoodConstants.cpp new file mode 100644 index 00000000..a05b21c8 --- /dev/null +++ b/Minecraft.World/FoodConstants.cpp @@ -0,0 +1,35 @@ +#include "stdafx.h" + +#include "FoodConstants.h" + +const int FoodConstants::MAX_FOOD = 20; +const float FoodConstants::MAX_SATURATION = (float) FoodConstants::MAX_FOOD; +const float FoodConstants::START_SATURATION = (float) FoodConstants::MAX_SATURATION / 4.0f; +const float FoodConstants::SATURATION_FLOOR = FoodConstants::MAX_SATURATION / 8.0f; + +// this value modifies how quickly food is dropped +const float FoodConstants::EXHAUSTION_DROP = 4.0f; + +// number of game ticks to change health because of food +const int FoodConstants::HEALTH_TICK_COUNT = 80; + +const int FoodConstants::HEAL_LEVEL = 18; +const int FoodConstants::STARVE_LEVEL = 0; + +// some saturation guidelines +const float FoodConstants::FOOD_SATURATION_POOR = .1f; +const float FoodConstants::FOOD_SATURATION_LOW = .3f; +const float FoodConstants::FOOD_SATURATION_NORMAL = .6f; +const float FoodConstants::FOOD_SATURATION_GOOD = .8f; +const float FoodConstants::FOOD_SATURATION_MAX = 1.0f; +const float FoodConstants::FOOD_SATURATION_SUPERNATURAL = 1.2f; + +// some exhaustion guidelines +const float FoodConstants::EXHAUSTION_JUMP = .2f; +const float FoodConstants::EXHAUSTION_SPRINT_JUMP = FoodConstants::EXHAUSTION_JUMP * 4; +const float FoodConstants::EXHAUSTION_MINE = .025f; +const float FoodConstants::EXHAUSTION_ATTACK = .3f; +const float FoodConstants::EXHAUSTION_DAMAGE = .1f; +const float FoodConstants::EXHAUSTION_WALK = .010f; +const float FoodConstants::EXHAUSTION_SPRINT = FoodConstants::EXHAUSTION_WALK * 10; +const float FoodConstants::EXHAUSTION_SWIM = .015f; \ No newline at end of file diff --git a/Minecraft.World/FoodConstants.h b/Minecraft.World/FoodConstants.h new file mode 100644 index 00000000..6f80b041 --- /dev/null +++ b/Minecraft.World/FoodConstants.h @@ -0,0 +1,37 @@ +#pragma once + +class FoodConstants +{ +public: + static const int MAX_FOOD; + static const float MAX_SATURATION; + static const float START_SATURATION; + static const float SATURATION_FLOOR; + + // this value modifies how quickly food is dropped + static const float EXHAUSTION_DROP; + + // number of game ticks to change health because of food + static const int HEALTH_TICK_COUNT; + + static const int HEAL_LEVEL; + static const int STARVE_LEVEL; + + // some saturation guidelines + static const float FOOD_SATURATION_POOR; + static const float FOOD_SATURATION_LOW; + static const float FOOD_SATURATION_NORMAL; + static const float FOOD_SATURATION_GOOD; + static const float FOOD_SATURATION_MAX; + static const float FOOD_SATURATION_SUPERNATURAL; + + // some exhaustion guidelines + static const float EXHAUSTION_JUMP; + static const float EXHAUSTION_SPRINT_JUMP; + static const float EXHAUSTION_MINE; + static const float EXHAUSTION_ATTACK; + static const float EXHAUSTION_DAMAGE; + static const float EXHAUSTION_WALK; + static const float EXHAUSTION_SPRINT; + static const float EXHAUSTION_SWIM; +}; \ No newline at end of file diff --git a/Minecraft.World/FoodData.cpp b/Minecraft.World/FoodData.cpp new file mode 100644 index 00000000..79ef0fc1 --- /dev/null +++ b/Minecraft.World/FoodData.cpp @@ -0,0 +1,159 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.damagesource.h" +#include "com.mojang.nbt.h" +#include "FoodConstants.h" +#include "FoodData.h" + +FoodData::FoodData() +{ + exhaustionLevel = 0; + tickTimer = 0; + + this->foodLevel = FoodConstants::MAX_FOOD; + this->lastFoodLevel = FoodConstants::MAX_FOOD; + this->saturationLevel = FoodConstants::START_SATURATION; +} + +void FoodData::eat(int food, float saturationModifier) +{ + foodLevel = min(food + foodLevel, FoodConstants::MAX_FOOD); + saturationLevel = min(saturationLevel + (float) food * saturationModifier * 2.0f, (float)foodLevel); +} + +void FoodData::eat(FoodItem *item) +{ + eat(item->getNutrition(), item->getSaturationModifier()); +} + +void FoodData::tick(shared_ptr player) +{ + + int difficulty = player->level->difficulty; + + lastFoodLevel = foodLevel; + + if (exhaustionLevel > FoodConstants::EXHAUSTION_DROP) + { + exhaustionLevel -= FoodConstants::EXHAUSTION_DROP; + + if (saturationLevel > 0) + { + saturationLevel = max(saturationLevel - 1, 0.0f); + } + else if (difficulty > Difficulty::PEACEFUL) + { + foodLevel = max(foodLevel - 1, 0); + } + } + + // 4J Added - Allow host to disable using hunger. We don't deplete the hunger bar due to exhaustion + // but I think we should deplete it to heal + if(player->isAllowedToIgnoreExhaustion()) + { + if(foodLevel > 0 && player->isHurt()) + { + tickTimer++; + if (tickTimer >= FoodConstants::HEALTH_TICK_COUNT) + { + player->heal(1); + --foodLevel; + tickTimer = 0; + } + } + } + else if (foodLevel >= FoodConstants::HEAL_LEVEL && player->isHurt()) + { + tickTimer++; + if (tickTimer >= FoodConstants::HEALTH_TICK_COUNT) + { + player->heal(1); + tickTimer = 0; + } + } + else if (foodLevel <= FoodConstants::STARVE_LEVEL) + { + tickTimer++; + if (tickTimer >= FoodConstants::HEALTH_TICK_COUNT) + { + if (player->getHealth() > 10 || difficulty >= Difficulty::HARD || (player->getHealth() > 1 && difficulty >= Difficulty::NORMAL)) + { + player->hurt(DamageSource::starve, 1); + } + tickTimer = 0; + } + } + else + { + tickTimer = 0; + } + +} + +void FoodData::readAdditionalSaveData(CompoundTag *entityTag) +{ + + if (entityTag->contains(L"foodLevel")) + { + foodLevel = entityTag->getInt(L"foodLevel"); + tickTimer = entityTag->getInt(L"foodTickTimer"); + saturationLevel = entityTag->getFloat(L"foodSaturationLevel"); + exhaustionLevel = entityTag->getFloat(L"foodExhaustionLevel"); + } +} + +void FoodData::addAdditonalSaveData(CompoundTag *entityTag) +{ + entityTag->putInt(L"foodLevel", foodLevel); + entityTag->putInt(L"foodTickTimer", tickTimer); + entityTag->putFloat(L"foodSaturationLevel", saturationLevel); + entityTag->putFloat(L"foodExhaustionLevel", exhaustionLevel); +} + +int FoodData::getFoodLevel() +{ + return foodLevel; +} + +int FoodData::getLastFoodLevel() +{ + return lastFoodLevel; +} + +bool FoodData::needsFood() +{ + return foodLevel < FoodConstants::MAX_FOOD; +} + +void FoodData::addExhaustion(float amount) +{ + exhaustionLevel = min(exhaustionLevel + amount, FoodConstants::MAX_SATURATION * 2); +} + +float FoodData::getExhaustionLevel() +{ + return exhaustionLevel; +} + +float FoodData::getSaturationLevel() +{ + return saturationLevel; +} + +void FoodData::setFoodLevel(int food) +{ + this->foodLevel = food; +} + +void FoodData::setSaturation(float saturation) +{ + this->saturationLevel = saturation; +} + +void FoodData::setExhaustion(float exhaustion) +{ + this->exhaustionLevel = exhaustion; +} \ No newline at end of file diff --git a/Minecraft.World/FoodData.h b/Minecraft.World/FoodData.h new file mode 100644 index 00000000..3416a44a --- /dev/null +++ b/Minecraft.World/FoodData.h @@ -0,0 +1,34 @@ +#pragma once + +class FoodItem; +class Player; +class CompoundTag; + +class FoodData +{ +private: + int foodLevel; + float saturationLevel; + float exhaustionLevel; + + int tickTimer; + int lastFoodLevel; + +public: + FoodData(); + + void eat(int food, float saturationModifier); + void eat(FoodItem *item); + void tick(shared_ptr player); + void readAdditionalSaveData(CompoundTag *entityTag); + void addAdditonalSaveData(CompoundTag *entityTag); + int getFoodLevel(); + int getLastFoodLevel(); + bool needsFood(); + void addExhaustion(float amount); + float getExhaustionLevel(); + float getSaturationLevel(); + void setFoodLevel(int food); + void setSaturation(float saturation); + void setExhaustion(float exhaustion); +}; \ No newline at end of file diff --git a/Minecraft.World/FoodItem.cpp b/Minecraft.World/FoodItem.cpp new file mode 100644 index 00000000..f5304cb9 --- /dev/null +++ b/Minecraft.World/FoodItem.cpp @@ -0,0 +1,120 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.food.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.effect.h" +#include "net.minecraft.h" +#include "ItemInstance.h" +#include "FoodItem.h" +#include "SoundTypes.h" + +// 4J : WESTY : Other award ... eating cooked pork chop. +#include "net.minecraft.stats.h" + +void FoodItem::_init() +{ + // 4J Initialisers + canAlwaysEat = false; + effectId = 0; + effectDurationSeconds = 0; + effectAmplifier = 0; + effectProbability = 0.0f; +} + +FoodItem::FoodItem(int id, int nutrition, float saturationMod, bool isMeat) + : Item( id ), nutrition(nutrition), saturationModifier( saturationMod ), m_isMeat( isMeat ) +{ + _init(); +} + +FoodItem::FoodItem(int id, int nutrition, bool isMeat) + : Item( id ), nutrition(nutrition), saturationModifier( FoodConstants::FOOD_SATURATION_NORMAL ), m_isMeat( isMeat ) +{ + _init(); +} + +shared_ptr FoodItem::useTimeDepleted(shared_ptr instance, Level *level, shared_ptr player) +{ + instance->count--; + player->getFoodData()->eat(this); + // 4J - new sound brought forward from 1.2.3 + level->playSound(player, eSoundType_RANDOM_BURP, 0.5f, level->random->nextFloat() * 0.1f + 0.9f); + + addEatEffect(instance, level, player); + + return instance; +} + +void FoodItem::addEatEffect(shared_ptr instance, Level *level, shared_ptr player) +{ + if (!level->isClientSide && effectId > 0 && level->random->nextFloat() < effectProbability) + { + player->addEffect(new MobEffectInstance(effectId, effectDurationSeconds * SharedConstants::TICKS_PER_SECOND, effectAmplifier)); + } + +} + +int FoodItem::getUseDuration(shared_ptr itemInstance) +{ + return EAT_DURATION; +} + +UseAnim FoodItem::getUseAnimation(shared_ptr itemInstance) +{ + return UseAnim_eat; +} + +shared_ptr FoodItem::use(shared_ptr instance, Level *level, shared_ptr player) +{ + if (player->canEat(canAlwaysEat)) + { + player->startUsingItem(instance, getUseDuration(instance)); + } + + // 4J : WESTY : Other award ... eating cooked pork chop. + // 4J-JEV: This is just for an avatar award on the xbox. +#ifdef _XBOX + if ( instance->getItem() == Item::porkChop_cooked ) + { + player->awardStat(GenericStats::eatPorkChop(),GenericStats::param_eatPorkChop()); + } +#endif + + return instance; +} + +float FoodItem::getSaturationModifier() +{ + return saturationModifier; +} + +int FoodItem::getNutrition() +{ + return nutrition; +} + +bool FoodItem::isMeat() +{ + return m_isMeat; +} + +FoodItem *FoodItem::setEatEffect(int id, int durationInSecods, int amplifier, float effectProbability) +{ + effectId = id; + effectDurationSeconds = durationInSecods; + effectAmplifier = amplifier; + this->effectProbability = effectProbability; + return this; +} + +FoodItem *FoodItem::setCanAlwaysEat() +{ + canAlwaysEat = true; + return this; +} + +// 4J Added +bool FoodItem::canEat(shared_ptr player) +{ + return player->canEat(canAlwaysEat); +} diff --git a/Minecraft.World/FoodItem.h b/Minecraft.World/FoodItem.h new file mode 100644 index 00000000..16e6ce3f --- /dev/null +++ b/Minecraft.World/FoodItem.h @@ -0,0 +1,49 @@ +#pragma once + +#include "Item.h" + +class Player; +class Level; + +class FoodItem : public Item +{ +public: + static const int EAT_DURATION = (int) (20 * 1.6); + +private: + const int nutrition; + const float saturationModifier; + const bool m_isMeat; + bool canAlwaysEat; + + int effectId; + int effectDurationSeconds; + int effectAmplifier; + float effectProbability; + + void _init(); +public: + FoodItem(int id, int nutrition, float saturationMod, bool isMeat); + FoodItem(int id, int nutrition, bool isMeat); + + virtual shared_ptr useTimeDepleted(shared_ptr instance, Level *level, shared_ptr player); + +protected: + virtual void addEatEffect(shared_ptr instance, Level *level, shared_ptr player); + +public: + virtual int getUseDuration(shared_ptr itemInstance); + virtual UseAnim getUseAnimation(shared_ptr itemInstance); + + virtual shared_ptr use(shared_ptr instance, Level *level, shared_ptr player); + + int getNutrition(); + float getSaturationModifier(); + bool isMeat(); + + FoodItem *setEatEffect(int id, int durationInSecods, int amplifier, float effectProbability); + FoodItem *setCanAlwaysEat(); + + // 4J Added + bool canEat(shared_ptr player); +}; \ No newline at end of file diff --git a/Minecraft.World/FoodRecipies.cpp b/Minecraft.World/FoodRecipies.cpp new file mode 100644 index 00000000..1497cd41 --- /dev/null +++ b/Minecraft.World/FoodRecipies.cpp @@ -0,0 +1,122 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "DyePowderItem.h" +#include "Tile.h" +#include "Recipy.h" +#include "Recipes.h" +#include "FoodRecipies.h" + +void FoodRecipies::addRecipes(Recipes *r) +{ + // 4J-JEV: Bumped up in the list to avoid a colision with the title. + r->addShapedRecipy(new ItemInstance(Item::apple_gold, 1, 0), // + L"ssscicig", + L"###", // + L"#X#", // + L"###", // + L'#', Item::goldNugget, L'X', Item::apple, + L'F'); + + // 4J-PB - Moving the mushroom stew shaped->shapeless forward from 1.9, so it will not need the crafting table + r->addShapelessRecipy(new ItemInstance(Item::mushroomStew), + L"ttig", + Tile::mushroom1, Tile::mushroom2, Item::bowl, + L'F'); + + /*r->addShapedRecipy(new ItemInstance(Item::mushroomStew), // + L"sssctctcig", + L"Y", // + L"X", // + L"#", // + + L'X', Tile::mushroom1, + L'Y', Tile::mushroom2, + L'#', Item::bowl, + L'F');*/ + + // 4J-PB - removing for the xbox game - we already have it above +// r->addShapedRecipy(new ItemInstance(Item::mushroomStew), // +// L"sssctctcig", +// L"Y", // +// L"X", // +// L"#", // +// +// L'X', Tile::mushroom2, +// L'Y', Tile::mushroom1, +// L'#', Item::bowl, +// L'F'); +// + r->addShapedRecipy(new ItemInstance(Item::cookie, 8), // + L"sczcig", + L"#X#", // + + L'X', new ItemInstance(Item::dye_powder, 1, DyePowderItem::BROWN), + L'#', Item::wheat, + L'F'); + + r->addShapedRecipy(new ItemInstance(Tile::melon), // + L"ssscig", + L"MMM", // + L"MMM", // + L"MMM", // + + L'M', Item::melon, + L'F'); + + r->addShapedRecipy(new ItemInstance(Item::seeds_melon), // + L"scig", + L"M", // + + L'M', Item::melon, + L'F'); + + r->addShapedRecipy(new ItemInstance(Item::seeds_pumpkin, 4), // + L"sctg", + L"M", // + + L'M', Tile::pumpkin, + L'F'); + + r->addShapelessRecipy(new ItemInstance(Item::pumpkinPie), // + L"tiig", + Tile::pumpkin, Item::sugar, Item::egg, + L'F'); + + r->addShapedRecipy(new ItemInstance(Item::apple_gold, 1, 1), // + L"sssctcig", + L"###", // + L"#X#", // + L"###", // + L'#', Tile::goldBlock, L'X', Item::apple, + L'F'); + + r->addShapedRecipy(new ItemInstance(Item::carrotGolden, 1, 0), // + L"ssscicig", + L"###", // + L"#X#", // + L"###", // + + L'#', Item::goldNugget, L'X', Item::carrots, + L'F'); + + r->addShapelessRecipy(new ItemInstance(Item::fermentedSpiderEye), // + L"itig", + Item::spiderEye, Tile::mushroom1, Item::sugar, + L'F'); + + r->addShapelessRecipy(new ItemInstance(Item::speckledMelon), // + L"iig", + Item::melon, Item::goldNugget, + L'F'); + + r->addShapelessRecipy(new ItemInstance(Item::blazePowder, 2), // + L"ig", + Item::blazeRod, + L'F'); + + r->addShapelessRecipy(new ItemInstance(Item::magmaCream), // + L"iig", + Item::blazePowder, Item::slimeBall, + L'F'); +} + diff --git a/Minecraft.World/FoodRecipies.h b/Minecraft.World/FoodRecipies.h new file mode 100644 index 00000000..8f2c1884 --- /dev/null +++ b/Minecraft.World/FoodRecipies.h @@ -0,0 +1,7 @@ +#pragma once + +class FoodRecipies +{ +public: + void addRecipes(Recipes *r); +}; diff --git a/Minecraft.World/ForestBiome.cpp b/Minecraft.World/ForestBiome.cpp new file mode 100644 index 00000000..b22b8b39 --- /dev/null +++ b/Minecraft.World/ForestBiome.cpp @@ -0,0 +1,26 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.h" +#include "ForestBiome.h" + +ForestBiome::ForestBiome(int id) : Biome(id) +{ + friendlies_wolf.push_back(new MobSpawnerData(eTYPE_WOLF, 5, 4, 4)); // 4J - moved to their own category + decorator->treeCount = 10; + decorator->grassCount = 2; +} + +Feature *ForestBiome::getTreeFeature(Random *random) +{ + if (random->nextInt(5) == 0) + { + return new BirchFeature(false); // 4J used to return member birchTree, now returning newly created object so that caller can be consistently resposible for cleanup + } + if (random->nextInt(10) == 0) + { + return new BasicTree(false); // 4J used to return member fancyTree, now returning newly created object so that caller can be consistently resposible for cleanup + } + return new TreeFeature(false); // 4J used to return member normalTree, now returning newly created object so that caller can be consistently resposible for cleanup +} diff --git a/Minecraft.World/ForestBiome.h b/Minecraft.World/ForestBiome.h new file mode 100644 index 00000000..e8045bd4 --- /dev/null +++ b/Minecraft.World/ForestBiome.h @@ -0,0 +1,10 @@ +#pragma once +#include "Biome.h" + +class ForestBiome : public Biome +{ +public: + ForestBiome(int id); + + virtual Feature *getTreeFeature(Random *random); +}; diff --git a/Minecraft.World/FurnaceMenu.cpp b/Minecraft.World/FurnaceMenu.cpp new file mode 100644 index 00000000..dca02231 --- /dev/null +++ b/Minecraft.World/FurnaceMenu.cpp @@ -0,0 +1,178 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "FurnaceResultSlot.h" +#include "Slot.h" +#include "GenericStats.h" +#include "FurnaceMenu.h" +#include "FurnaceRecipes.h" + +FurnaceMenu::FurnaceMenu(shared_ptr inventory, shared_ptr furnace) : AbstractContainerMenu() +{ + tc = 0; + lt = 0; + ld = 0; + + this->furnace = furnace; + + addSlot(new Slot(furnace, 0, 52 + 4, 13 + 4)); + addSlot(new Slot(furnace, 1, 52 + 4, 49 + 4)); + addSlot(new FurnaceResultSlot( dynamic_pointer_cast( inventory->player->shared_from_this() ), furnace, 2, 112 + 4, 31 + 4)); + + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x + y * 9 + 9, 8 + x * 18, 84 + y * 18)); + } + } + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x, 8 + x * 18, 142)); + } +} + +void FurnaceMenu::addSlotListener(ContainerListener *listener) +{ + AbstractContainerMenu::addSlotListener(listener); + listener->setContainerData(this, 0, furnace->tickCount); + listener->setContainerData(this, 1, furnace->litTime); + listener->setContainerData(this, 2, furnace->litDuration); +} + +void FurnaceMenu::broadcastChanges() +{ + AbstractContainerMenu::broadcastChanges(); + + AUTO_VAR(itEnd, containerListeners->end()); + for (AUTO_VAR(it, containerListeners->begin()); it != itEnd; it++) + { + ContainerListener *listener = *it; //containerListeners->at(i); + if (tc != furnace->tickCount) + { + listener->setContainerData(this, 0, furnace->tickCount); + } + if (lt != furnace->litTime) + { + listener->setContainerData(this, 1, furnace->litTime); + } + if (ld != furnace->litDuration) + { + listener->setContainerData(this, 2, furnace->litDuration); + } + } + + tc = furnace->tickCount; + lt = furnace->litTime; + ld = furnace->litDuration; +} + +void FurnaceMenu::setData(int id, int value) +{ + if (id == 0) furnace->tickCount = value; + if (id == 1) furnace->litTime = value; + if (id == 2) furnace->litDuration = value; +} + +bool FurnaceMenu::stillValid(shared_ptr player) +{ + return furnace->stillValid(player); +} + +shared_ptr FurnaceMenu::quickMoveStack(shared_ptr player, int slotIndex) +{ + shared_ptr clicked = nullptr; + Slot *slot = slots->at(slotIndex); + //Slot *IngredientSlot = slots->at(INGREDIENT_SLOT); + + bool charcoalUsed = furnace->wasCharcoalUsed(); + + if (slot != NULL && slot->hasItem()) + { + shared_ptr stack = slot->getItem(); + clicked = stack->copy(); + + if (slotIndex == RESULT_SLOT) + { + if (!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, true)) + { + return nullptr; + } + slot->onQuickCraft(stack, clicked); + + // 4J-JEV, hook for Durango achievement 'Renewable Energy'. +#ifdef _EXTENDED_ACHIEVEMENTS + if ( charcoalUsed && stack->getItem()->id == Item::coal_Id && stack->getAuxValue() == CoalItem::CHAR_COAL) + player->awardStat(GenericStats::renewableEnergy(),GenericStats::param_renewableEnergy()); +#endif + } + else if (slotIndex == FUEL_SLOT || slotIndex == INGREDIENT_SLOT) + { + if (!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + } + else if (FurnaceRecipes::getInstance()->getResult(stack->getItem()->id) != NULL) + { + if (!moveItemStackTo(stack, INGREDIENT_SLOT, INGREDIENT_SLOT + 1, false)) + { + return nullptr; + } + } + else if (FurnaceTileEntity::isFuel(stack)) + { + if (!moveItemStackTo(stack, FUEL_SLOT, FUEL_SLOT + 1, false)) + { + return nullptr; + } + } else if (slotIndex >= INV_SLOT_START && slotIndex < INV_SLOT_END) + { + if (!moveItemStackTo(stack, USE_ROW_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + } else if (slotIndex >= USE_ROW_SLOT_START && slotIndex < USE_ROW_SLOT_END) + { + if (!moveItemStackTo(stack, INV_SLOT_START, INV_SLOT_END, false)) + { + return nullptr; + } + } + if (stack->count == 0) + { + slot->set(nullptr); + } + else + { + slot->setChanged(); + } + if (stack->count == clicked->count) + { + return nullptr; + } + else + { + slot->onTake(player, stack); + } + } + return clicked; +} + +shared_ptr FurnaceMenu::clicked(int slotIndex, int buttonNum, int clickType, shared_ptr player) +{ + bool charcoalUsed = furnace->wasCharcoalUsed(); + + shared_ptr out = AbstractContainerMenu::clicked(slotIndex, buttonNum, clickType, player); + +#ifdef _EXTENDED_ACHIEVEMENTS + if ( charcoalUsed && (out!=nullptr) && (buttonNum==0 || buttonNum==1) && clickType==CLICK_PICKUP + && out->getItem()->id == Item::coal_Id && out->getAuxValue() == CoalItem::CHAR_COAL ) + { + player->awardStat(GenericStats::renewableEnergy(),GenericStats::param_renewableEnergy()); + } +#endif + + return out; +} diff --git a/Minecraft.World/FurnaceMenu.h b/Minecraft.World/FurnaceMenu.h new file mode 100644 index 00000000..72b264d3 --- /dev/null +++ b/Minecraft.World/FurnaceMenu.h @@ -0,0 +1,38 @@ +#pragma once + +#include "AbstractContainerMenu.h" + +class FurnaceTileEntity; + +class FurnaceMenu : public AbstractContainerMenu +{ + // 4J Stu Made these public for UI menus, perhaps should make friend class? +public: + static const int INGREDIENT_SLOT = 0; + static const int FUEL_SLOT = 1; + static const int RESULT_SLOT = 2; + static const int INV_SLOT_START = FurnaceMenu::RESULT_SLOT + 1; + static const int INV_SLOT_END = FurnaceMenu::INV_SLOT_START + 9 * 3; + static const int USE_ROW_SLOT_START = FurnaceMenu::INV_SLOT_END; + static const int USE_ROW_SLOT_END = FurnaceMenu::USE_ROW_SLOT_START + 9; + +private: + shared_ptr furnace; + +public: + FurnaceMenu(shared_ptr inventory, shared_ptr furnace); + +private: + int tc; + int lt; + int ld; + +public: + virtual void addSlotListener(ContainerListener *listener); + virtual void broadcastChanges(); + virtual void setData(int id, int value); + virtual bool stillValid(shared_ptr player); + virtual shared_ptr quickMoveStack(shared_ptr player, int slotIndex); + + virtual shared_ptr clicked(int slotIndex, int buttonNum, int clickType, shared_ptr player); +}; diff --git a/Minecraft.World/FurnaceRecipes.cpp b/Minecraft.World/FurnaceRecipes.cpp new file mode 100644 index 00000000..49ded1f5 --- /dev/null +++ b/Minecraft.World/FurnaceRecipes.cpp @@ -0,0 +1,82 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "Tile.h" +#include "FurnaceRecipes.h" + +FurnaceRecipes *FurnaceRecipes::instance = NULL; + +void FurnaceRecipes::staticCtor() +{ + FurnaceRecipes::instance = new FurnaceRecipes(); +} + +FurnaceRecipes *FurnaceRecipes::getInstance() +{ + return instance; +} + +FurnaceRecipes::FurnaceRecipes() +{ + addFurnaceRecipy(Tile::ironOre_Id, new ItemInstance(Item::ironIngot), .7f); + addFurnaceRecipy(Tile::goldOre_Id, new ItemInstance(Item::goldIngot), 1); + addFurnaceRecipy(Tile::diamondOre_Id, new ItemInstance(Item::diamond), 1); + addFurnaceRecipy(Tile::sand_Id, new ItemInstance(Tile::glass), .1f); + addFurnaceRecipy(Item::porkChop_raw_Id, new ItemInstance(Item::porkChop_cooked), .35f); + addFurnaceRecipy(Item::beef_raw_Id, new ItemInstance(Item::beef_cooked), .35f); + addFurnaceRecipy(Item::chicken_raw_Id, new ItemInstance(Item::chicken_cooked), .35f); + addFurnaceRecipy(Item::fish_raw_Id, new ItemInstance(Item::fish_cooked), .35f); + addFurnaceRecipy(Tile::stoneBrick_Id, new ItemInstance(Tile::rock), .1f); + addFurnaceRecipy(Item::clay_Id, new ItemInstance(Item::brick), .3f); + addFurnaceRecipy(Tile::cactus_Id, new ItemInstance(Item::dye_powder, 1, DyePowderItem::GREEN), .2f); + addFurnaceRecipy(Tile::treeTrunk_Id, new ItemInstance(Item::coal, 1, CoalItem::CHAR_COAL), .15f); + addFurnaceRecipy(Tile::emeraldOre_Id, new ItemInstance(Item::emerald), 1); + addFurnaceRecipy(Item::potato_Id, new ItemInstance(Item::potatoBaked), .35f); + // 4J - TU9 - add in smelting netherrack + addFurnaceRecipy(Tile::hellRock_Id, new ItemInstance(Item::netherbrick), .1f); + + // special silk touch related recipes: + addFurnaceRecipy(Tile::coalOre_Id, new ItemInstance(Item::coal), .1f); + addFurnaceRecipy(Tile::redStoneOre_Id, new ItemInstance(Item::redStone), .7f); + addFurnaceRecipy(Tile::lapisOre_Id, new ItemInstance(Item::dye_powder, 1, DyePowderItem::BLUE), .2f); + addFurnaceRecipy(Tile::netherQuartz_Id, new ItemInstance(Item::netherQuartz), .2f); + + +} + +void FurnaceRecipes::addFurnaceRecipy(int itemId, ItemInstance *result, float value) +{ + //recipies->put(itemId, result); + recipies[itemId]=result; + recipeValue[result->id] = value; +} + +bool FurnaceRecipes::isFurnaceItem(int itemId) +{ + AUTO_VAR(it, recipies.find(itemId)); + return it != recipies.end(); +} + +ItemInstance *FurnaceRecipes::getResult(int itemId) +{ + AUTO_VAR(it, recipies.find(itemId)); + if(it != recipies.end()) + { + return it->second; + } + return NULL; +} + +unordered_map *FurnaceRecipes::getRecipies() +{ + return &recipies; +} + +float FurnaceRecipes::getRecipeValue(int itemId) +{ + AUTO_VAR(it, recipeValue.find(itemId)); + if (it != recipeValue.end()) + { + return it->second; + } + return 0.0f; +} \ No newline at end of file diff --git a/Minecraft.World/FurnaceRecipes.h b/Minecraft.World/FurnaceRecipes.h new file mode 100644 index 00000000..168831c0 --- /dev/null +++ b/Minecraft.World/FurnaceRecipes.h @@ -0,0 +1,30 @@ +#pragma once + +class FurnaceRecipes +{ + +private: + static FurnaceRecipes *instance; + +public: + static void staticCtor(); + +private: + //Map recipies = new HashMap(); + unordered_map recipies; + unordered_map recipeValue; + +public: + static FurnaceRecipes *getInstance(); + +public: + FurnaceRecipes(); + +public: + void addFurnaceRecipy(int itemId, ItemInstance *result, float value); + bool isFurnaceItem(int itemId); + ItemInstance *getResult(int itemId); + unordered_map *getRecipies(); + float getRecipeValue(int itemId); + +}; diff --git a/Minecraft.World/FurnaceResultSlot.cpp b/Minecraft.World/FurnaceResultSlot.cpp new file mode 100644 index 00000000..d845dada --- /dev/null +++ b/Minecraft.World/FurnaceResultSlot.cpp @@ -0,0 +1,94 @@ +#include "stdafx.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.item.crafting.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "JavaMath.h" +#include "FurnaceResultSlot.h" + + +FurnaceResultSlot::FurnaceResultSlot(shared_ptr player, shared_ptr container, int slot, int x, int y) : Slot( container, slot, x, y ) +{ + this->player = player; + removeCount = 0; +} + +bool FurnaceResultSlot::mayPlace(shared_ptr item) +{ + return false; +} + +shared_ptr FurnaceResultSlot::remove(int c) +{ + if (hasItem()) + { + removeCount += min(c, getItem()->count); + } + return Slot::remove(c); +} + +void FurnaceResultSlot::onTake(shared_ptr player, shared_ptr carried) +{ + checkTakeAchievements(carried); + Slot::onTake(player, carried); +} + +void FurnaceResultSlot::onQuickCraft(shared_ptr picked, int count) +{ + removeCount += count; + checkTakeAchievements(picked); +} + +bool FurnaceResultSlot::mayCombine(shared_ptr second) +{ + return false; +} + +void FurnaceResultSlot::checkTakeAchievements(shared_ptr carried) +{ + carried->onCraftedBy(player->level, player, removeCount); + // spawn xp right on top of the player + if (!player->level->isClientSide) + { + int amount = removeCount; + float value = FurnaceRecipes::getInstance()->getRecipeValue(carried->id); + + if (value == 0) + { + amount = 0; + } + else if (value < 1) + { + int baseValue = floor((float) amount * value); + if (baseValue < ceil((float) amount * value) && (float) Math::random() < (((float) amount * value) - baseValue)) + { + baseValue++; + } + amount = baseValue; + } + + while (amount > 0) + { + int newCount = ExperienceOrb::getExperienceValue(amount); + amount -= newCount; + player->level->addEntity(shared_ptr( new ExperienceOrb(player->level, player->x, player->y + .5, player->z + .5, newCount) )); + } + } + +#ifdef _DURANGO + if (!player->level->isClientSide && removeCount > 0) + { + player->awardStat( + GenericStats::itemsSmelted(carried->id), + GenericStats::param_itemsSmelted(carried->id, carried->getAuxValue(), removeCount) + ); + } +#else + if (carried->id == Item::ironIngot_Id) player->awardStat(GenericStats::acquireIron(), GenericStats::param_acquireIron()); + if (carried->id == Item::fish_cooked_Id) player->awardStat(GenericStats::cookFish(), GenericStats::param_cookFish()); +#endif + + removeCount = 0; +} diff --git a/Minecraft.World/FurnaceResultSlot.h b/Minecraft.World/FurnaceResultSlot.h new file mode 100644 index 00000000..bc770fab --- /dev/null +++ b/Minecraft.World/FurnaceResultSlot.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Slot.h" + +class FurnaceResultSlot : public Slot +{ +private: + shared_ptr player; + int removeCount; + +public: + FurnaceResultSlot(shared_ptr player, shared_ptr container, int slot, int x, int y); + virtual ~FurnaceResultSlot() {} + + virtual bool mayPlace(shared_ptr item); + virtual shared_ptr remove(int c); + virtual void onTake(shared_ptr player, shared_ptr carried); + virtual bool mayCombine(shared_ptr item); // 4J Added + +protected: + virtual void onQuickCraft(shared_ptr picked, int count); + virtual void checkTakeAchievements(shared_ptr carried); +}; \ No newline at end of file diff --git a/Minecraft.World/FurnaceTile.cpp b/Minecraft.World/FurnaceTile.cpp new file mode 100644 index 00000000..ea0f84e8 --- /dev/null +++ b/Minecraft.World/FurnaceTile.cpp @@ -0,0 +1,208 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.h" +#include "FurnaceTile.h" +#include "Mob.h" +#include "Facing.h" + +bool FurnaceTile::noDrop = false; + +FurnaceTile::FurnaceTile(int id, bool lit) : EntityTile(id, Material::stone) +{ + random = new Random(); + this->lit = lit; + + iconTop = NULL; + iconFront = NULL; +} + +int FurnaceTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::furnace_Id; +} + +void FurnaceTile::onPlace(Level *level, int x, int y, int z) +{ + EntityTile::onPlace(level, x, y, z); + recalcLockDir(level, x, y, z); +} + +void FurnaceTile::recalcLockDir(Level *level, int x, int y, int z) +{ + if (level->isClientSide) + { + return; + } + + int n = level->getTile(x, y, z - 1); // face = 2 + int s = level->getTile(x, y, z + 1); // face = 3 + int w = level->getTile(x - 1, y, z); // face = 4 + int e = level->getTile(x + 1, y, z); // face = 5 + + int lockDir = 3; + if (Tile::solid[n] && !Tile::solid[s]) lockDir = 3; + if (Tile::solid[s] && !Tile::solid[n]) lockDir = 2; + if (Tile::solid[w] && !Tile::solid[e]) lockDir = 5; + if (Tile::solid[e] && !Tile::solid[w]) lockDir = 4; + level->setData(x, y, z, lockDir); +} + +Icon *FurnaceTile::getTexture(int face, int data) +{ + if (face == Facing::UP) return iconTop; + if (face == Facing::DOWN) return iconTop; + + if (face != data) return icon; + return iconFront; +} + +void FurnaceTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"furnace_side"); + iconFront = iconRegister->registerIcon(lit ? L"furnace_front_lit" : L"furnace_front"); + iconTop = iconRegister->registerIcon(L"furnace_top"); +} + +void FurnaceTile::animateTick(Level *level, int xt, int yt, int zt, Random *random) +{ + if (!lit) return; + + int dir = level->getData(xt, yt, zt); + + float x = xt + 0.5f; + float y = yt + 0.0f + random->nextFloat() * 6 / 16.0f; + float z = zt + 0.5f; + float r = 0.52f; + float ss = random->nextFloat() * 0.6f - 0.3f; + + if (dir == 4) + { + level->addParticle(eParticleType_smoke, x - r, y, z + ss, 0, 0, 0); + level->addParticle(eParticleType_flame, x - r, y, z + ss, 0, 0, 0); + } else if (dir == 5) + { + level->addParticle(eParticleType_smoke, x + r, y, z + ss, 0, 0, 0); + level->addParticle(eParticleType_flame, x + r, y, z + ss, 0, 0, 0); + } else if (dir == 2) + { + level->addParticle(eParticleType_smoke, x + ss, y, z - r, 0, 0, 0); + level->addParticle(eParticleType_flame, x + ss, y, z - r, 0, 0, 0); + } else if (dir == 3) + { + level->addParticle(eParticleType_smoke, x + ss, y, z + r, 0, 0, 0); + level->addParticle(eParticleType_flame, x + ss, y, z + r, 0, 0, 0); + } +} + +// 4J-PB - Adding a TestUse for tooltip display +bool FurnaceTile::TestUse() +{ + return true; +} + +bool FurnaceTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if( soundOnly) return false; + + if (level->isClientSide) + { + return true; + } + shared_ptr furnace = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + if (furnace != NULL ) player->openFurnace(furnace); + return true; +} + +void FurnaceTile::setLit(bool lit, Level *level, int x, int y, int z) +{ + int data = level->getData(x, y, z); + shared_ptr te = level->getTileEntity(x, y, z); + + noDrop = true; + if (lit) level->setTile(x, y, z, Tile::furnace_lit_Id); + else level->setTile(x, y, z, Tile::furnace_Id); + noDrop = false; + + level->setData(x, y, z, data); + if( te != NULL ) + { + te->clearRemoved(); + level->setTileEntity(x, y, z, te); + } +} + +shared_ptr FurnaceTile::newTileEntity(Level *level) +{ + return shared_ptr( new FurnaceTileEntity() ); +} + +void FurnaceTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int dir = (Mth::floor(by->yRot * 4 / (360) + 0.5)) & 3; + + if (dir == 0) level->setData(x, y, z, Facing::NORTH); + if (dir == 1) level->setData(x, y, z, Facing::EAST); + if (dir == 2) level->setData(x, y, z, Facing::SOUTH); + if (dir == 3) level->setData(x, y, z, Facing::WEST); +} + +void FurnaceTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + if (!noDrop) + { + shared_ptr container = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + if( container != NULL ) + { + for (unsigned int i = 0; i < container->getContainerSize(); i++) + { + shared_ptr item = container->getItem(i); + if (item != NULL) + { + float xo = random->nextFloat() * 0.8f + 0.1f; + float yo = random->nextFloat() * 0.8f + 0.1f; + float zo = random->nextFloat() * 0.8f + 0.1f; + + while (item->count > 0) + { + int count = random->nextInt(21) + 10; + if (count > item->count) count = item->count; + item->count -= count; + +#ifndef _CONTENT_PACKAGE + if(level->isClientSide) + { + printf("Client furnace dropping %d of %d/%d\n", count, item->id, item->getAuxValue() ); + } + else + { + printf("Server furnace dropping %d of %d/%d\n", count, item->id, item->getAuxValue() ); + } +#endif + + shared_ptr newItem = shared_ptr( new ItemInstance(item->id, count, item->getAuxValue()) ); + newItem->set4JData( item->get4JData() ); + shared_ptr itemEntity = shared_ptr( new ItemEntity(level, x + xo, y + yo, z + zo, newItem) ); + float pow = 0.05f; + itemEntity->xd = (float) random->nextGaussian() * pow; + itemEntity->yd = (float) random->nextGaussian() * pow + 0.2f; + itemEntity->zd = (float) random->nextGaussian() * pow; + if (item->hasTag()) + { + itemEntity->getItem()->setTag((CompoundTag *) item->getTag()->copy()); + } + level->addEntity(itemEntity); + } + + // 4J Stu - Fix for duplication glitch + container->setItem(i,nullptr); + } + } + } + } + EntityTile::onRemove(level, x, y, z, id, data); + +} diff --git a/Minecraft.World/FurnaceTile.h b/Minecraft.World/FurnaceTile.h new file mode 100644 index 00000000..c192f94d --- /dev/null +++ b/Minecraft.World/FurnaceTile.h @@ -0,0 +1,39 @@ +#pragma once +#include "EntityTile.h" + +class Mob; +class Player; +class Random; +class ChunkRebuildData; + +class FurnaceTile : public EntityTile +{ + friend class Tile; + friend class ChunkRebuildData; +private: + Random *random; + bool lit; + static bool noDrop; + Icon *iconTop; + Icon *iconFront; + +protected: + FurnaceTile(int id, bool lit); +public: + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual void onPlace(Level *level, int x, int y, int z); +private: + void recalcLockDir(Level *level, int x, int y, int z); +public: + Icon *getTexture(int face, int data); + void registerIcons(IconRegister *iconRegister); + virtual void animateTick(Level *level, int xt, int yt, int zt, Random *random); + virtual bool TestUse(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + static void setLit(bool lit, Level *level, int x, int y, int z); +protected: + virtual shared_ptr newTileEntity(Level *level); +public: + virtual void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); +}; \ No newline at end of file diff --git a/Minecraft.World/FurnaceTileEntity.cpp b/Minecraft.World/FurnaceTileEntity.cpp new file mode 100644 index 00000000..0f18528c --- /dev/null +++ b/Minecraft.World/FurnaceTileEntity.cpp @@ -0,0 +1,350 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.crafting.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "Material.h" +#include "FurnaceTileEntity.h" + + + +const int FurnaceTileEntity::BURN_INTERVAL = 10 * 20; + +// 4J Stu - Need a ctor to initialise member variables +FurnaceTileEntity::FurnaceTileEntity() : TileEntity() +{ + items = new ItemInstanceArray(3); + + litTime = 0; + + litDuration = 0; + + tickCount = 0; + + m_charcoalUsed = false; +} + +FurnaceTileEntity::~FurnaceTileEntity() +{ + delete[] items->data; + delete items; +} + + +unsigned int FurnaceTileEntity::getContainerSize() +{ + return items->length; +} + + +shared_ptr FurnaceTileEntity::getItem(unsigned int slot) +{ + return (*items)[slot]; +} + + +shared_ptr FurnaceTileEntity::removeItem(unsigned int slot, int count) +{ + m_charcoalUsed = false; + + if ((*items)[slot] != NULL) + { + if ((*items)[slot]->count <= count) + { + shared_ptr item = (*items)[slot]; + (*items)[slot] = nullptr; + // 4J Stu - Fix for duplication glitch + if(item->count <= 0) return nullptr; + return item; + } + else + { + shared_ptr i = (*items)[slot]->remove(count); + if ((*items)[slot]->count == 0) (*items)[slot] = nullptr; + // 4J Stu - Fix for duplication glitch + if(i->count <= 0) return nullptr; + return i; + } + } + return nullptr; +} + +shared_ptr FurnaceTileEntity::removeItemNoUpdate(int slot) +{ + m_charcoalUsed = false; + + if (items->data[slot] != NULL) + { + shared_ptr item = items->data[slot]; + items->data[slot] = nullptr; + return item; + } + return nullptr; +} + + +void FurnaceTileEntity::setItem(unsigned int slot, shared_ptr item) +{ + (*items)[slot] = item; + if (item != NULL && item->count > getMaxStackSize()) item->count = getMaxStackSize(); +} + + +int FurnaceTileEntity::getName() +{ + return IDS_TILE_FURNACE; +} + +void FurnaceTileEntity::load(CompoundTag *base) +{ + TileEntity::load(base); + ListTag *inventoryList = (ListTag *) base->getList(L"Items"); + items = new ItemInstanceArray(getContainerSize()); + for (int i = 0; i < inventoryList->size(); i++) + { + CompoundTag *tag = inventoryList->get(i); + unsigned int slot = tag->getByte(L"Slot"); + if (slot >= 0 && slot < items->length) (*items)[slot] = ItemInstance::fromTag(tag); + } + + litTime = base->getShort(L"BurnTime"); + tickCount = base->getShort(L"CookTime"); + litDuration = getBurnDuration((*items)[FUEL_SLOT]); + + m_charcoalUsed = base->getBoolean(L"CharcoalUsed"); +} + + +void FurnaceTileEntity::save(CompoundTag *base) +{ + TileEntity::save(base); + base->putShort(L"BurnTime", (short) (litTime)); + base->putShort(L"CookTime", (short) (tickCount)); + ListTag *listTag = new ListTag(); + + for (unsigned int i = 0; i < items->length; i++) + { + if ((*items)[i] != NULL) + { + CompoundTag *tag = new CompoundTag(); + tag->putByte(L"Slot", (byte) i); + (*items)[i]->save(tag); + listTag->add(tag); + } + } + base->put(L"Items", listTag); + + base->putBoolean(L"CharcoalUsed", m_charcoalUsed); +} + + +int FurnaceTileEntity::getMaxStackSize() +{ + return Container::LARGE_MAX_STACK_SIZE; +} + + +int FurnaceTileEntity::getBurnProgress(int max) +{ + return tickCount * max / BURN_INTERVAL; +} + + +int FurnaceTileEntity::getLitProgress(int max) +{ + if (litDuration == 0) litDuration = BURN_INTERVAL; + return litTime * max / litDuration; +} + + +bool FurnaceTileEntity::isLit() +{ + return litTime > 0; +} + + +void FurnaceTileEntity::tick() +{ + bool wasLit = litTime > 0; + bool changed = false; + if (litTime > 0) + { + litTime--; + } + + if ( level != NULL && !level->isClientSide) + { + if (litTime == 0 && canBurn()) + { + litDuration = litTime = getBurnDuration((*items)[FUEL_SLOT]); + if (litTime > 0) + { + changed = true; + if ((*items)[FUEL_SLOT] != NULL) + { + // 4J Added: Keep track of whether charcoal was used in production of current stack. + if ( (*items)[FUEL_SLOT]->getItem()->id == Item::coal_Id + && (*items)[FUEL_SLOT]->getAuxValue() == CoalItem::CHAR_COAL) + { + m_charcoalUsed = true; + } + + (*items)[FUEL_SLOT]->count--; + if ((*items)[FUEL_SLOT]->count == 0) + { + Item *remaining = (*items)[FUEL_SLOT]->getItem()->getCraftingRemainingItem(); + (*items)[FUEL_SLOT] = remaining != NULL ? shared_ptr(new ItemInstance(remaining)) : nullptr; + } + } + } + } + + if (isLit() && canBurn()) + { + tickCount++; + if (tickCount == BURN_INTERVAL) + { + tickCount = 0; + burn(); + changed = true; + } + } + else + { + tickCount = 0; + } + + if (wasLit != litTime > 0) + { + changed = true; + FurnaceTile::setLit(litTime > 0, level, x, y, z); + } + } + + if (changed) setChanged(); +} + + +bool FurnaceTileEntity::canBurn() +{ + if ((*items)[INPUT_SLOT] == NULL) return false; + ItemInstance *burnResult = FurnaceRecipes::getInstance()->getResult((*items)[0]->getItem()->id); + if (burnResult == NULL) return false; + if ((*items)[RESULT_SLOT] == NULL) return true; + if (!(*items)[RESULT_SLOT]->sameItem_not_shared(burnResult)) return false; + if ((*items)[RESULT_SLOT]->count < getMaxStackSize() && (*items)[RESULT_SLOT]->count < (*items)[RESULT_SLOT]->getMaxStackSize()) return true; + if ((*items)[RESULT_SLOT]->count < burnResult->getMaxStackSize()) return true; + return false; +} + + +void FurnaceTileEntity::burn() +{ + if (!canBurn()) return; + + ItemInstance *result = FurnaceRecipes::getInstance()->getResult((*items)[0]->getItem()->id); + if ((*items)[RESULT_SLOT] == NULL) (*items)[RESULT_SLOT] = result->copy(); + else if ((*items)[RESULT_SLOT]->id == result->id) (*items)[RESULT_SLOT]->count++; + + (*items)[INPUT_SLOT]->count--; + if ((*items)[INPUT_SLOT]->count <= 0) (*items)[INPUT_SLOT] = nullptr; +} + + +int FurnaceTileEntity::getBurnDuration(shared_ptr itemInstance) +{ + if (itemInstance == NULL) return 0; + int id = itemInstance->getItem()->id; + + Item *item = itemInstance->getItem(); + + if (id < 256 && Tile::tiles[id] != NULL) + { + Tile *tile = Tile::tiles[id]; + + if (tile == Tile::woodSlabHalf) + { + return BURN_INTERVAL * 3 / 4; + } + + if (tile->material == Material::wood) + { + return BURN_INTERVAL * 3 / 2; + } + } + + if (dynamic_cast(item) && ((DiggerItem *) item)->getTier() == Item::Tier::WOOD) + { + return BURN_INTERVAL; + } + else if (dynamic_cast(item) && ((WeaponItem *) item)->getTier() == Item::Tier::WOOD) + { + return BURN_INTERVAL; + } + else if (dynamic_cast(item) && ((HoeItem *) item)->getTier() == Item::Tier::WOOD) + { + return BURN_INTERVAL; + } + + if (id == Item::stick->id) + { + return BURN_INTERVAL / 2; + } + + if (id == Item::coal->id) return BURN_INTERVAL * 8; + + if (id == Item::bucket_lava->id) return BURN_INTERVAL * 100; + + if (id == Tile::sapling_Id) return BURN_INTERVAL / 2; + + if (id == Item::blazeRod_Id) return BURN_INTERVAL * 12; + + return 0; +} + +bool FurnaceTileEntity::isFuel(shared_ptr item) +{ + return getBurnDuration(item) > 0; +} + +bool FurnaceTileEntity::stillValid(shared_ptr player) +{ + if (level->getTileEntity(x, y, z) != shared_from_this() ) return false; + if (player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) > 8 * 8) return false; + return true; +} + +void FurnaceTileEntity::setChanged() +{ + return TileEntity::setChanged(); +} + +void FurnaceTileEntity::startOpen() +{ +} + +void FurnaceTileEntity::stopOpen() +{ +} + +// 4J Added +shared_ptr FurnaceTileEntity::clone() +{ + shared_ptr result = shared_ptr( new FurnaceTileEntity() ); + TileEntity::clone(result); + + result->litTime = litTime; + result->tickCount = tickCount; + result->litDuration = litDuration; + + for (unsigned int i = 0; i < items->length; i++) + { + if ((*items)[i] != NULL) + { + (*result->items)[i] = ItemInstance::clone((*items)[i]); + } + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/FurnaceTileEntity.h b/Minecraft.World/FurnaceTileEntity.h new file mode 100644 index 00000000..db39452e --- /dev/null +++ b/Minecraft.World/FurnaceTileEntity.h @@ -0,0 +1,78 @@ +#pragma once +using namespace std; + +#include "FurnaceTile.h" +#include "TileEntity.h" +#include "Container.h" + +class Player; +class Level; + +class FurnaceTileEntity : public TileEntity, public Container +{ +public: + eINSTANCEOF GetType() { return eTYPE_FURNACETILEENTITY; } + static TileEntity *create() { return new FurnaceTileEntity(); } + +using TileEntity::setChanged; + +private: + static const int BURN_INTERVAL; + ItemInstanceArray *items; + + enum { + INPUT_SLOT = 0, + FUEL_SLOT, + RESULT_SLOT, + }; + + // 4J-JEV: Added for 'Renewable Energy' achievement. + // Should be true iff characoal was consumed whilst cooking the current stack. + bool m_charcoalUsed; + +public: + int litTime; + int litDuration; + int tickCount; + +public: + // 4J Stu - Need a ctor to initialise member variables + FurnaceTileEntity(); + virtual ~FurnaceTileEntity(); + + virtual unsigned int getContainerSize(); + virtual shared_ptr getItem(unsigned int slot); + virtual shared_ptr removeItem(unsigned int slot, int count); + virtual shared_ptr removeItemNoUpdate(int slot); + virtual void setItem(unsigned int slot, shared_ptr item); + virtual int getName(); + virtual void load(CompoundTag *base); + virtual void save(CompoundTag *base); + virtual int getMaxStackSize(); + int getBurnProgress(int max); + int getLitProgress(int max); + bool isLit(); + virtual void tick(); + +private: + bool canBurn(); + +public: + void burn(); + + static int getBurnDuration(shared_ptr itemInstance); + static bool isFuel(shared_ptr item); + +public: + virtual bool stillValid(shared_ptr player); + virtual void setChanged(); + + void startOpen(); + void stopOpen(); + + // 4J Added + virtual shared_ptr clone(); + + // 4J-JEV: Added for 'Renewable Energy' achievement. + bool wasCharcoalUsed() { return m_charcoalUsed; } +}; \ No newline at end of file diff --git a/Minecraft.World/FuzzyZoomLayer.cpp b/Minecraft.World/FuzzyZoomLayer.cpp new file mode 100644 index 00000000..65c6709e --- /dev/null +++ b/Minecraft.World/FuzzyZoomLayer.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" +#include "System.h" +#include "net.minecraft.world.level.newbiome.layer.h" + +FuzzyZoomLayer::FuzzyZoomLayer(__int64 seedMixup, shared_ptrparent) : Layer(seedMixup) +{ + this->parent = parent; +} + +intArray FuzzyZoomLayer::getArea(int xo, int yo, int w, int h) +{ + int px = xo >> 1; + int py = yo >> 1; + int pw = (w >> 1) + 3; + int ph = (h >> 1) + 3; + intArray p = parent->getArea(px, py, pw, ph); + + intArray tmp = IntCache::allocate((pw * 2) * (ph * 2)); + int ww = (pw << 1); + for (int y = 0; y < ph - 1; y++) + { + int ry = y << 1; + int pp = ry * ww; + int ul = p[(0 + 0) + (y + 0) * pw]; + int dl = p[(0 + 0) + (y + 1) * pw]; + for (int x = 0; x < pw - 1; x++) + { + initRandom((x + px) << 1, (y + py) << 1); + int ur = p[(x + 1) + (y + 0) * pw]; + int dr = p[(x + 1) + (y + 1) * pw]; + + tmp[pp] = ul; + tmp[pp++ + ww] = random(ul, dl); + tmp[pp] = random(ul, ur); + tmp[pp++ + ww] = random(ul, ur, dl, dr); + + ul = ur; + dl = dr; + } + } + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + System::arraycopy(tmp, (y + (yo & 1)) * (pw << 1) + (xo & 1), &result, y * w, w); + } + + return result; +} + +int FuzzyZoomLayer::random(int a, int b) +{ + return nextRandom(2) == 0 ? a : b; +} + +int FuzzyZoomLayer::random(int a, int b, int c, int d) +{ + int s = nextRandom(4); + if (s == 0) return a; + if (s == 1) return b; + if (s == 2) return c; + return d; +} + +shared_ptrFuzzyZoomLayer::zoom(__int64 seed, shared_ptrsup, int count) +{ + shared_ptr result = sup; + for (int i = 0; i < count; i++) + { + result = shared_ptr(new FuzzyZoomLayer(seed + i, result)); + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/FuzzyZoomLayer.h b/Minecraft.World/FuzzyZoomLayer.h new file mode 100644 index 00000000..bc8f5e48 --- /dev/null +++ b/Minecraft.World/FuzzyZoomLayer.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Layer.h" + +class FuzzyZoomLayer : public Layer +{ +public: + FuzzyZoomLayer(__int64 seedMixup, shared_ptrparent); + intArray getArea(int xo, int yo, int w, int h); + +protected: + int random(int a, int b); + int random(int a, int b, int c, int d); + +public: + static shared_ptrzoom(__int64 seed, shared_ptrsup, int count); +}; \ No newline at end of file diff --git a/Minecraft.World/GZIPInputStream.h b/Minecraft.World/GZIPInputStream.h new file mode 100644 index 00000000..f852518b --- /dev/null +++ b/Minecraft.World/GZIPInputStream.h @@ -0,0 +1,17 @@ +#pragma once +// 4J Stu - We are not using GZIP compression, so this is just a pass through class + +#include "InputStream.h" + +class GZIPInputStream : public InputStream +{ +private: + InputStream *stream; +public: + GZIPInputStream( InputStream *out ) : stream( out ) {}; + virtual int read() { return stream->read(); }; + virtual int read(byteArray b) { return stream->read( b ); }; + virtual int read(byteArray b, unsigned int offset, unsigned int length) { return stream->read(b, offset, length); }; + virtual void close() { return stream->close(); }; + virtual __int64 skip(__int64 n) { return 0; }; +}; \ No newline at end of file diff --git a/Minecraft.World/GZIPOutputStream.h b/Minecraft.World/GZIPOutputStream.h new file mode 100644 index 00000000..dcb99c4d --- /dev/null +++ b/Minecraft.World/GZIPOutputStream.h @@ -0,0 +1,17 @@ +#pragma once +// 4J Stu - We are not using GZIP compression, so this is just a pass through class + +#include "OutputStream.h" + +class GZIPOutputStream : public OutputStream +{ +private: + OutputStream *stream; +public: + GZIPOutputStream( OutputStream *out ) : stream( out ) {}; + virtual void write(unsigned int b) { stream->write( b ); }; + virtual void write(byteArray b) { stream->write( b ); } ; + virtual void write(byteArray b, unsigned int offset, unsigned int length) { stream->write(b, offset, length); }; + virtual void close() { stream->close(); }; + virtual void flush() {} +}; \ No newline at end of file diff --git a/Minecraft.World/GameCommandPacket.cpp b/Minecraft.World/GameCommandPacket.cpp new file mode 100644 index 00000000..98cdd312 --- /dev/null +++ b/Minecraft.World/GameCommandPacket.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "BasicTypeContainers.h" +#include "GameCommandPacket.h" + +GameCommandPacket::GameCommandPacket() +{ + length = 0; +} + +GameCommandPacket::GameCommandPacket(EGameCommand command, byteArray data) +{ + this->command = command; + this->data = data; + length = 0; + + if (data.data != NULL) + { + length = data.length; + + if (length > Short::MAX_VALUE) + { + app.DebugPrintf("Payload may not be larger than 32K\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + //throw new IllegalArgumentException("Payload may not be larger than 32k"); + } + } +} + +GameCommandPacket::~GameCommandPacket() +{ + delete [] data.data; +} + +void GameCommandPacket::read(DataInputStream *dis) +{ + command = (EGameCommand)dis->readInt(); + length = dis->readShort(); + + if (length > 0 && length < Short::MAX_VALUE) + { + if(data.data != NULL) + { + delete [] data.data; + } + data = byteArray(length); + dis->readFully(data); + } +} + +void GameCommandPacket::write(DataOutputStream *dos) +{ + dos->writeInt(command); + dos->writeShort((short) length); + if (data.data != NULL) + { + dos->write(data); + } +} + +void GameCommandPacket::handle(PacketListener *listener) +{ + listener->handleGameCommand( shared_from_this() ); +} + +int GameCommandPacket::getEstimatedSize() +{ + return 2 + 2 + length; +} \ No newline at end of file diff --git a/Minecraft.World/GameCommandPacket.h b/Minecraft.World/GameCommandPacket.h new file mode 100644 index 00000000..1f63e950 --- /dev/null +++ b/Minecraft.World/GameCommandPacket.h @@ -0,0 +1,26 @@ +#pragma once +using namespace std; + +#include "CommandsEnum.h" +#include "Packet.h" + +class GameCommandPacket : public Packet, public enable_shared_from_this +{ +public: + EGameCommand command; + int length; + byteArray data; + + GameCommandPacket(); + GameCommandPacket(EGameCommand command, byteArray data); + ~GameCommandPacket(); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new GameCommandPacket()); } + virtual int getId() { return 167; } +}; \ No newline at end of file diff --git a/Minecraft.World/GameEventPacket.cpp b/Minecraft.World/GameEventPacket.cpp new file mode 100644 index 00000000..a85d5fe0 --- /dev/null +++ b/Minecraft.World/GameEventPacket.cpp @@ -0,0 +1,59 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "GameEventPacket.h" + + + +const int GameEventPacket::NO_RESPAWN_BED_AVAILABLE = 0; +const int GameEventPacket::START_RAINING = 1; +const int GameEventPacket::STOP_RAINING = 2; +const int GameEventPacket::CHANGE_GAME_MODE = 3; // 1.8.2 +const int GameEventPacket::WIN_GAME = 4; // 1.0.1 +const int GameEventPacket::DEMO_EVENT = 5; + +const int GameEventPacket::DEMO_PARAM_INTRO = 0; +const int GameEventPacket::DEMO_PARAM_HINT_1 = 101; +const int GameEventPacket::DEMO_PARAM_HINT_2 = 102; +const int GameEventPacket::DEMO_PARAM_HINT_3 = 103; + +// 4J Added +const int GameEventPacket::START_SAVING = 10; +const int GameEventPacket::STOP_SAVING = 11; + +const int GameEventPacket::EVENT_LANGUAGE_ID[EVENT_LANGUAGE_ID_LENGTH] = { IDS_TILE_BED_NOT_VALID, -1, -1, IDS_GAME_MODE_CHANGED, -1, -1 }; + +GameEventPacket::GameEventPacket() +{ + this->_event = 0; + this->param = 0; +} + +GameEventPacket::GameEventPacket(int _event, int param) +{ + this->_event = _event; + this->param = param; +} + +void GameEventPacket::read(DataInputStream *dis) //throws IOException +{ + _event = dis->readByte(); + param = dis->readByte(); +} + +void GameEventPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeByte(_event); + dos->writeByte(param); +} + +void GameEventPacket::handle(PacketListener *listener) +{ + listener->handleGameEvent(shared_from_this()); +} + +int GameEventPacket::getEstimatedSize() +{ + return 2; +} diff --git a/Minecraft.World/GameEventPacket.h b/Minecraft.World/GameEventPacket.h new file mode 100644 index 00000000..93ea911c --- /dev/null +++ b/Minecraft.World/GameEventPacket.h @@ -0,0 +1,45 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class GameEventPacket : public Packet, public enable_shared_from_this +{ + +public: + static const int NO_RESPAWN_BED_AVAILABLE; + static const int START_RAINING; + static const int STOP_RAINING; + static const int CHANGE_GAME_MODE; // 1.8.2 + static const int WIN_GAME; // 1.0.01 + static const int DEMO_EVENT; // 1.3.2 + static const int SUCCESSFUL_BOW_HIT = 6; + + static const int DEMO_PARAM_INTRO; // 1.3.2 + static const int DEMO_PARAM_HINT_1; // 1.3.2 + static const int DEMO_PARAM_HINT_2; // 1.3.2 + static const int DEMO_PARAM_HINT_3; // 1.3.2 + + // 4J Added + static const int START_SAVING; + static const int STOP_SAVING; + + static const int EVENT_LANGUAGE_ID_LENGTH = 6; + static const int EVENT_LANGUAGE_ID[EVENT_LANGUAGE_ID_LENGTH]; + + + int _event; + int param; + + GameEventPacket(); + GameEventPacket(int evnt, int param); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new GameEventPacket()); } + virtual int getId() { return 70; } +}; \ No newline at end of file diff --git a/Minecraft.World/GameModeCommand.cpp b/Minecraft.World/GameModeCommand.cpp new file mode 100644 index 00000000..86d4c5a5 --- /dev/null +++ b/Minecraft.World/GameModeCommand.cpp @@ -0,0 +1,53 @@ +#include "stdafx.h" +#include "net.minecraft.commands.h" +#include "GameModeCommand.h" + +EGameCommand GameModeCommand::getId() +{ + return eGameCommand_GameMode; +} + +void GameModeCommand::execute(shared_ptr source, byteArray commandData) +{ + //if (args.length > 0) + //{ + // GameType newMode = getModeForString(source, args[0]); + // Player player = args.length >= 2 ? getPlayer(args[1]) : convertSourceToPlayer(source); + + // player.setGameMode(newMode); + + // String mode = I18n.get("gameMode." + newMode.getName()); + + // if (player != source) { + // logAdminAction(source, AdminLogCommand.LOGTYPE_DONT_SHOW_TO_SELF, "commands.gamemode.success.other", player.getAName(), mode); + // } else { + // logAdminAction(source, AdminLogCommand.LOGTYPE_DONT_SHOW_TO_SELF, "commands.gamemode.success.self", mode); + // } + //} +} + +GameType *GameModeCommand::getModeForString(shared_ptr source, const wstring &name) +{ + return NULL; + //if (name.equalsIgnoreCase(GameType.SURVIVAL.getName()) || name.equalsIgnoreCase("s")) { + // return GameType.SURVIVAL; + //} else if (name.equalsIgnoreCase(GameType.CREATIVE.getName()) || name.equalsIgnoreCase("c")) { + // return GameType.CREATIVE; + //} else if (name.equalsIgnoreCase(GameType.ADVENTURE.getName()) || name.equalsIgnoreCase("a")) { + // return GameType.ADVENTURE; + //} else { + // return LevelSettings.validateGameType(convertArgToInt(source, name, 0, GameType.values().length - 2)); + //} +} + +shared_ptr GameModeCommand::getPlayer(PlayerUID playerId) +{ + return nullptr; + //Player player = MinecraftServer.getInstance().getPlayers().getPlayer(name); + + //if (player == null) { + // throw new PlayerNotFoundException(); + //} else { + // return player; + //} +} \ No newline at end of file diff --git a/Minecraft.World/GameModeCommand.h b/Minecraft.World/GameModeCommand.h new file mode 100644 index 00000000..66401587 --- /dev/null +++ b/Minecraft.World/GameModeCommand.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Command.h" + +class GameType; + +class GameModeCommand : public Command +{ +public: + virtual EGameCommand getId(); + virtual void execute(shared_ptr source, byteArray commandData); + +protected: + GameType *getModeForString(shared_ptr source, const wstring &name); + shared_ptr getPlayer(PlayerUID playerId); +}; \ No newline at end of file diff --git a/Minecraft.World/GasMaterial.h b/Minecraft.World/GasMaterial.h new file mode 100644 index 00000000..1ad23a53 --- /dev/null +++ b/Minecraft.World/GasMaterial.h @@ -0,0 +1,12 @@ +#pragma once +#include "Material.h" + +class GasMaterial : public Material +{ +public: + GasMaterial(MaterialColor *color) : Material(color) { replaceable(); } + + virtual bool isSolid() { return false; } + virtual bool blocksLight() { return false; } + virtual bool blocksMotion() { return false; } +}; \ No newline at end of file diff --git a/Minecraft.World/GeneralStat.cpp b/Minecraft.World/GeneralStat.cpp new file mode 100644 index 00000000..3a6ea1af --- /dev/null +++ b/Minecraft.World/GeneralStat.cpp @@ -0,0 +1,18 @@ +#include "stdafx.h" +#include "net.minecraft.stats.h" +#include "GeneralStat.h" + +GeneralStat::GeneralStat(int id, const wstring& name, StatFormatter *formatter) : Stat(id, name, formatter) +{ +} + +GeneralStat::GeneralStat(int id, const wstring& name) : Stat(id, name) +{ +} + +Stat *GeneralStat::postConstruct() +{ + Stat::postConstruct(); + Stats::generalStats->push_back(this); + return this; +} \ No newline at end of file diff --git a/Minecraft.World/GeneralStat.h b/Minecraft.World/GeneralStat.h new file mode 100644 index 00000000..27fe1b54 --- /dev/null +++ b/Minecraft.World/GeneralStat.h @@ -0,0 +1,12 @@ +#pragma once +using namespace std; + +#include "Stat.h" + +class GeneralStat : public Stat +{ +public: + GeneralStat(int id, const wstring& name, StatFormatter *formatter); + GeneralStat(int id, const wstring& name); + Stat *postConstruct(); +}; diff --git a/Minecraft.World/GenericStats.cpp b/Minecraft.World/GenericStats.cpp new file mode 100644 index 00000000..1fa90151 --- /dev/null +++ b/Minecraft.World/GenericStats.cpp @@ -0,0 +1,1284 @@ +#include "stdafx.h" + +#include "Monster.h" + +#include "GenericStats.h" + +GenericStats *GenericStats::instance = NULL; + +Stat* GenericStats::get_walkOneM() +{ + return NULL; +} + +Stat* GenericStats::get_swimOneM() +{ + return NULL; +} + +Stat* GenericStats::get_fallOneM() +{ + return NULL; +} + +Stat* GenericStats::get_climbOneM() +{ + return NULL; +} + +Stat* GenericStats::get_minecartOneM() +{ + return NULL; +} + +Stat* GenericStats::get_boatOneM() +{ + return NULL; +} + +Stat* GenericStats::get_pigOneM() +{ + return NULL; +} + +Stat* GenericStats::get_portalsCreated() +{ + return NULL; +} + +Stat* GenericStats::get_cowsMilked() +{ + return NULL; +} + +Stat* GenericStats::get_netherLavaCollected() +{ + return NULL; +} + +Stat* GenericStats::get_killMob() +{ + return NULL; +} + +Stat* GenericStats::get_killsZombie() +{ + return NULL; +} + +Stat* GenericStats::get_killsSkeleton() +{ + return NULL; +} + +Stat* GenericStats::get_killsCreeper() +{ + return NULL; +} + +Stat* GenericStats::get_killsSpider() +{ + return NULL; +} + +Stat* GenericStats::get_killsSpiderJockey() +{ + return NULL; +} + +Stat* GenericStats::get_killsZombiePigman() +{ + return NULL; +} + +Stat* GenericStats::get_killsSlime() +{ + return NULL; +} + +Stat* GenericStats::get_killsGhast() +{ + return NULL; +} + +Stat* GenericStats::get_killsNetherZombiePigman() +{ + return NULL; +} + +Stat* GenericStats::get_breedEntity(eINSTANCEOF entityId) +{ + return NULL; +} + +Stat* GenericStats::get_tamedEntity(eINSTANCEOF entityId) +{ + return NULL; +} + +Stat* GenericStats::get_curedEntity(eINSTANCEOF entityId) +{ + return NULL; +} + +Stat* GenericStats::get_craftedEntity(eINSTANCEOF entityId) +{ + return NULL; +} + +Stat* GenericStats::get_shearedEntity(eINSTANCEOF entityId) +{ + return NULL; +} + +Stat* GenericStats::get_totalBlocksMined() +{ + return NULL; +} + +Stat* GenericStats::get_timePlayed() +{ + return NULL; +} + +Stat* GenericStats::get_blocksPlaced(int blockId) +{ + return NULL; +} + +Stat* GenericStats::get_blocksMined(int blockId) +{ + return NULL; +} + +Stat* GenericStats::get_itemsCollected(int itemId, int itemAux) +{ + return NULL; +} + +Stat* GenericStats::get_itemsCrafted(int itemId) +{ + return NULL; +} + +Stat* GenericStats::get_itemsSmelted(int itemId) +{ + return this->get_itemsCrafted(itemId); +} + +Stat* GenericStats::get_itemsUsed(int itemId) +{ + return NULL; +} + +Stat *GenericStats::get_itemsBought(int itemId) +{ + return NULL; +} + +Stat* GenericStats::get_killsEnderdragon() +{ + return NULL; +} + +Stat* GenericStats::get_completeTheEnd() +{ + return NULL; +} + +Stat* GenericStats::get_changedDimension(int from, int to) +{ + return NULL; +} + +Stat* GenericStats::get_enteredBiome(int biomeId) +{ + return NULL; +} + +Stat* GenericStats::get_achievement(eAward achievementId) +{ + return NULL; +} + +Stat* GenericStats::openInventory() +{ + return instance->get_achievement( eAward_TakingInventory ); +} + +Stat* GenericStats::mineWood() +{ + return instance->get_achievement( eAward_GettingWood ); +} + +Stat* GenericStats::buildWorkbench() +{ + return instance->get_achievement( eAward_Benchmarking ); +} + +Stat* GenericStats::buildPickaxe() +{ + return instance->get_achievement( eAward_TimeToMine); +} + +Stat* GenericStats::buildFurnace() +{ + return instance->get_achievement( eAward_HotTopic ); +} + +Stat* GenericStats::acquireIron() +{ + return instance->get_achievement( eAward_AquireHardware ); +} + +Stat* GenericStats::buildHoe() +{ + return instance->get_achievement( eAward_TimeToFarm ); +} + +Stat* GenericStats::makeBread() +{ + return instance->get_achievement( eAward_BakeBread ); +} + +Stat* GenericStats::bakeCake() +{ + return instance->get_achievement( eAward_TheLie ); +} + +Stat* GenericStats::buildBetterPickaxe() +{ + return instance->get_achievement( eAward_GettingAnUpgrade ); +} + +Stat* GenericStats::cookFish() +{ + return instance->get_achievement( eAward_DeliciousFish ); +} + +Stat* GenericStats::onARail() +{ + return instance->get_achievement( eAward_OnARail ); +} + +Stat* GenericStats::buildSword() +{ + return instance->get_achievement( eAward_TimeToStrike ); +} + +Stat* GenericStats::killEnemy() +{ + return instance->get_achievement( eAward_MonsterHunter ); +} + +Stat* GenericStats::killCow() +{ + return instance->get_achievement( eAward_CowTipper ); +} + +Stat* GenericStats::flyPig() +{ + return instance->get_achievement( eAward_WhenPigsFly ); +} + +Stat* GenericStats::snipeSkeleton() +{ +#ifndef _XBOX + return instance->get_achievement( eAward_snipeSkeleton ); +#else + return NULL; +#endif +} + +Stat* GenericStats::diamonds() +{ +#ifndef _XBOX + return instance->get_achievement( eAward_diamonds ); +#else + return NULL; +#endif +} + +Stat* GenericStats::ghast() +{ +#ifndef _XBOX + return instance->get_achievement( eAward_ghast ); +#else + return NULL; +#endif +} + +Stat* GenericStats::blazeRod() +{ +#ifndef _XBOX + return instance->get_achievement( eAward_blazeRod ); +#else + return NULL; +#endif +} + +Stat* GenericStats::potion() +{ +#ifndef _XBOX + return instance->get_achievement( eAward_potion ); +#else + return NULL; +#endif +} + +Stat* GenericStats::theEnd() +{ +#ifndef _XBOX + return instance->get_achievement( eAward_theEnd ); +#else + return NULL; +#endif +} + +Stat* GenericStats::winGame() +{ +#ifndef _XBOX + return instance->get_achievement( eAward_winGame ); +#else + return NULL; +#endif +} + +Stat* GenericStats::enchantments() +{ +#ifndef _XBOX + return instance->get_achievement( eAward_enchantments ); +#else + return NULL; +#endif +} + +Stat* GenericStats::overkill() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement( eAward_overkill ); +#else + return NULL; +#endif +} + +Stat* GenericStats::bookcase() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement( eAward_bookcase ); +#else + return NULL; +#endif +} + +Stat* GenericStats::leaderOfThePack() +{ + return instance->get_achievement( eAward_LeaderOfThePack ); +} + +Stat* GenericStats::MOARTools() +{ + return instance->get_achievement( eAward_MOARTools ); +} + +Stat* GenericStats::dispenseWithThis() +{ + return instance->get_achievement( eAward_DispenseWithThis ); +} + +Stat* GenericStats::InToTheNether() +{ + return instance->get_achievement( eAward_InToTheNether ); +} + +Stat* GenericStats::socialPost() +{ + return instance->get_achievement( eAward_socialPost ); +} + +Stat* GenericStats::eatPorkChop() +{ + return instance->get_achievement( eAward_eatPorkChop ); +} + +Stat* GenericStats::play100Days() +{ + return instance->get_achievement( eAward_play100Days ); +} + +Stat* GenericStats::arrowKillCreeper() +{ + return instance->get_achievement( eAward_arrowKillCreeper ); +} + +Stat* GenericStats::mine100Blocks() +{ + return instance->get_achievement( eAward_mine100Blocks ); +} + +Stat* GenericStats::kill10Creepers() +{ + return instance->get_achievement( eAward_kill10Creepers ); +} + +Stat* GenericStats::adventuringTime() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_adventuringTime); +#else + return NULL; +#endif +} + +Stat* GenericStats::repopulation() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_repopulation); +#else + return NULL; +#endif +} + +Stat* GenericStats::porkChop() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_eatPorkChop); +#else + return NULL; +#endif +} + +Stat* GenericStats::diamondsToYou() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_diamondsToYou); +#else + return NULL; +#endif +} + +Stat* GenericStats::passingTheTime() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_play100Days); +#else + return NULL; +#endif +} + +Stat* GenericStats::archer() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_arrowKillCreeper); +#else + return NULL; +#endif +} + +Stat* GenericStats::theHaggler() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_theHaggler); +#else + return NULL; +#endif +} + +Stat* GenericStats::potPlanter() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_potPlanter); +#else + return NULL; +#endif +} + +Stat* GenericStats::itsASign() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_itsASign); +#else + return NULL; +#endif +} + +Stat* GenericStats::ironBelly() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_ironBelly); +#else + return NULL; +#endif +} + +Stat* GenericStats::haveAShearfulDay() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_haveAShearfulDay); +#else + return NULL; +#endif +} + +Stat* GenericStats::rainbowCollection() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_rainbowCollection); +#else + return NULL; +#endif +} + +Stat* GenericStats::stayinFrosty() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_stayinFrosty); +#else + return NULL; +#endif +} + +Stat* GenericStats::chestfulOfCobblestone() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_chestfulOfCobblestone); +#else + return NULL; +#endif +} + +Stat* GenericStats::renewableEnergy() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_renewableEnergy); +#else + return NULL; +#endif +} + +Stat* GenericStats::musicToMyEars() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_musicToMyEars); +#else + return NULL; +#endif +} + +Stat* GenericStats::bodyGuard() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_bodyGuard); +#else + return NULL; +#endif +} + +Stat* GenericStats::ironMan() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_ironMan); +#else + return NULL; +#endif +} + +Stat* GenericStats::zombieDoctor() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_zombieDoctor); +#else + return NULL; +#endif +} + +Stat* GenericStats::lionTamer() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->get_achievement(eAward_lionTamer); +#else + return NULL; +#endif +} + +byteArray GenericStats::getParam_walkOneM(int distance) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_swimOneM(int distance) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_fallOneM(int distance) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_climbOneM(int distance) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_minecartOneM(int distance) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_boatOneM(int distance) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_pigOneM(int distance) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_cowsMilked() +{ + return getParam_noArgs(); +} + +byteArray GenericStats::getParam_blocksPlaced(int id, int data, int count) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_blocksMined(int id, int data, int count) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_itemsCollected(int id, int aux, int count) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_itemsCrafted(int id, int aux, int count) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_itemsSmelted(int id, int aux, int count) +{ + return this->getParam_itemsCrafted(id,aux,count); +} + +byteArray GenericStats::getParam_itemsUsed(shared_ptr plr, shared_ptr itm) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_itemsBought(int id, int aux, int count) +{ + return getParam_noArgs(); +} + +byteArray GenericStats::getParam_mobKill(shared_ptr plr, shared_ptr mob, DamageSource *dmgSrc) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_breedEntity(eINSTANCEOF entityId) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_tamedEntity(eINSTANCEOF entityId) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_curedEntity(eINSTANCEOF entityId) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_craftedEntity(eINSTANCEOF entityId) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_shearedEntity(eINSTANCEOF entityId) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_time(int timediff) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_changedDimension(int from, int to) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_enteredBiome(int biomeId) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_achievement(eAward id) +{ + return getParam_noArgs(); // Really just a count on most platforms. +} + +byteArray GenericStats::getParam_onARail(int distance) +{ + return getParam_achievement(eAward_OnARail); +} + +byteArray GenericStats::getParam_overkill(int damage) +{ +#ifdef _XBOX + return getParam_noArgs(); +#else + return getParam_achievement(eAward_overkill); +#endif +} + +byteArray GenericStats::getParam_openInventory(int menuId) +{ + return getParam_achievement(eAward_TakingInventory); +} + +byteArray GenericStats::getParam_chestfulOfCobblestone(int cobbleStone) +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return getParam_achievement(eAward_chestfulOfCobblestone); +#else + return getParam_noArgs(); +#endif +} + +byteArray GenericStats::getParam_musicToMyEars(int recordId) +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return getParam_achievement(eAward_musicToMyEars); +#else + return getParam_noArgs(); +#endif +} + +byteArray GenericStats::getParam_noArgs() +{ + return byteArray(); // TODO +} + +byteArray GenericStats::param_walk(int distance) +{ + return instance->getParam_walkOneM(distance); +} + +byteArray GenericStats::param_swim(int distance) +{ + return instance->getParam_swimOneM(distance); +} + +byteArray GenericStats::param_fall(int distance) +{ + return instance->getParam_fallOneM(distance); +} + +byteArray GenericStats::param_climb(int distance) +{ + return instance->getParam_climbOneM(distance); +} + +byteArray GenericStats::param_minecart(int distance) +{ + return instance->getParam_minecartOneM(distance); +} + +byteArray GenericStats::param_boat(int distance) +{ + return instance->getParam_boatOneM(distance); +} + +byteArray GenericStats::param_pig(int distance) +{ + return instance->getParam_pigOneM(distance); +} + +byteArray GenericStats::param_cowsMilked() +{ + return instance->getParam_cowsMilked(); +} + +byteArray GenericStats::param_blocksPlaced(int id, int data, int count) +{ + return instance->getParam_blocksPlaced(id,data,count); +} + +byteArray GenericStats::param_blocksMined(int id, int data, int count) +{ + return instance->getParam_blocksMined(id,data,count); +} + +byteArray GenericStats::param_itemsCollected(int id, int aux, int count) +{ + return instance->getParam_itemsCollected(id,aux,count); +} + +byteArray GenericStats::param_itemsCrafted(int id, int aux, int count) +{ + return instance->getParam_itemsCrafted(id,aux,count); +} + +byteArray GenericStats::param_itemsSmelted(int id, int aux, int count) +{ + return instance->getParam_itemsSmelted(id,aux,count); +} + +byteArray GenericStats::param_itemsUsed(shared_ptr plr, shared_ptr itm) +{ + if ( (plr != NULL) && (itm != NULL) ) return instance->getParam_itemsUsed(plr, itm); + else return instance->getParam_noArgs(); +} + +byteArray GenericStats::param_itemsBought(int id, int aux, int count) +{ + return instance->getParam_itemsBought(id,aux,count); +} + +byteArray GenericStats::param_mobKill(shared_ptr plr, shared_ptr mob, DamageSource *dmgSrc) +{ + if ( (plr != NULL) && (mob != NULL) ) return instance->getParam_mobKill(plr, mob, dmgSrc); + else return instance->getParam_noArgs(); +} + +byteArray GenericStats::param_breedEntity(eINSTANCEOF entityId) +{ + return instance->getParam_breedEntity(entityId); +} + +byteArray GenericStats::param_tamedEntity(eINSTANCEOF entityId) +{ + return instance->getParam_tamedEntity(entityId); +} + +byteArray GenericStats::param_curedEntity(eINSTANCEOF entityId) +{ + return instance->getParam_curedEntity(entityId); +} + +byteArray GenericStats::param_craftedEntity(eINSTANCEOF entityId) +{ + return instance->getParam_craftedEntity(entityId); +} + +byteArray GenericStats::param_shearedEntity(eINSTANCEOF entityId) +{ + return instance->getParam_shearedEntity(entityId); +} + +byteArray GenericStats::param_time(int timediff) +{ + return instance->getParam_time(timediff); +} + +byteArray GenericStats::param_changedDimension(int from, int to) +{ + return instance->getParam_changedDimension(from,to); +} + +byteArray GenericStats::param_enteredBiome(int biomeId) +{ + return instance->getParam_enteredBiome(biomeId); +} + +byteArray GenericStats::param_noArgs() +{ + return instance->getParam_noArgs(); +} + +byteArray GenericStats::param_openInventory() +{ + return instance->getParam_achievement(eAward_TakingInventory); +} + +byteArray GenericStats::param_mineWood() +{ + return instance->getParam_achievement(eAward_GettingWood); +} + +byteArray GenericStats::param_buildWorkbench() +{ + return instance->getParam_achievement(eAward_Benchmarking); +} + +byteArray GenericStats::param_buildPickaxe() +{ + return instance->getParam_achievement(eAward_TimeToMine); +} + +byteArray GenericStats::param_buildFurnace() +{ + return instance->getParam_achievement(eAward_HotTopic); +} + +byteArray GenericStats::param_acquireIron() +{ + return instance->getParam_achievement(eAward_AquireHardware); +} + +byteArray GenericStats::param_buildHoe() +{ + return instance->getParam_achievement(eAward_TimeToFarm); +} + +byteArray GenericStats::param_makeBread() +{ + return instance->getParam_achievement(eAward_BakeBread); +} + +byteArray GenericStats::param_bakeCake() +{ + return instance->getParam_achievement(eAward_TheLie); +} + +byteArray GenericStats::param_buildBetterPickaxe() +{ + return instance->getParam_achievement(eAward_GettingAnUpgrade); +} + +byteArray GenericStats::param_cookFish() +{ + return instance->getParam_achievement(eAward_DeliciousFish); +} + +byteArray GenericStats::param_onARail(int distance) +{ + return instance->getParam_onARail(distance); +} + +byteArray GenericStats::param_buildSword() +{ + return instance->getParam_achievement(eAward_TimeToStrike); +} + +byteArray GenericStats::param_killEnemy() +{ + return instance->getParam_achievement(eAward_MonsterHunter); +} + +byteArray GenericStats::param_killCow() +{ + return instance->getParam_achievement(eAward_CowTipper); +} + +byteArray GenericStats::param_flyPig() +{ + return instance->getParam_achievement(eAward_WhenPigsFly); +} + +byteArray GenericStats::param_snipeSkeleton() +{ +#ifdef _XBOX + return instance->getParam_noArgs(); +#else + return instance->getParam_achievement(eAward_snipeSkeleton); +#endif +} + +byteArray GenericStats::param_diamonds() +{ +#ifdef _XBOX + return instance->getParam_noArgs(); +#else + return instance->getParam_achievement(eAward_diamonds); +#endif +} + +byteArray GenericStats::param_ghast() +{ +#ifdef _XBOX + return instance->getParam_noArgs(); +#else + return instance->getParam_achievement(eAward_ghast); +#endif +} + +byteArray GenericStats::param_blazeRod() +{ +#ifdef _XBOX + return instance->getParam_noArgs(); +#else + return instance->getParam_achievement(eAward_blazeRod); +#endif +} + +byteArray GenericStats::param_potion() +{ +#ifdef _XBOX + return instance->getParam_noArgs(); +#else + return instance->getParam_achievement(eAward_potion); +#endif +} + +byteArray GenericStats::param_theEnd() +{ +#ifdef _XBOX + return instance->getParam_noArgs(); +#else + return instance->getParam_achievement(eAward_theEnd); +#endif +} + +byteArray GenericStats::param_winGame() +{ +#ifdef _XBOX + return instance->getParam_noArgs(); +#else + return instance->getParam_achievement(eAward_winGame); +#endif +} + +byteArray GenericStats::param_enchantments() +{ +#ifdef _XBOX + return instance->getParam_noArgs(); +#else + return instance->getParam_achievement(eAward_enchantments); +#endif +} + +byteArray GenericStats::param_overkill(int dmg) +{ + return instance->getParam_overkill(dmg); +} + +byteArray GenericStats::param_bookcase() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_bookcase); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_leaderOfThePack() +{ + return instance->getParam_achievement(eAward_LeaderOfThePack); +} + +byteArray GenericStats::param_MOARTools() +{ + return instance->getParam_achievement(eAward_MOARTools); +} + +byteArray GenericStats::param_dispenseWithThis() +{ + return instance->getParam_achievement(eAward_DispenseWithThis); +} + +byteArray GenericStats::param_InToTheNether() +{ + return instance->getParam_achievement(eAward_InToTheNether); +} + +byteArray GenericStats::param_socialPost() +{ + return instance->getParam_achievement(eAward_socialPost); +} + +byteArray GenericStats::param_eatPorkChop() +{ + return instance->getParam_achievement(eAward_eatPorkChop); +} + +byteArray GenericStats::param_play100Days() +{ + return instance->getParam_achievement(eAward_play100Days); +} + +byteArray GenericStats::param_arrowKillCreeper() +{ + return instance->getParam_achievement(eAward_arrowKillCreeper); +} + +byteArray GenericStats::param_mine100Blocks() +{ + return instance->getParam_achievement(eAward_mine100Blocks); +} + +byteArray GenericStats::param_kill10Creepers() +{ + return instance->getParam_achievement(eAward_kill10Creepers); +} + +byteArray GenericStats::param_adventuringTime() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_adventuringTime); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_repopulation() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_repopulation); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_porkChop() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_eatPorkChop); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_diamondsToYou() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_diamondsToYou); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_passingTheTime() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_play100Days); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_archer() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_arrowKillCreeper); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_theHaggler() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_theHaggler); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_potPlanter() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_potPlanter); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_itsASign() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_itsASign); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_ironBelly() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_ironBelly); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_haveAShearfulDay() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_haveAShearfulDay); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_rainbowCollection() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_rainbowCollection); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_stayinFrosty() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_stayinFrosty); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_chestfulOfCobblestone(int cobbleStone) +{ + return instance->getParam_chestfulOfCobblestone(cobbleStone); +} + +byteArray GenericStats::param_renewableEnergy() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_renewableEnergy); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_musicToMyEars(int recordId) +{ + return instance->getParam_musicToMyEars(recordId); +} + +byteArray GenericStats::param_bodyGuard() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_bodyGuard); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_ironMan() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_ironMan); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_zombieDoctor() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_zombieDoctor); +#else + return instance->getParam_noArgs(); +#endif +} + +byteArray GenericStats::param_lionTamer() +{ +#ifdef _EXTENDED_ACHIEVEMENTS + return instance->getParam_achievement(eAward_lionTamer); +#else + return instance->getParam_noArgs(); +#endif +} diff --git a/Minecraft.World/GenericStats.h b/Minecraft.World/GenericStats.h new file mode 100644 index 00000000..541424db --- /dev/null +++ b/Minecraft.World/GenericStats.h @@ -0,0 +1,348 @@ +#pragma once + +#include "Stat.h" +#include "Stats.h" + +//#include "DamageSource.h" + +//#include "..\Minecraft.Client\Common\Console_Awards_enum.h" + +/** + 4J-JEV: + Java version exposed the static instance of each stat. + + This was inconvient for me as I needed to structure the stats/achievements differently + on Durango. + + Using getters like this means we can use different Stats easilly on different + platforms and still have a convenient identifier to use to award them. +*/ +class GenericStats +{ +private: // Static instance. + static GenericStats *instance; + +public: + static void setInstance(GenericStats *newInstance) { instance = newInstance; } + static GenericStats *getInstance() { return instance; } + + // For retrieving a stat from an id. + virtual Stat *get_stat(int i) = 0; + static Stat *stat(int i) { return instance->get_stat(i); } + + // STATS - STATIC // + + static Stat* walkOneM() { return instance->get_walkOneM(); } + static Stat* swimOneM() { return instance->get_swimOneM(); } + static Stat* fallOneM() { return instance->get_fallOneM(); } + static Stat* climbOneM() { return instance->get_climbOneM(); } + static Stat* minecartOneM() { return instance->get_minecartOneM(); } + static Stat* boatOneM() { return instance->get_boatOneM(); } + static Stat* pigOneM() { return instance->get_pigOneM(); } + static Stat* portalsCreated() { return instance->get_portalsCreated(); } + static Stat* cowsMilked() { return instance->get_cowsMilked(); } + static Stat* netherLavaCollected() { return instance->get_netherLavaCollected(); } + + static Stat* killMob() { return instance->get_killMob(); } + + static Stat* killsZombie() { return instance->get_killsZombie(); } + static Stat* killsSkeleton() { return instance->get_killsSkeleton(); } + static Stat* killsCreeper() { return instance->get_killsCreeper(); } + static Stat* killsSpider() { return instance->get_killsSpider(); } + static Stat* killsSpiderJockey() { return instance->get_killsSpiderJockey(); } + static Stat* killsZombiePigman() { return instance->get_killsZombiePigman(); } + static Stat* killsSlime() { return instance->get_killsSlime(); } + static Stat* killsGhast() { return instance->get_killsGhast(); } + static Stat* killsNetherZombiePigman() { return instance->get_killsNetherZombiePigman(); } + + static Stat* breedEntity(eINSTANCEOF entityId) { return instance->get_breedEntity(entityId); } + static Stat* tamedEntity(eINSTANCEOF entityId) { return instance->get_tamedEntity(entityId); } + static Stat* curedEntity(eINSTANCEOF entityId) { return instance->get_curedEntity(entityId); } + static Stat* craftedEntity(eINSTANCEOF entityId) { return instance->get_craftedEntity(entityId); } + static Stat* shearedEntity(eINSTANCEOF entityId) { return instance->get_shearedEntity(entityId); } + + static Stat* totalBlocksMined() { return instance->get_totalBlocksMined(); } + static Stat* timePlayed() { return instance->get_timePlayed(); } + + static Stat* blocksPlaced(int blockId) { return instance->get_blocksPlaced(blockId); } + static Stat* blocksMined(int blockId) { return instance->get_blocksMined(blockId); } + static Stat* itemsCollected(int itemId, int itemAux) { return instance->get_itemsCollected(itemId, itemAux); } + static Stat* itemsCrafted(int itemId) { return instance->get_itemsCrafted(itemId); } + static Stat* itemsSmelted(int itemId) { return instance->get_itemsSmelted(itemId); } // 4J-JEV: Diffentiation needed, when only one type of event should be sent (eg iron smelting). + static Stat* itemsUsed(int itemId) { return instance->get_itemsUsed(itemId); } + static Stat* itemsBought(int itemId) { return instance->get_itemsBought(itemId); } + + static Stat* killsEnderdragon() { return instance->get_killsEnderdragon(); } + static Stat* completeTheEnd() { return instance->get_completeTheEnd(); } + + static Stat* changedDimension(int from, int to) { return instance->get_changedDimension(from,to); } + static Stat* enteredBiome(int biomeId) { return instance->get_enteredBiome(biomeId); } + + // ACHIEVEMENTS - STATIC // + + static Stat* achievement(eAward achievementId) { return instance->get_achievement(achievementId); } + + static Stat* openInventory(); + static Stat* mineWood(); + static Stat* buildWorkbench(); + static Stat* buildPickaxe(); + static Stat* buildFurnace(); + static Stat* acquireIron(); + static Stat* buildHoe(); + static Stat* makeBread(); + static Stat* bakeCake(); + static Stat* buildBetterPickaxe(); + static Stat* cookFish(); + static Stat* onARail(); + static Stat* buildSword(); + static Stat* killEnemy(); + static Stat* killCow(); + static Stat* flyPig(); + static Stat* snipeSkeleton(); + static Stat* diamonds(); + static Stat* ghast(); + static Stat* blazeRod(); + static Stat* potion(); + static Stat* theEnd(); + static Stat* winGame(); + static Stat* enchantments(); + static Stat* overkill(); + static Stat* bookcase(); + + static Stat* leaderOfThePack(); + static Stat* MOARTools(); + static Stat* dispenseWithThis(); + static Stat* InToTheNether(); + + static Stat* socialPost(); + static Stat* eatPorkChop(); + static Stat* play100Days(); + static Stat* arrowKillCreeper(); + static Stat* mine100Blocks(); + static Stat* kill10Creepers(); + + static Stat* adventuringTime(); // Requires new Stat + static Stat* repopulation(); + static Stat* diamondsToYou(); // +Durango + static Stat* porkChop(); // Req Stat? + static Stat* passingTheTime(); // Req Stat + static Stat* archer(); + static Stat* theHaggler(); // Req Stat + static Stat* potPlanter(); // Req Stat + static Stat* itsASign(); // Req Stat + static Stat* ironBelly(); + static Stat* haveAShearfulDay(); + static Stat* rainbowCollection(); // Requires new Stat + static Stat* stayinFrosty(); // +Durango + static Stat* chestfulOfCobblestone(); // +Durango + static Stat* renewableEnergy(); // +Durango + static Stat* musicToMyEars(); // +Durango + static Stat* bodyGuard(); + static Stat* ironMan(); // +Durango + static Stat* zombieDoctor(); // +Durango + static Stat* lionTamer(); + + // STAT PARAMS - STATIC // + + static byteArray param_walk(int distance); + static byteArray param_swim(int distance); + static byteArray param_fall(int distance); + static byteArray param_climb(int distance); + static byteArray param_minecart(int distance); + static byteArray param_boat(int distance); + static byteArray param_pig(int distance); + + static byteArray param_cowsMilked(); + + static byteArray param_blocksPlaced(int id, int data, int count); + static byteArray param_blocksMined(int id, int data, int count); + static byteArray param_itemsCollected(int id, int aux, int count); + static byteArray param_itemsCrafted(int id, int aux, int count); + static byteArray param_itemsSmelted(int id, int aux, int cound); + static byteArray param_itemsUsed(shared_ptr plr, shared_ptr itm); + static byteArray param_itemsBought(int id, int aux, int count); + + static byteArray param_mobKill(shared_ptr plr, shared_ptr mob, DamageSource *dmgSrc); + + static byteArray param_breedEntity(eINSTANCEOF mobType); + static byteArray param_tamedEntity(eINSTANCEOF mobType); + static byteArray param_curedEntity(eINSTANCEOF mobType); + static byteArray param_craftedEntity(eINSTANCEOF mobType); + static byteArray param_shearedEntity(eINSTANCEOF mobType); + + static byteArray param_time(int timediff); + + static byteArray param_changedDimension(int from, int to); + static byteArray param_enteredBiome(int biomeId); + + //static byteArray param_achievement(eAward id); + + //static byteArray param_ach_onARail(); + //static byteArray param_overkill(int damage); //TODO + //static byteArray param_openInventory(int menuId); + //static byteArray param_chestfulOfCobblestone(); + //static byteArray param_musicToMyEars(int recordId); + + static byteArray param_noArgs(); + + // STATIC + VIRTUAL - ACHIEVEMENT - PARAMS // + + static byteArray param_openInventory(); + static byteArray param_mineWood(); + static byteArray param_buildWorkbench(); + static byteArray param_buildPickaxe(); + static byteArray param_buildFurnace(); + static byteArray param_acquireIron(); + static byteArray param_buildHoe(); + static byteArray param_makeBread(); + static byteArray param_bakeCake(); + static byteArray param_buildBetterPickaxe(); + static byteArray param_cookFish(); + static byteArray param_onARail(int distance); + static byteArray param_buildSword(); + static byteArray param_killEnemy(); + static byteArray param_killCow(); + static byteArray param_flyPig(); + static byteArray param_snipeSkeleton(); + static byteArray param_diamonds(); + static byteArray param_ghast(); + static byteArray param_blazeRod(); + static byteArray param_potion(); + static byteArray param_theEnd(); + static byteArray param_winGame(); + static byteArray param_enchantments(); + static byteArray param_overkill(int dmg); + static byteArray param_bookcase(); + + static byteArray param_leaderOfThePack(); + static byteArray param_MOARTools(); + static byteArray param_dispenseWithThis(); + static byteArray param_InToTheNether(); + + static byteArray param_socialPost(); + static byteArray param_eatPorkChop(); + static byteArray param_play100Days(); + static byteArray param_arrowKillCreeper(); + static byteArray param_mine100Blocks(); + static byteArray param_kill10Creepers(); + + static byteArray param_adventuringTime(); + static byteArray param_repopulation(); + static byteArray param_porkChop(); + static byteArray param_diamondsToYou(); + static byteArray param_passingTheTime(); + static byteArray param_archer(); + static byteArray param_theHaggler(); + static byteArray param_potPlanter(); + static byteArray param_itsASign(); + static byteArray param_ironBelly(); + static byteArray param_haveAShearfulDay(); + static byteArray param_rainbowCollection(); + static byteArray param_stayinFrosty(); + static byteArray param_chestfulOfCobblestone(int cobbleStone); + static byteArray param_renewableEnergy(); + static byteArray param_musicToMyEars(int recordId); + static byteArray param_bodyGuard(); + static byteArray param_ironMan(); + static byteArray param_zombieDoctor(); + static byteArray param_lionTamer(); + +protected: + // ACHIEVEMENTS - VIRTUAL // + + virtual Stat* get_achievement(eAward achievementId); + + + // STATS - VIRTUAL // + + virtual Stat* get_walkOneM(); + virtual Stat* get_swimOneM(); + virtual Stat* get_fallOneM(); + virtual Stat* get_climbOneM(); + virtual Stat* get_minecartOneM(); + virtual Stat* get_boatOneM(); + virtual Stat* get_pigOneM(); + virtual Stat* get_portalsCreated(); + virtual Stat* get_cowsMilked(); + virtual Stat* get_netherLavaCollected(); + + virtual Stat* get_killMob(); + + virtual Stat* get_killsZombie(); + virtual Stat* get_killsSkeleton(); + virtual Stat* get_killsCreeper(); + virtual Stat* get_killsSpider(); + virtual Stat* get_killsSpiderJockey(); + virtual Stat* get_killsZombiePigman(); + virtual Stat* get_killsSlime(); + virtual Stat* get_killsGhast(); + virtual Stat* get_killsNetherZombiePigman(); + + virtual Stat* get_breedEntity(eINSTANCEOF entityId); + virtual Stat* get_tamedEntity(eINSTANCEOF entityId); + virtual Stat* get_curedEntity(eINSTANCEOF entityId); + virtual Stat* get_craftedEntity(eINSTANCEOF entityId); + virtual Stat* get_shearedEntity(eINSTANCEOF entityId); + + virtual Stat* get_totalBlocksMined(); + virtual Stat* get_timePlayed(); + + virtual Stat* get_blocksPlaced(int blockId); + virtual Stat* get_blocksMined(int blockId); + virtual Stat* get_itemsCollected(int itemId, int itemAux); + virtual Stat* get_itemsCrafted(int itemId); + virtual Stat* get_itemsSmelted(int itemId); + virtual Stat* get_itemsUsed(int itemId); + virtual Stat* get_itemsBought(int itemId); + + virtual Stat* get_killsEnderdragon(); + virtual Stat* get_completeTheEnd(); + + virtual Stat* get_changedDimension(int from, int to); + virtual Stat* get_enteredBiome(int biomeId); + + // STAT PARAMS - VIRTUAL // + + virtual byteArray getParam_walkOneM(int distance); + virtual byteArray getParam_swimOneM(int distance); + virtual byteArray getParam_fallOneM(int distance); + virtual byteArray getParam_climbOneM(int distance); + virtual byteArray getParam_minecartOneM(int distance); + virtual byteArray getParam_boatOneM(int distance); + virtual byteArray getParam_pigOneM(int distance); + + virtual byteArray getParam_cowsMilked(); + + virtual byteArray getParam_blocksPlaced(int id, int data, int count); + virtual byteArray getParam_blocksMined(int id, int data, int count); + virtual byteArray getParam_itemsCollected(int id, int aux, int count); + virtual byteArray getParam_itemsCrafted(int id, int aux, int count); + virtual byteArray getParam_itemsSmelted(int id, int aux, int count); + virtual byteArray getParam_itemsUsed(shared_ptr plr, shared_ptr itm); + virtual byteArray getParam_itemsBought(int id, int aux, int count); + + virtual byteArray getParam_mobKill(shared_ptr plr, shared_ptr mob, DamageSource *dmgSrc); + + virtual byteArray getParam_breedEntity(eINSTANCEOF entityId); + virtual byteArray getParam_tamedEntity(eINSTANCEOF entityId); + virtual byteArray getParam_curedEntity(eINSTANCEOF entityId); + virtual byteArray getParam_craftedEntity(eINSTANCEOF entityId); + virtual byteArray getParam_shearedEntity(eINSTANCEOF entityId); + + virtual byteArray getParam_time(int timediff); + + virtual byteArray getParam_changedDimension(int from, int to); + virtual byteArray getParam_enteredBiome(int biomeId); + + virtual byteArray getParam_achievement(eAward id); + + virtual byteArray getParam_onARail(int distance); + virtual byteArray getParam_overkill(int damage); + virtual byteArray getParam_openInventory(int menuId); + virtual byteArray getParam_chestfulOfCobblestone(int cobbleStone); + virtual byteArray getParam_musicToMyEars(int recordId); + + virtual byteArray getParam_noArgs(); +}; + +// Req Stats \ No newline at end of file diff --git a/Minecraft.World/GetInfoPacket.cpp b/Minecraft.World/GetInfoPacket.cpp new file mode 100644 index 00000000..92db130f --- /dev/null +++ b/Minecraft.World/GetInfoPacket.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "GetInfoPacket.h" + +void GetInfoPacket::read(DataInputStream *dis) +{ +} + +void GetInfoPacket::write(DataOutputStream *dos) +{ +} + +void GetInfoPacket::handle(PacketListener *listener) +{ + listener->handleGetInfo(shared_from_this()); +} + +int GetInfoPacket::getEstimatedSize() +{ + return 0; +} \ No newline at end of file diff --git a/Minecraft.World/GetInfoPacket.h b/Minecraft.World/GetInfoPacket.h new file mode 100644 index 00000000..90688f3e --- /dev/null +++ b/Minecraft.World/GetInfoPacket.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Packet.h" + +class GetInfoPacket : public Packet, public enable_shared_from_this +{ +public: + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new GetInfoPacket()); } + virtual int getId() { return 254; } +}; \ No newline at end of file diff --git a/Minecraft.World/Ghast.cpp b/Minecraft.World/Ghast.cpp new file mode 100644 index 00000000..fcf0b705 --- /dev/null +++ b/Minecraft.World/Ghast.cpp @@ -0,0 +1,249 @@ +#include "stdafx.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.stats.h" +#include "Ghast.h" +#include "..\Minecraft.Client\Textures.h" +#include "LevelEvent.h" +#include "SoundTypes.h" + + + +void Ghast::_init() +{ + floatDuration = 0; + target = nullptr; + retargetTime = 0; + oCharge = 0; + charge = 0; + + xTarget = 0.0f; + yTarget = 0.0f; + zTarget = 0.0f; +} + +Ghast::Ghast(Level *level) : FlyingMob( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + _init(); + + this->textureIdx = TN_MOB_GHAST; // 4J was L"/mob/ghast.png"; + this->setSize(4, 4); + this->fireImmune = true; + xpReward = Enemy::XP_REWARD_MEDIUM; +} + +bool Ghast::hurt(DamageSource *source, int dmg) +{ + if (source->getMsgId() == ChatPacket::e_ChatDeathFireball) + { + shared_ptr player = dynamic_pointer_cast( source->getEntity() ); + if (player != NULL) + { + // reflected fireball, kill the ghast + FlyingMob::hurt(source, 1000); + player->awardStat(GenericStats::ghast(),GenericStats::param_ghast()); + return true; + } + } + + return FlyingMob::hurt(source, dmg); +} + +void Ghast::defineSynchedData() +{ + FlyingMob::defineSynchedData(); + + entityData->define(DATA_IS_CHARGING, (byte) 0); +} + +int Ghast::getMaxHealth() +{ + return 10; +} + +void Ghast::tick() +{ + FlyingMob::tick(); + byte current = entityData->getByte(DATA_IS_CHARGING); +// this->textureName = current == 1 ? L"/mob/ghast_fire.png" : L"/mob/ghast.png"; // 4J replaced with following line + this->textureIdx = current == 1 ? TN_MOB_GHAST_FIRE : TN_MOB_GHAST; +} + +void Ghast::serverAiStep() +{ + if (!level->isClientSide && level->difficulty == Difficulty::PEACEFUL) remove(); + checkDespawn(); + + oCharge = charge; + double xd = xTarget - x; + double yd = yTarget - y; + double zd = zTarget - z; + + double dd = xd * xd + yd * yd + zd * zd; + + if (dd < 1 * 1 || dd > 60 * 60) + { + xTarget = x + (random->nextFloat() * 2 - 1) * 16; + yTarget = y + (random->nextFloat() * 2 - 1) * 16; + zTarget = z + (random->nextFloat() * 2 - 1) * 16; + } + + if (floatDuration-- <= 0) + { + floatDuration += random->nextInt(5) + 2; + + dd = sqrt(dd); + + if (canReach(xTarget, yTarget, zTarget, dd)) + { + this->xd += xd / dd * 0.1; + this->yd += yd / dd * 0.1; + this->zd += zd / dd * 0.1; + } + else + { + xTarget = x; + yTarget = y; + zTarget = z; + } + } + + if (target != NULL && target->removed) target = nullptr; + if (target == NULL || retargetTime-- <= 0) + { + target = level->getNearestAttackablePlayer(shared_from_this(), 100); + if (target != NULL) + { + retargetTime = 20; + } + } + + double maxDist = 64.0f; + if (target != NULL && target->distanceToSqr(shared_from_this()) < maxDist * maxDist) + { + double xdd = target->x - x; + double ydd = (target->bb->y0 + target->bbHeight / 2) - (y + bbHeight / 2); + double zdd = target->z - z; + yBodyRot = yRot = -(float) atan2(xdd, zdd) * 180 / PI; + + if (this->canSee(target)) + { + if (charge == 10) + { + // 4J - change brought forward from 1.2.3 + level->levelEvent(nullptr, LevelEvent::SOUND_GHAST_WARNING, (int) x, (int) y, (int) z, 0); + } + charge++; + if (charge == 20) + { + // 4J - change brought forward from 1.2.3 + level->levelEvent(nullptr, LevelEvent::SOUND_GHAST_FIREBALL, (int) x, (int) y, (int) z, 0); + shared_ptr ie = shared_ptr( new Fireball(level, dynamic_pointer_cast( shared_from_this() ), xdd, ydd, zdd) ); + double d = 4; + Vec3 *v = getViewVector(1); + ie->x = x + v->x * d; + ie->y = y + bbHeight / 2 + 0.5f; + ie->z = z + v->z * d; + level->addEntity(ie); + charge = -40; + } + } + else + { + if (charge > 0) charge--; + } + } + else + { + yBodyRot = yRot = -(float) atan2(this->xd, this->zd) * 180 / PI; + if (charge > 0) charge--; + } + + if (!level->isClientSide) + { + byte old = entityData->getByte(DATA_IS_CHARGING); + byte current = (byte) (charge > 10 ? 1 : 0); + if (old != current) + { + entityData->set(DATA_IS_CHARGING, current); + } + } +} + +bool Ghast::canReach(double xt, double yt, double zt, double dist) +{ + double xd = (xTarget - x) / dist; + double yd = (yTarget - y) / dist; + double zd = (zTarget - z) / dist; + + AABB *bb = this->bb->copy(); + for (int d = 1; d < dist; d++) + { + bb->move(xd, yd, zd); + if (!level->getCubes( shared_from_this(), bb)->empty()) return false; + } + + return true; +} + +int Ghast::getAmbientSound() +{ + return eSoundType_MOB_GHAST_MOAN; +} + +int Ghast::getHurtSound() +{ + return eSoundType_MOB_GHAST_SCREAM; +} + +int Ghast::getDeathSound() +{ + return eSoundType_MOB_GHAST_DEATH; +} + +int Ghast::getDeathLoot() +{ + return Item::sulphur->id; +} + +void Ghast::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + int count = random->nextInt(2) + random->nextInt(1 + playerBonusLevel); + for (int i = 0; i < count; i++) + { + spawnAtLocation(Item::ghastTear_Id, 1); + } + count = random->nextInt(3) + random->nextInt(1 + playerBonusLevel); + for (int i = 0; i < count; i++) + { + spawnAtLocation(Item::sulphur_Id, 1); + } +} + +float Ghast::getSoundVolume() +{ + return 0.4f;//10; 4J-PB - changing due to customer demands +} + +bool Ghast::canSpawn() +{ + return (random->nextInt(20) == 0 && FlyingMob::canSpawn() && level->difficulty > Difficulty::PEACEFUL); +} + +int Ghast::getMaxSpawnClusterSize() +{ + return 1; +} diff --git a/Minecraft.World/Ghast.h b/Minecraft.World/Ghast.h new file mode 100644 index 00000000..cdb44b61 --- /dev/null +++ b/Minecraft.World/Ghast.h @@ -0,0 +1,65 @@ +#pragma once +using namespace std; + +#include "FlyingMob.h" +#include "Enemy.h" + +class GhastClass; +class Level; + +class Ghast : public FlyingMob, public Enemy +{ +public: + eINSTANCEOF GetType() { return eTYPE_GHAST; } + static Entity *create(Level *level) { return new Ghast(level); } + +private: + static const int DATA_IS_CHARGING = 16; + +public: + int floatDuration; + double xTarget, yTarget, zTarget; + +private: + shared_ptr target; + int retargetTime; + +public: + int oCharge; + int charge; + +private: + void _init(); + +public: + Ghast(Level *level); + + virtual bool hurt(DamageSource *source, int dmg); + +protected: + virtual void defineSynchedData(); + +public: + int getMaxHealth(); + +public: + virtual void tick(); + +protected: + virtual void serverAiStep(); + +private: + virtual bool canReach(double xt, double yt, double zt, double dist); + +protected: + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual int getDeathLoot(); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + virtual float getSoundVolume(); + +public: + virtual bool canSpawn(); + virtual int getMaxSpawnClusterSize(); +}; diff --git a/Minecraft.World/Giant.cpp b/Minecraft.World/Giant.cpp new file mode 100644 index 00000000..054394da --- /dev/null +++ b/Minecraft.World/Giant.cpp @@ -0,0 +1,32 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "Giant.h" +#include "..\Minecraft.Client\Textures.h" + + + +Giant::Giant(Level *level) : Monster( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_ZOMBIE; // 4J was L"/mob/zombie.png"; + runSpeed = 0.5f; + attackDamage = 50; + this->heightOffset*=6; + this->setSize(bbWidth * 6, bbHeight * 6); +} + +int Giant::getMaxHealth() +{ + return 100; +} + +float Giant::getWalkTargetValue(int x, int y, int z) +{ + return level->getBrightness(x, y, z)-0.5f; +} \ No newline at end of file diff --git a/Minecraft.World/Giant.h b/Minecraft.World/Giant.h new file mode 100644 index 00000000..21798854 --- /dev/null +++ b/Minecraft.World/Giant.h @@ -0,0 +1,18 @@ +#pragma once +using namespace std; + +#include "Monster.h" + +class Level; + +class Giant : public Monster +{ +public: + eINSTANCEOF GetType() { return eTYPE_GIANT; } + static Entity *create(Level *level) { return new Giant(level); } + + Giant(Level *level); + + int getMaxHealth(); + virtual float getWalkTargetValue(int x, int y, int z); +}; diff --git a/Minecraft.World/GiveItemCommand.cpp b/Minecraft.World/GiveItemCommand.cpp new file mode 100644 index 00000000..1d13592f --- /dev/null +++ b/Minecraft.World/GiveItemCommand.cpp @@ -0,0 +1,50 @@ +#include "stdafx.h" +#include "net.minecraft.commands.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.network.packet.h" +#include "..\Minecraft.Client\ServerPlayer.h" +#include "GiveItemCommand.h" + +EGameCommand GiveItemCommand::getId() +{ + return eGameCommand_Give; +} + +void GiveItemCommand::execute(shared_ptr source, byteArray commandData) +{ + ByteArrayInputStream bais(commandData); + DataInputStream dis(&bais); + + PlayerUID uid = dis.readPlayerUID(); + int item = dis.readInt(); + int amount = dis.readInt(); + int aux = dis.readInt(); + wstring tag = dis.readUTF(); + + bais.reset(); + + shared_ptr player = getPlayer(uid); + if(player != NULL && item > 0 && Item::items[item] != NULL) + { + shared_ptr itemInstance = shared_ptr(new ItemInstance(item, amount, aux)); + player->drop(itemInstance); + //logAdminAction(source, L"commands.give.success", ChatPacket::e_ChatCustom, Item::items[item]->getName(itemInstance), item, amount, player->getAName()); + logAdminAction(source, ChatPacket::e_ChatCustom, L"commands.give.success", item, player->getAName()); + } +} + +shared_ptr GiveItemCommand::preparePacket(shared_ptr player, int item, int amount, int aux, const wstring &tag) +{ + if(player == NULL) return nullptr; + + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + dos.writePlayerUID(player->getXuid()); + dos.writeInt(item); + dos.writeInt(amount); + dos.writeInt(aux); + dos.writeUTF(tag); + + return shared_ptr( new GameCommandPacket(eGameCommand_Give, baos.toByteArray() )); +} \ No newline at end of file diff --git a/Minecraft.World/GiveItemCommand.h b/Minecraft.World/GiveItemCommand.h new file mode 100644 index 00000000..532070a6 --- /dev/null +++ b/Minecraft.World/GiveItemCommand.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Command.h" + +class GameCommandPacket; + +class GiveItemCommand : public Command +{ +public: + virtual EGameCommand getId(); + virtual void execute(shared_ptr source, byteArray commandData); + +public: + static shared_ptr preparePacket(shared_ptr player, int item, int amount = 1, int aux = 0, const wstring &tag = L""); +}; \ No newline at end of file diff --git a/Minecraft.World/GlassTile.cpp b/Minecraft.World/GlassTile.cpp new file mode 100644 index 00000000..d3e7f68f --- /dev/null +++ b/Minecraft.World/GlassTile.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "GlassTile.h" + +GlassTile::GlassTile(int id, Material *material, bool allowSame) : HalfTransparentTile(id, L"glass", material, allowSame) +{ +} + +int GlassTile::getResourceCount(Random *random) +{ + return 0; +} + +int GlassTile::getRenderLayer() +{ + return 0; +} + +bool GlassTile::isSolidRender() +{ + return false; +} + +bool GlassTile::isCubeShaped() +{ + return false; +} + +bool GlassTile::isSilkTouchable() +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/GlassTile.h b/Minecraft.World/GlassTile.h new file mode 100644 index 00000000..77c62759 --- /dev/null +++ b/Minecraft.World/GlassTile.h @@ -0,0 +1,17 @@ +#pragma once +#include "HalfTransparentTile.h" + +class Random; + +class GlassTile : public HalfTransparentTile +{ +public: + using HalfTransparentTile::isSolidRender; + + GlassTile(int id, Material *material, bool allowSame); + virtual int getResourceCount(Random *random); + virtual int getRenderLayer(); + virtual bool isSolidRender(); + virtual bool isCubeShaped(); + virtual bool isSilkTouchable(); +}; diff --git a/Minecraft.World/GlobalEntity.cpp b/Minecraft.World/GlobalEntity.cpp new file mode 100644 index 00000000..b4846b90 --- /dev/null +++ b/Minecraft.World/GlobalEntity.cpp @@ -0,0 +1,3 @@ +#include "stdafx.h" +#include "GlobalEntity.h" + diff --git a/Minecraft.World/GlobalEntity.h b/Minecraft.World/GlobalEntity.h new file mode 100644 index 00000000..3d0892ba --- /dev/null +++ b/Minecraft.World/GlobalEntity.h @@ -0,0 +1,11 @@ +#pragma once +#include "Entity.h" + +class Level; + +//class GlobalEntity : public Entity +class GlobalEntity : public Entity +{ +public: + GlobalEntity(Level *level) : Entity( level ) {}; +}; \ No newline at end of file diff --git a/Minecraft.World/Goal.cpp b/Minecraft.World/Goal.cpp new file mode 100644 index 00000000..2420aefa --- /dev/null +++ b/Minecraft.World/Goal.cpp @@ -0,0 +1,39 @@ +#include "stdafx.h" +#include "Goal.h" + +Goal::Goal() +{ + _requiredControlFlags = 0; +} + +bool Goal::canContinueToUse() +{ + return canUse(); +} + +bool Goal::canInterrupt() +{ + return true; +} + +void Goal::start() +{ +} + +void Goal::stop() +{ +} + +void Goal::tick() +{ +} + +void Goal::setRequiredControlFlags(int requiredControlFlags) +{ + _requiredControlFlags = requiredControlFlags; +} + +int Goal::getRequiredControlFlags() +{ + return _requiredControlFlags; +} \ No newline at end of file diff --git a/Minecraft.World/Goal.h b/Minecraft.World/Goal.h new file mode 100644 index 00000000..80d39b9b --- /dev/null +++ b/Minecraft.World/Goal.h @@ -0,0 +1,23 @@ +#pragma once + +class Goal +{ +private: + int _requiredControlFlags; + +protected: + Goal(); +public: + virtual ~Goal() {} + virtual bool canUse() = 0; + virtual bool canContinueToUse(); + virtual bool canInterrupt(); + virtual void start(); + virtual void stop(); + virtual void tick(); + virtual void setRequiredControlFlags(int requiredControlFlags); + virtual int getRequiredControlFlags(); + + // 4J Added override to update ai elements when loading entity from schematics + virtual void setLevel(Level *level) {}; +}; \ No newline at end of file diff --git a/Minecraft.World/GoalSelector.cpp b/Minecraft.World/GoalSelector.cpp new file mode 100644 index 00000000..85a37dda --- /dev/null +++ b/Minecraft.World/GoalSelector.cpp @@ -0,0 +1,147 @@ +#include "stdafx.h" +#include "Goal.h" +#include "GoalSelector.h" + + +GoalSelector::InternalGoal::InternalGoal(int prio, Goal *goal, bool canDeletePointer) +{ + this->prio = prio; + this->goal = goal; + this->canDeletePointer = canDeletePointer; +} + +GoalSelector::GoalSelector() +{ + tickCount = 0; + newGoalRate = 3; +} + +GoalSelector::~GoalSelector() +{ + for(AUTO_VAR(it, goals.begin()); it != goals.end(); ++it) + { + if((*it)->canDeletePointer) delete (*it)->goal; + delete (*it); + } +} + +void GoalSelector::addGoal(int prio, Goal *goal, bool canDeletePointer /*= true*/) // 4J Added canDelete param +{ + goals.push_back(new InternalGoal(prio, goal, canDeletePointer)); +} + +void GoalSelector::tick() +{ + vector toStart; + + if(tickCount++ % newGoalRate == 0) + { + //for (InternalGoal ig : goals) + for(AUTO_VAR(it, goals.begin()); it != goals.end(); ++it) + { + InternalGoal *ig = *it; + //bool isUsing = usingGoals.contains(ig); + AUTO_VAR(usingIt, find(usingGoals.begin(), usingGoals.end(), ig)); + + //if (isUsing) + if(usingIt != usingGoals.end()) + { + if (!canUseInSystem(ig) || !canContinueToUse(ig)) + { + ig->goal->stop(); + //usingGoals.remove(ig); + usingGoals.erase(usingIt); + } + else continue; + } + + if (!canUseInSystem(ig) || !ig->goal->canUse()) continue; + + toStart.push_back(ig); + usingGoals.push_back(ig); + } + } + else + { + for(AUTO_VAR(it, usingGoals.begin() ); it != usingGoals.end(); ) + { + InternalGoal *ig = *it; + if (!ig->goal->canContinueToUse()) + { + ig->goal->stop(); + it = usingGoals.erase(it); + } + else + { + ++it; + } + } + } + + + //bool debug = false; + //if (debug && toStart.size() > 0) System.out.println("Starting: "); + //for (InternalGoal ig : toStart) + for(AUTO_VAR(it, toStart.begin()); it != toStart.end(); ++it) + { + //if (debug) System.out.println(ig.goal.toString() + ", "); + (*it)->goal->start(); + } + + //if (debug && usingGoals.size() > 0) System.out.println("Running: "); + //for (InternalGoal ig : usingGoals) + for(AUTO_VAR(it, usingGoals.begin()); it != usingGoals.end(); ++it) + { + //if (debug) System.out.println(ig.goal.toString()); + (*it)->goal->tick(); + } +} + +vector *GoalSelector::getRunningGoals() +{ + return &usingGoals; +} + +bool GoalSelector::canContinueToUse(InternalGoal *ig) +{ + return ig->goal->canContinueToUse(); +} + +bool GoalSelector::canUseInSystem(GoalSelector::InternalGoal *goal) +{ + //for (InternalGoal ig : goals) + for(AUTO_VAR(it, goals.begin()); it != goals.end(); ++it) + { + InternalGoal *ig = *it; + if (ig == goal) continue; + + AUTO_VAR(usingIt, find(usingGoals.begin(), usingGoals.end(), ig)); + + if (goal->prio >= ig->prio) + { + if (usingIt != usingGoals.end() && !canCoExist(goal, ig)) return false; + } + else if (usingIt != usingGoals.end() && !ig->goal->canInterrupt()) return false; + } + + return true; +} + +bool GoalSelector::canCoExist(GoalSelector::InternalGoal *goalA, GoalSelector::InternalGoal *goalB) +{ + return (goalA->goal->getRequiredControlFlags() & goalB->goal->getRequiredControlFlags()) == 0; +} + +void GoalSelector::setNewGoalRate(int newGoalRate) +{ + this->newGoalRate = newGoalRate; +} + +void GoalSelector::setLevel(Level *level) +{ + for(AUTO_VAR(it, goals.begin()); it != goals.end(); ++it) + { + InternalGoal *ig = *it; + ig->goal->setLevel(level); + } +} \ No newline at end of file diff --git a/Minecraft.World/GoalSelector.h b/Minecraft.World/GoalSelector.h new file mode 100644 index 00000000..c2bf0b87 --- /dev/null +++ b/Minecraft.World/GoalSelector.h @@ -0,0 +1,45 @@ +#pragma once +using namespace std; + +class Goal; + +class GoalSelector +{ +private: + class InternalGoal + { + public: + // 4J Added canDelete param + InternalGoal(int prio, Goal *goal, bool canDeletePointer); + + Goal *goal; + int prio; + bool canDeletePointer; + }; + +private: + vector goals; + vector usingGoals; + int tickCount; + int newGoalRate; + +public: + GoalSelector(); + ~GoalSelector(); + + // 4J Added canDelete param + void addGoal(int prio, Goal *goal, bool canDeletePointer = true); + void tick(); + vector *getRunningGoals(); + +private: + bool canContinueToUse(InternalGoal *ig); + bool canUseInSystem(InternalGoal *goal); + bool canCoExist(InternalGoal *goalA, InternalGoal *goalB); + +public: + void setNewGoalRate(int newGoalRate); + + // 4J Added override to update ai elements when loading entity from schematics + void setLevel(Level *level); +}; \ No newline at end of file diff --git a/Minecraft.World/GoldenAppleItem.cpp b/Minecraft.World/GoldenAppleItem.cpp new file mode 100644 index 00000000..6c99d201 --- /dev/null +++ b/Minecraft.World/GoldenAppleItem.cpp @@ -0,0 +1,53 @@ +#include "stdafx.h" +#include "net.minecraft.world.effect.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "SharedConstants.h" +#include "GoldenAppleItem.h" + +GoldenAppleItem::GoldenAppleItem(int id, int nutrition, float saturationMod, bool isMeat) : FoodItem(id, nutrition, saturationMod, isMeat) +{ + setStackedByData(true); +} + +bool GoldenAppleItem::isFoil(shared_ptr itemInstance) +{ + return itemInstance->getAuxValue() > 0; +} + +const Rarity *GoldenAppleItem::getRarity(shared_ptr itemInstance) +{ + if (itemInstance->getAuxValue() == 0) + { + return Rarity::rare; + } + return Rarity::epic; +} + +void GoldenAppleItem::addEatEffect(shared_ptr instance, Level *level, shared_ptr player) +{ + if (instance->getAuxValue() > 0) + { + if (!level->isClientSide) + { + player->addEffect(new MobEffectInstance(MobEffect::regeneration->id, 30 * SharedConstants::TICKS_PER_SECOND, 3)); + player->addEffect(new MobEffectInstance(MobEffect::damageResistance->id, 300 * SharedConstants::TICKS_PER_SECOND, 0)); + player->addEffect(new MobEffectInstance(MobEffect::fireResistance->id, 300 * SharedConstants::TICKS_PER_SECOND, 0)); + } + } + else + { + FoodItem::addEatEffect(instance, level, player); + } +} + +unsigned int GoldenAppleItem::getUseDescriptionId(int iData /*= -1*/) +{ + if (iData == 0) return IDS_DESC_GOLDENAPPLE; + else return IDS_DESC_ENCHANTED_GOLDENAPPLE; +} + +unsigned int GoldenAppleItem::getUseDescriptionId(shared_ptr instance) +{ + return this->getUseDescriptionId(instance->getAuxValue()); +} \ No newline at end of file diff --git a/Minecraft.World/GoldenAppleItem.h b/Minecraft.World/GoldenAppleItem.h new file mode 100644 index 00000000..722bb2a9 --- /dev/null +++ b/Minecraft.World/GoldenAppleItem.h @@ -0,0 +1,21 @@ +#pragma once + +#include "FoodItem.h" + +class GoldenAppleItem : public FoodItem +{ +public: + using Item::getUseDescriptionId; + + GoldenAppleItem(int id, int nutrition, float saturationMod, bool isMeat); + + virtual bool isFoil(shared_ptr itemInstance); + virtual const Rarity *getRarity(shared_ptr itemInstance); + + // 4J-JEV: Enchanted goldenapples and goldenapples each require their own tooltips. + virtual unsigned int getUseDescriptionId(int iData /*= -1*/); + virtual unsigned int getUseDescriptionId(shared_ptr instance); + +protected: + void addEatEffect(shared_ptr instance, Level *level, shared_ptr player); +}; diff --git a/Minecraft.World/Golem.cpp b/Minecraft.World/Golem.cpp new file mode 100644 index 00000000..7e3dc9d2 --- /dev/null +++ b/Minecraft.World/Golem.cpp @@ -0,0 +1,39 @@ +#include "stdafx.h" + +#include "Golem.h" + + + +Golem::Golem(Level *level) : PathfinderMob(level) +{ +} + + +void Golem::causeFallDamage(float distance) +{ +} + +int Golem::getAmbientSound() +{ + return -1; +} + +int Golem::getHurtSound() +{ + return -1; +} + +int Golem::getDeathSound() +{ + return -1; +} + +int Golem::getAmbientSoundInterval() +{ + return 20 * 6; +} + +bool Golem::removeWhenFarAway() +{ + return false; +} \ No newline at end of file diff --git a/Minecraft.World/Golem.h b/Minecraft.World/Golem.h new file mode 100644 index 00000000..1943483b --- /dev/null +++ b/Minecraft.World/Golem.h @@ -0,0 +1,25 @@ +#pragma once + +#include "PathfinderMob.h" +#include "Creature.h" + +class Level; +class CompoundTag; + +class Golem : public PathfinderMob, public Creature +{ +public: + Golem(Level *level); + +protected: + virtual void causeFallDamage(float distance); + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + +public: + virtual int getAmbientSoundInterval(); + +protected: + virtual bool removeWhenFarAway(); +}; \ No newline at end of file diff --git a/Minecraft.World/GrassColor.cpp b/Minecraft.World/GrassColor.cpp new file mode 100644 index 00000000..4ba7c46b --- /dev/null +++ b/Minecraft.World/GrassColor.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" +#include "GrassColor.h" + +// 4J Stu - Don't want to do this any more +//intArray GrassColor::pixels; +// +//void GrassColor::init(intArray pixels) +//{ +// int *oldData = GrassColor::pixels.data; +// GrassColor::pixels = pixels; +// delete [] oldData; +//} +// +//int GrassColor::get(double temp, double rain) +//{ +// rain *= temp; +// int x = (int) ((1 - temp) * 255); +// int y = (int) ((1 - rain) * 255); +// int returnVal = pixels[y << 8 | x]; +// return returnVal; +//} \ No newline at end of file diff --git a/Minecraft.World/GrassColor.h b/Minecraft.World/GrassColor.h new file mode 100644 index 00000000..3aa09283 --- /dev/null +++ b/Minecraft.World/GrassColor.h @@ -0,0 +1,12 @@ +#pragma once + +class GrassColor +{ + // 4J Stu - We don't use this any more +//private: +// static intArray pixels; +//public: +// +// static void init(intArray pixels); +// static int get(double temp, double rain); +}; \ No newline at end of file diff --git a/Minecraft.World/GrassTile.cpp b/Minecraft.World/GrassTile.cpp new file mode 100644 index 00000000..0e955025 --- /dev/null +++ b/Minecraft.World/GrassTile.cpp @@ -0,0 +1,146 @@ +#include "stdafx.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "GrassTile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.h" +#include "net.minecraft.world.h" + +// AP - included for PSVita Alpha cut out optimisation +#include "IntBuffer.h" +#include "..\Minecraft.Client\Tesselator.h" + +GrassTile::GrassTile(int id) : Tile(id, Material::grass) +{ + iconTop = NULL; + iconSnowSide = NULL; + iconSideOverlay = NULL; + + setTicking(true); +} + +Icon *GrassTile::getTexture(int face, int data) +{ + if (face == Facing::UP) return iconTop; + if (face == Facing::DOWN) return Tile::dirt->getTexture(face); + return icon; +} + +Icon *GrassTile::getTexture(LevelSource *level, int x, int y, int z, int face) +{ + if (face == Facing::UP) return iconTop; + if (face == Facing::DOWN) return Tile::dirt->getTexture(face); + Material *above = level->getMaterial(x, y + 1, z); + if (above == Material::topSnow || above == Material::snow) return iconSnowSide; + else return icon; +} + +void GrassTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"grass_side"); + iconTop = iconRegister->registerIcon(L"grass_top"); + iconSnowSide = iconRegister->registerIcon(L"snow_side"); + iconSideOverlay = iconRegister->registerIcon(L"grass_side_overlay"); +} + +int GrassTile::getColor() const +{ + // 4J Replaced + //double temp = 0.5; + //double rain = 1.0; + + //return GrassColor::get(temp, rain); + + return Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Grass_Common ); +} + +int GrassTile::getColor(int auxData) +{ + return getColor(); +} + +int GrassTile::getColor(LevelSource *level, int x, int y, int z) +{ + return getColor( level, x, y, z, level->getData( x, y, z ) ); +} + +// 4J - changed interface to have data passed in, and put existing interface as wrapper above +int GrassTile::getColor(LevelSource *level, int x, int y, int z, int data) +{ + //return level->getBiomeSource()->getBiome(x, z)->getGrassColor(level, x, y, z); + + int totalRed = 0; + int totalGreen = 0; + int totalBlue = 0; + + for (int oz = -1; oz <= 1; oz++) + { + for (int ox = -1; ox <= 1; ox++) + { + int grassColor = level->getBiome(x + ox, z + oz)->getGrassColor(); + + totalRed += (grassColor & 0xff0000) >> 16; + totalGreen += (grassColor & 0xff00) >> 8; + totalBlue += (grassColor & 0xff); + } + } + + return (((totalRed / 9) & 0xFF) << 16) | (((totalGreen / 9) & 0xFF) << 8) | (((totalBlue / 9) & 0xFF)); +} + +void GrassTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (level->isClientSide) return; + + if (level->getRawBrightness(x, y + 1, z) < MIN_BRIGHTNESS && Tile::lightBlock[level->getTile(x, y + 1, z)] > 2) + { + level->setTile(x, y, z, Tile::dirt_Id); + } + else + { + if (level->getRawBrightness(x, y + 1, z) >= Level::MAX_BRIGHTNESS - 6) + { + for (int i = 0; i < 4; i++) + { + int xt = x + random->nextInt(3) - 1; + int yt = y + random->nextInt(5) - 3; + int zt = z + random->nextInt(3) - 1; + int above = level->getTile(xt, yt + 1, zt); + if (level->getTile(xt, yt, zt) == Tile::dirt_Id && level->getRawBrightness(xt, yt + 1, zt) >= MIN_BRIGHTNESS && Tile::lightBlock[above] <= 2) + { + level->setTile(xt, yt, zt, Tile::grass_Id); + } + } + } + } +} + +int GrassTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::dirt->getResource(0, random, playerBonusLevel); +} + +Icon *GrassTile::getSideTextureOverlay() +{ +#ifdef __PSVITA__ + // AP - alpha cut out is expensive on vita. Because of the way grass sides are treated as special case we need to set the alpha flag here + // this would normally happen in TileRenderer::getTextureOrMissing + Tesselator* t = Tesselator::getInstance(); + t->setAlphaCutOut( true ); +#endif + + return Tile::grass->iconSideOverlay; +} + +bool GrassTile::shouldTileTick(Level *level, int x,int y,int z) +{ + bool should = false; + + if( (level->getRawBrightness(x, y + 1, z) < MIN_BRIGHTNESS && Tile::lightBlock[level->getTile(x, y + 1, z)] > 2) || + (level->getRawBrightness(x, y + 1, z) >= Level::MAX_BRIGHTNESS - 6) ) + { + should = true; + } + + return should; +} diff --git a/Minecraft.World/GrassTile.h b/Minecraft.World/GrassTile.h new file mode 100644 index 00000000..41b59574 --- /dev/null +++ b/Minecraft.World/GrassTile.h @@ -0,0 +1,33 @@ +#pragma once +#include "Tile.h" + +class Level; +class ChunkRebuildData; +class GrassTile : public Tile +{ + friend class Tile; + friend class ChunkRebuildData; +private: + Icon *iconTop; + Icon *iconSnowSide; + Icon *iconSideOverlay; +public: + static const int MIN_BRIGHTNESS = 4; + +protected: + GrassTile(int id); +public: + virtual Icon *getTexture(int face, int data); + virtual Icon *getTexture(LevelSource *level, int x, int y, int z, int face); + void registerIcons(IconRegister *iconRegister); + virtual int getColor() const; + virtual int getColor(int auxData); + virtual int getColor(LevelSource *level, int x, int y, int z); + virtual int getColor(LevelSource *level, int x, int y, int z, int data); // 4J added + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual int getResource(int data, Random *random, int playerBonusLevel); + static Icon *getSideTextureOverlay(); + + // 4J Added so we can check before we try to add a tile to the tick list if it's actually going to do seomthing + virtual bool shouldTileTick(Level *level, int x,int y,int z); +}; \ No newline at end of file diff --git a/Minecraft.World/GravelTile.cpp b/Minecraft.World/GravelTile.cpp new file mode 100644 index 00000000..1ee866a9 --- /dev/null +++ b/Minecraft.World/GravelTile.cpp @@ -0,0 +1,13 @@ +#include "stdafx.h" +#include "GravelTile.h" +#include "net.minecraft.world.item.h" + +GravelTile::GravelTile(int type) : HeavyTile(type) +{ +} + +int GravelTile::getResource(int data, Random *random, int playerBonusLevel) +{ + if (random->nextInt(10 - playerBonusLevel * 3) == 0) return Item::flint->id; + return id; +} \ No newline at end of file diff --git a/Minecraft.World/GravelTile.h b/Minecraft.World/GravelTile.h new file mode 100644 index 00000000..0457a9e9 --- /dev/null +++ b/Minecraft.World/GravelTile.h @@ -0,0 +1,11 @@ +#pragma once +#include "HeavyTile.h" + +class Random; + +class GravelTile : public HeavyTile +{ +public: + GravelTile(int type); + virtual int getResource(int data, Random *random, int playerBonusLevel); +}; diff --git a/Minecraft.World/GroundBushFeature.cpp b/Minecraft.World/GroundBushFeature.cpp new file mode 100644 index 00000000..df7561f3 --- /dev/null +++ b/Minecraft.World/GroundBushFeature.cpp @@ -0,0 +1,43 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "GroundBushFeature.h" + +GroundBushFeature::GroundBushFeature(int trunkType, int leafType) +{ + this->trunkTileType = trunkType; + this->leafTileType = leafType; +} + +bool GroundBushFeature::place(Level *level, Random *random, int x, int y, int z) +{ + PIXBeginNamedEvent(0,"Placing GroundBushFeature"); + int t = 0; + while (((t = level->getTile(x, y, z)) == 0 || t == Tile::leaves_Id) && y > 0) + y--; + + int tile = level->getTile(x, y, z); + if (tile == Tile::dirt_Id || tile == Tile::grass_Id) + { + y++; + placeBlock(level, x, y, z, Tile::treeTrunk_Id, trunkTileType); + + for (int yy = y; yy <= y + 2; yy++) + { + int yo = yy - y; + int offs = 2 - yo; + for (int xx = x - offs; xx <= x + offs; xx++) + { + int xo = xx - (x); + for (int zz = z - offs; zz <= z + offs; zz++) + { + int zo = zz - (z); + if (abs(xo) == offs && abs(zo) == offs && random->nextInt(2) == 0) continue; + if (!Tile::solid[level->getTile(xx, yy, zz)]) placeBlock(level, xx, yy, zz, Tile::leaves_Id, leafTileType); + } + } + } + } + PIXEndNamedEvent(); + return true; +} \ No newline at end of file diff --git a/Minecraft.World/GroundBushFeature.h b/Minecraft.World/GroundBushFeature.h new file mode 100644 index 00000000..cf7f16bb --- /dev/null +++ b/Minecraft.World/GroundBushFeature.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Feature.h" + +class GroundBushFeature : public Feature +{ +private: + int leafTileType; + int trunkTileType; + +public: + GroundBushFeature(int trunkType, int leafType); + + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/GrowMushroomIslandLayer.cpp b/Minecraft.World/GrowMushroomIslandLayer.cpp new file mode 100644 index 00000000..14857366 --- /dev/null +++ b/Minecraft.World/GrowMushroomIslandLayer.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" +#include "net.minecraft.world.level.biome.h" + + +GrowMushroomIslandLayer::GrowMushroomIslandLayer(__int64 seedMixup, shared_ptr parent) : Layer(seedMixup) +{ + this->parent = parent; +} + +intArray GrowMushroomIslandLayer::getArea(int xo, int yo, int w, int h) +{ + int px = xo - 1; + int py = yo - 1; + int pw = w + 2; + int ph = h + 2; + intArray p = parent->getArea(px, py, pw, ph); + + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + int n1 = p[(x + 0) + (y + 0) * pw]; + int n2 = p[(x + 2) + (y + 0) * pw]; + int n3 = p[(x + 0) + (y + 2) * pw]; + int n4 = p[(x + 2) + (y + 2) * pw]; + + int c = p[(x + 1) + (y + 1) * pw]; + + if( ( n1 == Biome::mushroomIsland->id ) || ( n2 == Biome::mushroomIsland->id ) || ( n3 == Biome::mushroomIsland->id ) || ( n4 == Biome::mushroomIsland->id ) ) + { + result[x + y * w] = Biome::mushroomIsland->id; + } + else + { + result[x + y * w] = c; + } + } + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/GrowMushroomIslandLayer.h b/Minecraft.World/GrowMushroomIslandLayer.h new file mode 100644 index 00000000..3bca4d5c --- /dev/null +++ b/Minecraft.World/GrowMushroomIslandLayer.h @@ -0,0 +1,9 @@ +#pragma once +#include "Layer.h" + +class GrowMushroomIslandLayer : public Layer +{ +public: + GrowMushroomIslandLayer(__int64 seedMixup, shared_ptr parent); + virtual intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/HalfSlabTile.cpp b/Minecraft.World/HalfSlabTile.cpp new file mode 100644 index 00000000..ed3969c1 --- /dev/null +++ b/Minecraft.World/HalfSlabTile.cpp @@ -0,0 +1,143 @@ +#include "stdafx.h" +#include "HalfSlabTile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.stats.h" +#include "Facing.h" + +/*package net.minecraft.world.level.tile; + +import java.util.*; + +import net.minecraft.Facing; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.*; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.phys.AABB;*/ + + + +HalfSlabTile::HalfSlabTile(int id, bool fullSize, Material *material) : Tile(id, material, fullSize) +{ + this->fullSize = fullSize; + + if (fullSize) + { + solid[id] = true; + } + else + { + setShape(0, 0, 0, 1, 0.5f, 1); + } + setLightBlock(255); +} + +void HalfSlabTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + if (fullSize) + { + setShape(0, 0, 0, 1, 1, 1); + } + else + { + bool upper = (level->getData(x, y, z) & TOP_SLOT_BIT) != 0; + if (upper) + { + setShape(0, 0.5f, 0, 1, 1, 1); + } + else + { + setShape(0, 0, 0, 1, 0.5f, 1); + } + } +} + +void HalfSlabTile::updateDefaultShape() +{ + if (fullSize) + { + setShape(0, 0, 0, 1, 1, 1); + } + else + { + setShape(0, 0, 0, 1, 0.5f, 1); + } +} + +void HalfSlabTile::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source) +{ + updateShape(level, x, y, z); + Tile::addAABBs(level, x, y, z, box, boxes, source); +} + +bool HalfSlabTile::isSolidRender(bool isServerLevel) +{ + return fullSize; +} + +int HalfSlabTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) +{ + if (fullSize) return itemValue; + + if (face == Facing::DOWN || (face != Facing::UP && clickY > 0.5)) + { + return itemValue | TOP_SLOT_BIT; + } + return itemValue; +} + +int HalfSlabTile::getResourceCount(Random *random) +{ + if (fullSize) + { + return 2; + } + return 1; +} + +int HalfSlabTile::getSpawnResourcesAuxValue(int data) +{ + return data & TYPE_MASK; +} + +bool HalfSlabTile::isCubeShaped() +{ + return fullSize; +} + +bool HalfSlabTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + if (fullSize) return Tile::shouldRenderFace(level, x, y, z, face); + + if (face != Facing::UP && face != Facing::DOWN && !Tile::shouldRenderFace(level, x, y, z, face)) + { + return false; + } + + int ox = x, oy = y, oz = z; + ox += Facing::STEP_X[Facing::OPPOSITE_FACING[face]]; + oy += Facing::STEP_Y[Facing::OPPOSITE_FACING[face]]; + oz += Facing::STEP_Z[Facing::OPPOSITE_FACING[face]]; + + boolean isUpper = (level->getData(ox, oy, oz) & TOP_SLOT_BIT) != 0; + if (isUpper) + { + if (face == Facing::DOWN) return true; + if (face == Facing::UP && Tile::shouldRenderFace(level, x, y, z, face)) return true; + return !(isHalfSlab(level->getTile(x, y, z)) && (level->getData(x, y, z) & TOP_SLOT_BIT) != 0); + } + else + { + if (face == Facing::UP) return true; + if (face == Facing::DOWN && Tile::shouldRenderFace(level, x, y, z, face)) return true; + return !(isHalfSlab(level->getTile(x, y, z)) && (level->getData(x, y, z) & TOP_SLOT_BIT) == 0); + } +} + +bool HalfSlabTile::isHalfSlab(int tileId) +{ + return tileId == Tile::stoneSlabHalf_Id || tileId == Tile::woodSlabHalf_Id; +} + + diff --git a/Minecraft.World/HalfSlabTile.h b/Minecraft.World/HalfSlabTile.h new file mode 100644 index 00000000..183a4add --- /dev/null +++ b/Minecraft.World/HalfSlabTile.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Tile.h" + + +class HalfSlabTile : public Tile +{ + + +public: + static const int TYPE_MASK = 7; + static const int TOP_SLOT_BIT = 8; + +protected: + bool fullSize; + +public: + HalfSlabTile(int id, bool fullSize, Material *material); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual void updateDefaultShape(); + virtual void addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source); + virtual bool isSolidRender(bool isServerLevel); + virtual int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + virtual int getResourceCount(Random *random); + virtual int getSpawnResourcesAuxValue(int data); + virtual bool isCubeShaped(); + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); +private: + static bool isHalfSlab(int tileId); +public: + virtual int getAuxName(int auxValue) = 0; + +}; \ No newline at end of file diff --git a/Minecraft.World/HalfTransparentTile.cpp b/Minecraft.World/HalfTransparentTile.cpp new file mode 100644 index 00000000..6fa63d71 --- /dev/null +++ b/Minecraft.World/HalfTransparentTile.cpp @@ -0,0 +1,32 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" +#include "HalfTransparentTile.h" + +HalfTransparentTile::HalfTransparentTile(int id, const wstring &tex, Material *material, bool allowSame) : Tile(id,material,isSolidRender()) +{ + this->allowSame = allowSame; + this->texture = tex; +} + +bool HalfTransparentTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool HalfTransparentTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + int id = level->getTile(x, y, z); + if (!allowSame && id == this->id) return false; + return Tile::shouldRenderFace(level, x, y, z, face); +} + +bool HalfTransparentTile::blocksLight() +{ + return false; +} + +void HalfTransparentTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(texture); +} \ No newline at end of file diff --git a/Minecraft.World/HalfTransparentTile.h b/Minecraft.World/HalfTransparentTile.h new file mode 100644 index 00000000..e3d34c3f --- /dev/null +++ b/Minecraft.World/HalfTransparentTile.h @@ -0,0 +1,19 @@ +#pragma once +#include "Tile.h" + +class ChunkRebuildData; + +class HalfTransparentTile : public Tile +{ + friend class ChunkRebuildData; +private: + bool allowSame; + wstring texture; +protected: + HalfTransparentTile(int id, const wstring &tex, Material *material, bool allowSame); +public: + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + virtual bool blocksLight(); + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/HangingEntity.cpp b/Minecraft.World/HangingEntity.cpp new file mode 100644 index 00000000..6256a381 --- /dev/null +++ b/Minecraft.World/HangingEntity.cpp @@ -0,0 +1,292 @@ +#include "stdafx.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.damagesource.h" +#include "com.mojang.nbt.h" +#include "HangingEntity.h" + + + +void HangingEntity::_init(Level *level) +{ + checkInterval = 0; + dir = 0; + xTile = yTile = zTile = 0; +} + +HangingEntity::HangingEntity(Level *level) : Entity( level ) +{ + _init(level); + + this->heightOffset = 0; + this->setSize(0.5f, 0.5f); +} + +HangingEntity::HangingEntity(Level *level, int xTile, int yTile, int zTile, int dir) : Entity( level ) +{ + _init(level); + //motive = NULL; + this->heightOffset = 0; + this->setSize(0.5f, 0.5f); + this->xTile = xTile; + this->yTile = yTile; + this->zTile = zTile; +} + +void HangingEntity::setDir(int dir) +{ + this->dir = dir; + this->yRotO = this->yRot = (float)(dir * 90); + + float w = (float)getWidth(); + float h = (float)getHeight(); + float d = (float)getWidth(); + + if (dir == Direction::NORTH || dir == Direction::SOUTH) + { + d = 0.5f; + yRot = yRotO = (float)(Direction::DIRECTION_OPPOSITE[dir] * 90); + } + else + { + w = 0.5f; + } + + w /= 32.0f; + h /= 32.0f; + d /= 32.0f; + + float x = xTile + 0.5f; + float y = yTile + 0.5f; + float z = zTile + 0.5f; + + float fOffs = 0.5f + 1.0f / 16.0f; + + if (dir == Direction::NORTH) z -= fOffs; + if (dir == Direction::WEST) x -= fOffs; + if (dir == Direction::SOUTH) z += fOffs; + if (dir == Direction::EAST) x += fOffs; + + if (dir == Direction::NORTH) x -= offs(getWidth()); + if (dir == Direction::WEST) z += offs(getWidth()); + if (dir == Direction::SOUTH) x += offs(getWidth()); + if (dir == Direction::EAST) z -= offs(getWidth()); + y += offs(getHeight()); + + this->setPos(x, y, z); + + float ss = -(0.5f / 16.0f); + + // 4J Stu - Due to rotations the bb couold be set with a lower bound x/z being higher than the higher bound + float x0 = x - w - ss; + float x1 = x + w + ss; + float y0 = y - h - ss; + float y1 = y + h + ss; + float z0 = z - d - ss; + float z1 = z + d + ss; + bb->set(min(x0,x1), min(y0,y1), min(z0,z1), max(x0,x1), max(y0,y1), max(z0,z1)); +} + +float HangingEntity::offs(int w) +{ + if (w == 32) return 0.5f; + if (w == 64) return 0.5f; + return 0.0f; +} + +void HangingEntity::tick() +{ + if (checkInterval++ == 20 * 5 && !level->isClientSide)//isClientSide) + { + checkInterval = 0; + if (!removed && !survives()) + { + remove(); + dropItem(); + } + } +} + +bool HangingEntity::survives() +{ + if (level->getCubes(shared_from_this(), bb)->size()!=0)//isEmpty()) + { + return false; + } + else + { + int ws = max(1, getWidth() / 16); + int hs = max(1, getHeight() / 16); + + int xt = xTile; + int yt = yTile; + int zt = zTile; + if (dir == Direction::NORTH) xt = Mth::floor(x - getWidth() / 32.0f); + if (dir == Direction::WEST) zt = Mth::floor(z - getWidth() / 32.0f); + if (dir == Direction::SOUTH) xt = Mth::floor(x - getWidth() / 32.0f); + if (dir == Direction::EAST) zt = Mth::floor(z - getWidth() / 32.0f); + yt = Mth::floor(y - getHeight() / 32.0f); + + for (int ss = 0; ss < ws; ss++) + { + for (int yy = 0; yy < hs; yy++) + { + Material *m; + if (dir == Direction::NORTH || dir == Direction::SOUTH) + { + m = level->getMaterial(xt + ss, yt + yy, zTile); + } + else + { + m = level->getMaterial(xTile, yt + yy, zt + ss); + } + if (!m->isSolid()) + { + return false; + } + } + + vector > *entities = level->getEntities(shared_from_this(), bb); + + if (entities != NULL && entities->size() > 0) + { + AUTO_VAR(itEnd, entities->end()); + for (AUTO_VAR(it, entities->begin()); it != itEnd; it++) + { + shared_ptr e = (*it); + if(dynamic_pointer_cast(e) != NULL) + { + return false; + } + } + } + } + } + return true; +} + +bool HangingEntity::isPickable() +{ + return true; +} + +bool HangingEntity::skipAttackInteraction(shared_ptr source) +{ + if(source->GetType()==eTYPE_PLAYER) + { + return hurt(DamageSource::playerAttack(dynamic_pointer_cast( source)), 0); + } + return false; +} + +bool HangingEntity::hurt(DamageSource *source, int damage) +{ + if (!removed && !level->isClientSide) + { + if (dynamic_cast(source) != NULL) + { + shared_ptr sourceEntity = source->getDirectEntity(); + + if (dynamic_pointer_cast(sourceEntity) != NULL && !dynamic_pointer_cast(sourceEntity)->isAllowedToHurtEntity(shared_from_this()) ) + { + return false; + } + } + + remove(); + markHurt(); + + shared_ptr player = nullptr; + shared_ptr e = source->getEntity(); + if (e!=NULL && ((e->GetType() & eTYPE_PLAYER)!=0) ) // check if it's serverplayer or player + { + player = dynamic_pointer_cast( e ); + } + + if (player != NULL && player->abilities.instabuild) + { + return true; + } + + dropItem(); + } + return true; +} + +// 4J - added noEntityCubes parameter +void HangingEntity::move(double xa, double ya, double za, bool noEntityCubes) +{ + if (!level->isClientSide && !removed && (xa * xa + ya * ya + za * za) > 0) + { + remove(); + dropItem(); + } +} + +void HangingEntity::push(double xa, double ya, double za) +{ + if (!level->isClientSide && !removed && (xa * xa + ya * ya + za * za) > 0) + { + remove(); + dropItem(); + } +} + +void HangingEntity::addAdditonalSaveData(CompoundTag *tag) +{ + tag->putByte(L"Direction", (byte) dir); + tag->putInt(L"TileX", xTile); + tag->putInt(L"TileY", yTile); + tag->putInt(L"TileZ", zTile); + + // Back compat + switch (dir) + { + case Direction::NORTH: + tag->putByte(L"Dir", (byte) 0); + break; + case Direction::WEST: + tag->putByte(L"Dir", (byte) 1); + break; + case Direction::SOUTH: + tag->putByte(L"Dir", (byte) 2); + break; + case Direction::EAST: + tag->putByte(L"Dir", (byte) 3); + break; + } +} + +void HangingEntity::readAdditionalSaveData(CompoundTag *tag) +{ + if (tag->contains(L"Direction")) + { + dir = tag->getByte(L"Direction"); + } + else + { + switch (tag->getByte(L"Dir")) + { + case 0: + dir = Direction::NORTH; + break; + case 1: + dir = Direction::WEST; + break; + case 2: + dir = Direction::SOUTH; + break; + case 3: + dir = Direction::EAST; + break; + } + } + xTile = tag->getInt(L"TileX"); + yTile = tag->getInt(L"TileY"); + zTile = tag->getInt(L"TileZ"); + setDir(dir); +} + + diff --git a/Minecraft.World/HangingEntity.h b/Minecraft.World/HangingEntity.h new file mode 100644 index 00000000..b87915e4 --- /dev/null +++ b/Minecraft.World/HangingEntity.h @@ -0,0 +1,42 @@ + +#pragma once + +#include "Entity.h" + +class HangingEntity : public Entity +{ +public: + eINSTANCEOF GetType() { return eTYPE_HANGING_ENTITY; } +private: + + void _init(Level *level); + float offs(int w); + + int checkInterval; + //eINSTANCEOF eType; + +protected: + virtual void defineSynchedData() {}; + +public: + int dir; + int xTile, yTile, zTile; + + HangingEntity(Level *level); + HangingEntity(Level *level, int xTile, int yTile, int zTile, int dir); + void setDir(int dir); + bool survives(); + + virtual void tick(); + virtual bool isPickable(); + virtual bool skipAttackInteraction(shared_ptr source); + virtual bool hurt(DamageSource *source, int damage); + virtual void move(double xa, double ya, double za, bool noEntityCubes=false); // 4J - added noEntityCubes parameter + virtual void push(double xa, double ya, double za); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + + virtual int getWidth()=0; + virtual int getHeight()=0; + virtual void dropItem()=0; +}; diff --git a/Minecraft.World/HangingEntityItem.cpp b/Minecraft.World/HangingEntityItem.cpp new file mode 100644 index 00000000..505dce4d --- /dev/null +++ b/Minecraft.World/HangingEntityItem.cpp @@ -0,0 +1,88 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.h" +#include "HangingEntityItem.h" +#include "HangingEntity.h" +#include "Painting.h" +#include "GenericStats.h" +#include "ItemFrame.h" + + +HangingEntityItem::HangingEntityItem(int id, eINSTANCEOF eClassType) : Item(id) +{ + //super(id); + //this.clazz = clazz; + this->eType=eClassType; + // setItemCategory(CreativeModeTab.TAB_DECORATIONS); +} + +bool HangingEntityItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int xt, int yt, int zt, int face, float clickX, float clickY, float clickZ, bool bTestOnly) +{ + if (face == Facing::DOWN) return false; + if (face == Facing::UP) return false; + + if(bTestOnly) + { + if (!player->mayBuild(xt, yt, zt)) return false; + + return true; + } + + int dir = Direction::FACING_DIRECTION[face]; + + shared_ptr entity = createEntity(level, xt, yt, zt, dir); + + //if (!player->mayUseItemAt(xt, yt, zt, face, instance)) return false; + if (!player->mayBuild(xt, yt, zt)) return false; + + if (entity != NULL && entity->survives()) + { + if (!level->isClientSide) + { + if(level->addEntity(entity)==TRUE) + { + // 4J-JEV: Hook for durango 'BlockPlaced' event. + if (eType==eTYPE_PAINTING) player->awardStat(GenericStats::blocksPlaced(Item::painting_Id), GenericStats::param_blocksPlaced(Item::painting_Id,instance->getAuxValue(),1)); + else if (eType==eTYPE_ITEM_FRAME) player->awardStat(GenericStats::blocksPlaced(Item::itemFrame_Id), GenericStats::param_blocksPlaced(Item::itemFrame_Id,instance->getAuxValue(),1)); + + instance->count--; + } + else + { + player->displayClientMessage(IDS_MAX_HANGINGENTITIES ); + return false; + } + } + else + { + instance->count--; + } + } + return true; +} + + +shared_ptr HangingEntityItem::createEntity(Level *level, int x, int y, int z, int dir) +{ + if (eType == eTYPE_PAINTING) + { + shared_ptr painting = shared_ptr(new Painting(level, x, y, z, dir)); + painting->PaintingPostConstructor(dir); + + return dynamic_pointer_cast (painting); + } + else if (eType == eTYPE_ITEM_FRAME) + { + shared_ptr itemFrame = shared_ptr(new ItemFrame(level, x, y, z, dir)); + + return dynamic_pointer_cast (itemFrame); + } + else + { + return nullptr; + } +} + diff --git a/Minecraft.World/HangingEntityItem.h b/Minecraft.World/HangingEntityItem.h new file mode 100644 index 00000000..51bd8b23 --- /dev/null +++ b/Minecraft.World/HangingEntityItem.h @@ -0,0 +1,21 @@ +#pragma once + +#include "item.h" + +class HangingEntity; + +class HangingEntityItem : public Item +{ +private: + //final Class clazz; + eINSTANCEOF eType; + +public: + HangingEntityItem(int id, eINSTANCEOF eClassType); + + virtual bool useOn(shared_ptr instance, shared_ptr player, Level *level, int xt, int yt, int zt, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly);//, float clickX, float clickY, float clickZ); + +private: + shared_ptr createEntity(Level *level, int x, int y, int z, int dir) ; + +}; diff --git a/Minecraft.World/HashExtension.h b/Minecraft.World/HashExtension.h new file mode 100644 index 00000000..7ec9a909 --- /dev/null +++ b/Minecraft.World/HashExtension.h @@ -0,0 +1,20 @@ +#pragma once +#ifdef _XBOX +//using namespace std; +namespace std +{ + namespace tr1 + { + + template + class hash< shared_ptr > + { + public: + size_t operator()(const shared_ptr& key) const + { + return (size_t)key.get(); + } + }; + } +} +#endif \ No newline at end of file diff --git a/Minecraft.World/Hasher.cpp b/Minecraft.World/Hasher.cpp new file mode 100644 index 00000000..7c3d0564 --- /dev/null +++ b/Minecraft.World/Hasher.cpp @@ -0,0 +1,28 @@ +#include "stdafx.h" +#include + +#include "Hasher.h" + +Hasher::Hasher(wstring &salt) +{ + this->salt = salt; +} + +wstring Hasher::getHash(wstring &name) +{ + // 4J Stu - Removed try/catch + //try { + wstring s = wstring( salt ).append( name ); + //MessageDigest m; + //m = MessageDigest.getInstance("MD5"); + //m.update(s.getBytes(), 0, s.length()); + //return new BigInteger(1, m.digest()).toString(16); + + // TODO 4J Stu - Will this hash us with the same distribution as the MD5? + return _toString( std::hash{}( s ) ); + //} + //catch (NoSuchAlgorithmException e) + //{ + // throw new RuntimeException(e); + //} +} \ No newline at end of file diff --git a/Minecraft.World/Hasher.h b/Minecraft.World/Hasher.h new file mode 100644 index 00000000..325a95b8 --- /dev/null +++ b/Minecraft.World/Hasher.h @@ -0,0 +1,12 @@ +#pragma once +using namespace std; + +class Hasher +{ +private: + wstring salt; + +public: + Hasher(wstring &salt); + wstring getHash(wstring &name); +}; \ No newline at end of file diff --git a/Minecraft.World/HatchetItem.cpp b/Minecraft.World/HatchetItem.cpp new file mode 100644 index 00000000..16bf89bf --- /dev/null +++ b/Minecraft.World/HatchetItem.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.tile.h" +#include "HatchetItem.h" + +TileArray *HatchetItem::diggables = NULL; + +void HatchetItem::staticCtor() +{ + HatchetItem::diggables = new TileArray( HATCHET_DIGGABLES); + diggables->data[0] = Tile::wood; + diggables->data[1] = Tile::bookshelf; + diggables->data[2] = Tile::treeTrunk; + diggables->data[3] = Tile::chest; + // 4J - brought forward from 1.2.3 + diggables->data[4] = Tile::stoneSlab; + diggables->data[5] = Tile::stoneSlabHalf; + diggables->data[6] = Tile::pumpkin; + diggables->data[7] = Tile::litPumpkin; +} + +HatchetItem::HatchetItem(int id, const Tier *tier) : DiggerItem (id, 3, tier, diggables) +{ +} + +// 4J - brought forward from 1.2.3 +float HatchetItem::getDestroySpeed(shared_ptr itemInstance, Tile *tile) +{ + if (tile != NULL && tile->material == Material::wood) + { + return speed; + } + return DiggerItem::getDestroySpeed(itemInstance, tile); +} \ No newline at end of file diff --git a/Minecraft.World/HatchetItem.h b/Minecraft.World/HatchetItem.h new file mode 100644 index 00000000..afb6dfc0 --- /dev/null +++ b/Minecraft.World/HatchetItem.h @@ -0,0 +1,15 @@ +#pragma once + +#include "DiggerItem.h" + +#define HATCHET_DIGGABLES 8 +class HatchetItem : public DiggerItem +{ +private: + static TileArray *diggables; + +public: + static void staticCtor(); + HatchetItem(int id, const Tier *tier); + virtual float getDestroySpeed(shared_ptr itemInstance, Tile *tile); // 4J - brought forward from 1.2.3 +}; diff --git a/Minecraft.World/HeavyTile.cpp b/Minecraft.World/HeavyTile.cpp new file mode 100644 index 00000000..18460f2f --- /dev/null +++ b/Minecraft.World/HeavyTile.cpp @@ -0,0 +1,91 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.item.h" +#include "HeavyTile.h" +#include "FireTile.h" + +bool HeavyTile::instaFall = false; + +HeavyTile::HeavyTile(int type, bool isSolidRender) : Tile(type, Material::sand, isSolidRender) +{ +} + +HeavyTile::HeavyTile(int type, Material *material, bool isSolidRender) : Tile(type, material, isSolidRender) +{ +} + +void HeavyTile::onPlace(Level *level, int x, int y, int z) +{ + level->addToTickNextTick(x, y, z, id, getTickDelay()); +} + +void HeavyTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + level->addToTickNextTick(x, y, z, id, getTickDelay()); +} + +void HeavyTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if(!level->isClientSide) + { + checkSlide(level, x, y, z); + } +} + +void HeavyTile::checkSlide(Level *level, int x, int y, int z) +{ + int x2 = x; + int y2 = y; + int z2 = z; + if (isFree(level, x2, y2 - 1, z2) && y2 >= 0) + { + int r = 32; + + if (instaFall || !level->hasChunksAt(x - r, y - r, z - r, x + r, y + r, z + r) ) + { + level->setTile(x, y, z, 0); + while (isFree(level, x, y - 1, z) && y > 0) + y--; + if (y > 0) { + level->setTile(x, y, z, id); + } + } + else if (!level->isClientSide) + { + // 4J added - don't do anything just now if we can't create any new falling tiles + if( !level->newFallingTileAllowed() ) + { + level->addToTickNextTick(x, y, z, id, getTickDelay()); + return; + } + + shared_ptr e = shared_ptr( new FallingTile(level, x + 0.5f, y + 0.5f, z + 0.5f, id, level->getData(x, y, z)) ); + falling(e); + level->addEntity(e); + } + } +} + +void HeavyTile::falling(shared_ptr entity) +{ +} + +int HeavyTile::getTickDelay() +{ + return 5; +} + +bool HeavyTile::isFree(Level *level, int x, int y, int z) +{ + int t = level->getTile(x, y, z); + if (t == 0) return true; + if (t == Tile::fire_Id) return true; + Material *material = Tile::tiles[t]->material; + if (material == Material::water) return true; + if (material == Material::lava) return true; + return false; +} + +void HeavyTile::onLand(Level *level, int xt, int yt, int zt, int data) +{ +} \ No newline at end of file diff --git a/Minecraft.World/HeavyTile.h b/Minecraft.World/HeavyTile.h new file mode 100644 index 00000000..a9186eae --- /dev/null +++ b/Minecraft.World/HeavyTile.h @@ -0,0 +1,26 @@ +#pragma once +#include "Tile.h" + +class Random; +class Level; +class FallingTile; + +class HeavyTile : public Tile +{ +public: + static bool instaFall; + + HeavyTile(int type, bool isSolidRender = true); + HeavyTile(int type, Material *material, bool isSolidRender = true); + virtual void onPlace(Level *level, int x, int y, int z); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual void tick(Level *level, int x, int y, int z, Random *random); +private: + void checkSlide(Level *level, int x, int y, int z); +protected: + virtual void falling(shared_ptr entity); +public: + virtual int getTickDelay(); + static bool isFree(Level *level, int x, int y, int z); + virtual void onLand(Level *level, int xt, int yt, int zt, int data); +}; diff --git a/Minecraft.World/HellBiome.cpp b/Minecraft.World/HellBiome.cpp new file mode 100644 index 00000000..df0b4ed7 --- /dev/null +++ b/Minecraft.World/HellBiome.cpp @@ -0,0 +1,16 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.monster.h" +#include "HellBiome.h" + +HellBiome::HellBiome(int id) : Biome(id) +{ + enemies.clear(); + friendlies.clear(); + friendlies_chicken.clear(); // 4J added + friendlies_wolf.clear(); // 4J added + waterFriendlies.clear(); + + enemies.push_back(new MobSpawnerData(eTYPE_GHAST, 50, 4, 4)); + enemies.push_back(new MobSpawnerData(eTYPE_PIGZOMBIE, 100, 4, 4)); + enemies.push_back(new MobSpawnerData(eTYPE_LAVASLIME, 1, 4, 4)); +} \ No newline at end of file diff --git a/Minecraft.World/HellBiome.h b/Minecraft.World/HellBiome.h new file mode 100644 index 00000000..ed51de83 --- /dev/null +++ b/Minecraft.World/HellBiome.h @@ -0,0 +1,8 @@ +#pragma once +#include "Biome.h" + +class HellBiome : public Biome +{ +public: + HellBiome(int id); +}; \ No newline at end of file diff --git a/Minecraft.World/HellDimension.cpp b/Minecraft.World/HellDimension.cpp new file mode 100644 index 00000000..85e2a25a --- /dev/null +++ b/Minecraft.World/HellDimension.cpp @@ -0,0 +1,89 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.storage.h" +#include "HellDimension.h" +#include "net.minecraft.world.level.levelgen.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.tile.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\Common\Colours\ColourTable.h" + +void HellDimension::init() +{ + biomeSource = new FixedBiomeSource(Biome::hell, 1, 0); + ultraWarm = true; + hasCeiling = true; + id = -1; +} + +Vec3 *HellDimension::getFogColor(float td, float a) const +{ + int colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Nether_Fog_Colour ); + byte redComponent = ((colour>>16)&0xFF); + byte greenComponent = ((colour>>8)&0xFF); + byte blueComponent = ((colour)&0xFF); + + float rr = (float)redComponent/256;//0.2f; + float gg = (float)greenComponent/256;//0.03f; + float bb = (float)blueComponent/256;//0.03f; + return Vec3::newTemp(rr, gg, bb); +} + +void HellDimension::updateLightRamp() +{ + float ambientLight = 0.10f; + for (int i = 0; i <= Level::MAX_BRIGHTNESS; i++) + { + float v = (1 - i / (float) (Level::MAX_BRIGHTNESS)); + brightnessRamp[i] = ((1 - v) / (v * 3 + 1)) * (1 - ambientLight) + ambientLight; + } +} + +ChunkSource *HellDimension::createRandomLevelSource() const +{ +#ifdef _DEBUG_MENUS_ENABLED + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<getSeed()); + } + else +#endif + if (levelType == LevelType::lvl_flat) + { + return new HellFlatLevelSource(level, level->getSeed()); + } + else + { + return new HellRandomLevelSource(level, level->getSeed()); + } +} + +bool HellDimension::isNaturalDimension() +{ + return false; +} + +bool HellDimension::isValidSpawn(int x, int z) const +{ + return false; +} + +float HellDimension::getTimeOfDay(__int64 time, float a) const +{ + return 0.5f; +} + +bool HellDimension::mayRespawn() const +{ + return false; +} + +bool HellDimension::isFoggyAt(int x, int z) +{ + return true; +} + +int HellDimension::getXZSize() +{ + return ceil((float)level->getLevelData()->getXZSize() / level->getLevelData()->getHellScale()); +} diff --git a/Minecraft.World/HellDimension.h b/Minecraft.World/HellDimension.h new file mode 100644 index 00000000..b37f2b53 --- /dev/null +++ b/Minecraft.World/HellDimension.h @@ -0,0 +1,23 @@ +#pragma once +#include "Dimension.h" + +class HellDimension : public Dimension +{ +public: + virtual void init(); + virtual Vec3 *getFogColor(float td, float a) const; + +protected: + virtual void updateLightRamp(); + +public: + virtual ChunkSource *createRandomLevelSource() const; + virtual bool isNaturalDimension(); + virtual bool isValidSpawn(int x, int y) const; + virtual float getTimeOfDay(__int64 time, float a) const; + virtual bool mayRespawn() const; + virtual bool isFoggyAt(int x, int z); + + // 4J Added + virtual int getXZSize(); +}; diff --git a/Minecraft.World/HellFireFeature.cpp b/Minecraft.World/HellFireFeature.cpp new file mode 100644 index 00000000..f795d997 --- /dev/null +++ b/Minecraft.World/HellFireFeature.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "HellFireFeature.h" +#include "net.minecraft.world.level.tile.h" + +bool HellFireFeature::place(Level *level, Random *random, int x, int y, int z) +{ + for (int i = 0; i < 64; i++) + { + int x2 = x + random->nextInt(8) - random->nextInt(8); + int y2 = y + random->nextInt(4) - random->nextInt(4); + int z2 = z + random->nextInt(8) - random->nextInt(8); + if (!level->isEmptyTile(x2, y2, z2)) continue; + if (level->getTile(x2, y2 - 1, z2) != Tile::hellRock_Id) continue; + level->setTile(x2, y2, z2, Tile::fire_Id); + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/HellFireFeature.h b/Minecraft.World/HellFireFeature.h new file mode 100644 index 00000000..9373e25b --- /dev/null +++ b/Minecraft.World/HellFireFeature.h @@ -0,0 +1,10 @@ +#pragma once +#include "Feature.h" + +class Level; + +class HellFireFeature : public Feature +{ +public: + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/HellFlatLevelSource.cpp b/Minecraft.World/HellFlatLevelSource.cpp new file mode 100644 index 00000000..0b1c4359 --- /dev/null +++ b/Minecraft.World/HellFlatLevelSource.cpp @@ -0,0 +1,224 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.storage.h" +#include "HellFlatLevelSource.h" + +HellFlatLevelSource::HellFlatLevelSource(Level *level, __int64 seed) +{ + int xzSize = level->getLevelData()->getXZSize(); + int hellScale = level->getLevelData()->getHellScale(); + m_XZSize = ceil((float)xzSize / hellScale); + + this->level = level; + + random = new Random(seed); + pprandom = new Random(seed); +} + +HellFlatLevelSource::~HellFlatLevelSource() +{ + delete random; + delete pprandom; +} + +void HellFlatLevelSource::prepareHeights(int xOffs, int zOffs, byteArray blocks) +{ + int height = blocks.length / (16 * 16); + + for (int xc = 0; xc < 16; xc++) + { + for (int zc = 0; zc < 16; zc++) + { + for (int yc = 0; yc < height; yc++) + { + int block = 0; + if ( (yc <= 6) || ( yc >= 121 ) ) + { + block = Tile::hellRock_Id; + } + + blocks[xc << 11 | zc << 7 | yc] = (byte) block; + } + } + } +} + +void HellFlatLevelSource::buildSurfaces(int xOffs, int zOffs, byteArray blocks) +{ + for (int x = 0; x < 16; x++) + { + for (int z = 0; z < 16; z++) + { + for (int y = Level::genDepthMinusOne; y >= 0; y--) + { + int offs = (z * 16 + x) * Level::genDepth + y; + + // 4J Build walls around the level + bool blockSet = false; + if(xOffs <= -(m_XZSize/2)) + { + if( z - random->nextInt( 4 ) <= 0 || xOffs < -(m_XZSize/2) ) + { + blocks[offs] = (byte) Tile::unbreakable_Id; + blockSet = true; + } + } + if(zOffs <= -(m_XZSize/2)) + { + if( x - random->nextInt( 4 ) <= 0 || zOffs < -(m_XZSize/2)) + { + blocks[offs] = (byte) Tile::unbreakable_Id; + blockSet = true; + } + } + if(xOffs >= (m_XZSize/2)-1) + { + if( z + random->nextInt(4) >= 15 || xOffs > (m_XZSize/2)) + { + blocks[offs] = (byte) Tile::unbreakable_Id; + blockSet = true; + } + } + if(zOffs >= (m_XZSize/2)-1) + { + if( x + random->nextInt(4) >= 15 || zOffs > (m_XZSize/2) ) + { + blocks[offs] = (byte) Tile::unbreakable_Id; + blockSet = true; + } + } + if( blockSet ) continue; + // End 4J Extra to build walls around the level + + if (y >= Level::genDepthMinusOne - random->nextInt(5)) + { + blocks[offs] = (byte) Tile::unbreakable_Id; + } + else if (y <= 0 + random->nextInt(5)) + { + blocks[offs] = (byte) Tile::unbreakable_Id; +} + } + } + } +} + +LevelChunk *HellFlatLevelSource::create(int x, int z) +{ + return getChunk(x,z); +} + +LevelChunk *HellFlatLevelSource::getChunk(int xOffs, int zOffs) +{ + random->setSeed(xOffs * 341873128712l + zOffs * 132897987541l); + + // 4J - now allocating this with a physical alloc & bypassing general memory management so that it will get cleanly freed + int chunksSize = Level::genDepth * 16 * 16; + byte *tileData = (byte *)XPhysicalAlloc(chunksSize, MAXULONG_PTR, 4096, PAGE_READWRITE); + XMemSet128(tileData,0,chunksSize); + byteArray blocks = byteArray(tileData,chunksSize); +// byteArray blocks = byteArray(16 * level->depth * 16); + + prepareHeights(xOffs, zOffs, blocks); + buildSurfaces(xOffs, zOffs, blocks); + +// caveFeature->apply(this, level, xOffs, zOffs, blocks); + // townFeature.apply(this, level, xOffs, zOffs, blocks); + // addCaves(xOffs, zOffs, blocks); + // addTowns(xOffs, zOffs, blocks); + + // 4J - this now creates compressed block data from the blocks array passed in, so needs to be after data is finalised. + // Also now need to free the passed in blocks as the LevelChunk doesn't use the passed in allocation anymore. + LevelChunk *levelChunk = new LevelChunk(level, blocks, xOffs, zOffs); + XPhysicalFree(tileData); + return levelChunk; +} + +// 4J - removed & moved into its own method from getChunk, so we can call recalcHeightmap after the chunk is added into the cache. Without +// doing this, then loads of the lightgaps() calls will fail to add any lights, because adding a light checks if the cache has this chunk in. +// lightgaps also does light 1 block into the neighbouring chunks, and maybe that is somehow enough to get lighting to propagate round the world, +// but this just doesn't seem right - this isn't a new fault in the 360 version, have checked that java does the same. +void HellFlatLevelSource::lightChunk(LevelChunk *lc) +{ + lc->recalcHeightmap(); +} + +bool HellFlatLevelSource::hasChunk(int x, int y) +{ + return true; +} + +void HellFlatLevelSource::postProcess(ChunkSource *parent, int xt, int zt) +{ + HeavyTile::instaFall = true; + int xo = xt * 16; + int zo = zt * 16; + + // 4J - added. The original java didn't do any setting of the random seed here. We'll be running our postProcess in parallel with getChunk etc. so + // we need to use a separate random - have used the same initialisation code as used in RandomLevelSource::postProcess to make sure this random value + // is consistent for each world generation. Also changed all uses of random here to pprandom. + pprandom->setSeed(level->getSeed()); + __int64 xScale = pprandom->nextLong() / 2 * 2 + 1; + __int64 zScale = pprandom->nextLong() / 2 * 2 + 1; + pprandom->setSeed(((xt * xScale) + (zt * zScale)) ^ level->getSeed()); + + int count = pprandom->nextInt(pprandom->nextInt(10) + 1) + 1; + + for (int i = 0; i < count; i++) + { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(Level::genDepth - 8) + 4; + int z = zo + pprandom->nextInt(16) + 8; + HellFireFeature().place(level, pprandom, x, y, z); + } + + count = pprandom->nextInt(pprandom->nextInt(10) + 1); + for (int i = 0; i < count; i++) + { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(Level::genDepth - 8) + 4; + int z = zo + pprandom->nextInt(16) + 8; + LightGemFeature().place(level, pprandom, x, y, z); + } + + HeavyTile::instaFall = false; + + app.processSchematics(parent->getChunk(xt,zt)); + +} + +bool HellFlatLevelSource::save(bool force, ProgressListener *progressListener) +{ + return true; +} + +bool HellFlatLevelSource::tick() +{ + return false; +} + +bool HellFlatLevelSource::shouldSave() +{ + return true; +} + +wstring HellFlatLevelSource::gatherStats() +{ + return L"HellFlatLevelSource"; +} + +vector *HellFlatLevelSource::getMobsAt(MobCategory *mobCategory, int x, int y, int z) +{ + Biome *biome = level->getBiome(x, z); + if (biome == NULL) + { + return NULL; + } + return biome->getMobs(mobCategory); +} + +TilePos *HellFlatLevelSource::findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z) +{ + return NULL; +} diff --git a/Minecraft.World/HellFlatLevelSource.h b/Minecraft.World/HellFlatLevelSource.h new file mode 100644 index 00000000..b756d9a6 --- /dev/null +++ b/Minecraft.World/HellFlatLevelSource.h @@ -0,0 +1,51 @@ +#pragma once +#include "ChunkSource.h" +#include "PerlinNoise.h" +#include "LargeFeature.h" +#include "LargeHellCaveFeature.h" +#include "LevelChunk.h" +#include "HellFireFeature.h" +#include "LightGemFeature.h" +#include "HellPortalFeature.h" +#include "FlowerFeature.h" +#include "HellSpringFeature.h" + +class ProgressListener; + +class HellFlatLevelSource : public ChunkSource +{ +public: + static const int CHUNK_HEIGHT = 8; + static const int CHUNK_WIDTH = 4; + +private: + Random *random; + Random *pprandom; + +private: + Level *level; + +public: + HellFlatLevelSource(Level *level, __int64 seed); + ~HellFlatLevelSource(); + +private: + void prepareHeights(int xOffs, int zOffs, byteArray blocks); + +public: + void buildSurfaces(int xOffs, int zOffs, byteArray blocks); + + LevelChunk *create(int x, int z); + LevelChunk *getChunk(int xOffs, int zOffs); + virtual void lightChunk(LevelChunk *lc); // 4J added + +public: + virtual bool hasChunk(int x, int y); + void postProcess(ChunkSource *parent, int xt, int zt); + bool save(bool force, ProgressListener *progressListener); + bool tick(); + bool shouldSave(); + wstring gatherStats(); + virtual vector *getMobsAt(MobCategory *mobCategory, int x, int y, int z); + virtual TilePos *findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z); +}; diff --git a/Minecraft.World/HellPortalFeature.cpp b/Minecraft.World/HellPortalFeature.cpp new file mode 100644 index 00000000..7a918e2c --- /dev/null +++ b/Minecraft.World/HellPortalFeature.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "HellPortalFeature.h" +#include "net.minecraft.world.level.tile.h" + +bool HellPortalFeature::place(Level *level, Random *random, int x, int y, int z) +{ + if (!level->isEmptyTile(x, y, z)) return false; + if (level->getTile(x, y + 1, z) != Tile::hellRock_Id) return false; + level->setTile(x, y, z, Tile::lightGem_Id); + + for (int i = 0; i < 1500; i++) + { + int x2 = x + random->nextInt(8) - random->nextInt(8); + int y2 = y - random->nextInt(12); + int z2 = z + random->nextInt(8) - random->nextInt(8); + if (level->getTile(x2, y2, z2) != 0) continue; + + int count = 0; + for (int t = 0; t < 6; t++) + { + int tile = 0; + if (t == 0) tile = level->getTile(x2 - 1, y2, z2); + if (t == 1) tile = level->getTile(x2 + 1, y2, z2); + if (t == 2) tile = level->getTile(x2, y2 - 1, z2); + if (t == 3) tile = level->getTile(x2, y2 + 1, z2); + if (t == 4) tile = level->getTile(x2, y2, z2 - 1); + if (t == 5) tile = level->getTile(x2, y2, z2 + 1); + + if (tile == Tile::lightGem_Id) count++; + } + + if (count == 1) level->setTile(x2, y2, z2, Tile::lightGem_Id); + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/HellPortalFeature.h b/Minecraft.World/HellPortalFeature.h new file mode 100644 index 00000000..d4699ee3 --- /dev/null +++ b/Minecraft.World/HellPortalFeature.h @@ -0,0 +1,8 @@ +#pragma once +#include "Feature.h" + +class HellPortalFeature : public Feature +{ +public: + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/HellRandomLevelSource.cpp b/Minecraft.World/HellRandomLevelSource.cpp new file mode 100644 index 00000000..10194f7d --- /dev/null +++ b/Minecraft.World/HellRandomLevelSource.cpp @@ -0,0 +1,544 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.level.storage.h" +#include "BiomeSource.h" +#include "HellRandomLevelSource.h" + +HellRandomLevelSource::HellRandomLevelSource(Level *level, __int64 seed) +{ + int xzSize = level->getLevelData()->getXZSize(); + int hellScale = level->getLevelData()->getHellScale(); + m_XZSize = ceil((float)xzSize / hellScale); + + netherBridgeFeature = new NetherBridgeFeature(); + caveFeature = new LargeHellCaveFeature(); + + this->level = level; + + random = new Random(seed); + pprandom = new Random(seed); // 4J - added, so that we can have a separate random for doing post-processing in parallel with creation + lperlinNoise1 = new PerlinNoise(random, 16); + lperlinNoise2 = new PerlinNoise(random, 16); + perlinNoise1 = new PerlinNoise(random, 8); + perlinNoise2 = new PerlinNoise(random, 4); + perlinNoise3 = new PerlinNoise(random, 4); + + scaleNoise = new PerlinNoise(random, 10); + depthNoise = new PerlinNoise(random, 16); +} + +HellRandomLevelSource::~HellRandomLevelSource() +{ + delete netherBridgeFeature; + delete caveFeature; + + delete random; + delete pprandom; // 4J added + delete lperlinNoise1; + delete lperlinNoise2; + delete perlinNoise1; + delete perlinNoise2; + delete perlinNoise3; + + delete scaleNoise; + delete depthNoise; +} + +void HellRandomLevelSource::prepareHeights(int xOffs, int zOffs, byteArray blocks) +{ + int xChunks = 16 / CHUNK_WIDTH; + int waterHeight = 32; + + int xSize = xChunks + 1; + int ySize = Level::genDepth / CHUNK_HEIGHT + 1; + int zSize = xChunks + 1; + doubleArray buffer; // 4J - used to be declared with class level scope but tidying up for thread safety reasons + buffer = getHeights(buffer, xOffs * xChunks, 0, zOffs * xChunks, xSize, ySize, zSize); + + for (int xc = 0; xc < xChunks; xc++) + { + for (int zc = 0; zc < xChunks; zc++) + { + for (int yc = 0; yc < Level::genDepth / CHUNK_HEIGHT; yc++) + { + double yStep = 1 / (double) CHUNK_HEIGHT; + double s0 = buffer[((xc + 0) * zSize + (zc + 0)) * ySize + (yc + 0)]; + double s1 = buffer[((xc + 0) * zSize + (zc + 1)) * ySize + (yc + 0)]; + double s2 = buffer[((xc + 1) * zSize + (zc + 0)) * ySize + (yc + 0)]; + double s3 = buffer[((xc + 1) * zSize + (zc + 1)) * ySize + (yc + 0)]; + + double s0a = (buffer[((xc + 0) * zSize + (zc + 0)) * ySize + (yc + 1)] - s0) * yStep; + double s1a = (buffer[((xc + 0) * zSize + (zc + 1)) * ySize + (yc + 1)] - s1) * yStep; + double s2a = (buffer[((xc + 1) * zSize + (zc + 0)) * ySize + (yc + 1)] - s2) * yStep; + double s3a = (buffer[((xc + 1) * zSize + (zc + 1)) * ySize + (yc + 1)] - s3) * yStep; + + for (int y = 0; y < CHUNK_HEIGHT; y++) + { + double xStep = 1 / (double) CHUNK_WIDTH; + + double _s0 = s0; + double _s1 = s1; + double _s0a = (s2 - s0) * xStep; + double _s1a = (s3 - s1) * xStep; + + for (int x = 0; x < CHUNK_WIDTH; x++) + { + int offs = (x + xc * CHUNK_WIDTH) << Level::genDepthBitsPlusFour | (0 + zc * CHUNK_WIDTH) << Level::genDepthBits | (yc * CHUNK_HEIGHT + y); + int step = 1 << Level::genDepthBits; + double zStep = 1 / (double) CHUNK_WIDTH; + + double val = _s0; + double vala = (_s1 - _s0) * zStep; + for (int z = 0; z < CHUNK_WIDTH; z++) + { + int tileId = 0; + if (yc * CHUNK_HEIGHT + y < waterHeight) + { + tileId = Tile::calmLava_Id; + } + if (val > 0) + { + tileId = Tile::hellRock_Id; + } + + blocks[offs] = (byte) tileId; + offs += step; + val += vala; + } + _s0 += _s0a; + _s1 += _s1a; + } + + s0 += s0a; + s1 += s1a; + s2 += s2a; + s3 += s3a; + } + } + } + } + delete [] buffer.data; +} + +void HellRandomLevelSource::buildSurfaces(int xOffs, int zOffs, byteArray blocks) +{ + int waterHeight = Level::genDepth - 64; + + double s = 1 / 32.0; + + doubleArray sandBuffer(16*16); // 4J - used to be declared with class level scope but moved here for thread safety + doubleArray gravelBuffer(16*16); + doubleArray depthBuffer(16*16); + + sandBuffer = perlinNoise2->getRegion(sandBuffer, xOffs * 16, zOffs * 16, 0, 16, 16, 1, s, s, 1); + gravelBuffer = perlinNoise2->getRegion(gravelBuffer, xOffs * 16, 109, zOffs * 16, 16, 1, 16, s, 1, s); + depthBuffer = perlinNoise3->getRegion(depthBuffer, xOffs * 16, zOffs * 16, 0, 16, 16, 1, s * 2, s * 2, s * 2); + + for (int x = 0; x < 16; x++) + { + for (int z = 0; z < 16; z++) + { + bool sand = (sandBuffer[x + z * 16] + random->nextDouble() * 0.2) > 0; + bool gravel = (gravelBuffer[x + z * 16] + random->nextDouble() * 0.2) > 0; + int runDepth = (int) (depthBuffer[x + z * 16] / 3 + 3 + random->nextDouble() * 0.25); + + int run = -1; + + byte top = (byte) Tile::hellRock_Id; + byte material = (byte) Tile::hellRock_Id; + + for (int y = Level::genDepthMinusOne; y >= 0; y--) + { + int offs = (z * 16 + x) * Level::genDepth + y; + + // 4J Build walls around the level + bool blockSet = false; + if(xOffs <= -(m_XZSize/2)) + { + if( z - random->nextInt( 4 ) <= 0 || xOffs < -(m_XZSize/2) ) + { + blocks[offs] = (byte) Tile::unbreakable_Id; + blockSet = true; + } + } + if(zOffs <= -(m_XZSize/2)) + { + if( x - random->nextInt( 4 ) <= 0 || zOffs < -(m_XZSize/2)) + { + blocks[offs] = (byte) Tile::unbreakable_Id; + blockSet = true; + } + } + if(xOffs >= (m_XZSize/2)-1) + { + if( z + random->nextInt(4) >= 15 || xOffs > (m_XZSize/2)) + { + blocks[offs] = (byte) Tile::unbreakable_Id; + blockSet = true; + } + } + if(zOffs >= (m_XZSize/2)-1) + { + if( x + random->nextInt(4) >= 15 || zOffs > (m_XZSize/2) ) + { + blocks[offs] = (byte) Tile::unbreakable_Id; + blockSet = true; + } + } + if( blockSet ) continue; + // End 4J Extra to build walls around the level + + if (y >= Level::genDepthMinusOne - random->nextInt(5) || y <= 0 + random->nextInt(5)) + { + blocks[offs] = (byte) Tile::unbreakable_Id; + } + else + { + int old = blocks[offs]; + + if (old == 0) + { + run = -1; + } + else if (old == Tile::hellRock_Id) + { + if (run == -1) + { + if (runDepth <= 0) + { + top = 0; + material = (byte) Tile::hellRock_Id; + } + else if (y >= waterHeight - 4 && y <= waterHeight + 1) + { + top = (byte) Tile::hellRock_Id; + material = (byte) Tile::hellRock_Id; + if (gravel) top = (byte) Tile::gravel_Id; + if (gravel) material = (byte) Tile::hellRock_Id; + if (sand) + { + // 4J Stu - Make some nether wart spawn outside of the nether fortresses + if(random->nextInt(16) == 0) + { + top = (byte) Tile::netherStalk_Id; + + // Place the nether wart on top of the soul sand + y += 1; + int genDepthMinusOne = Level::genDepthMinusOne; // Take into local int for PS4 as min takes a reference to the const int there and then needs the value to exist for the linker + y = min(y, genDepthMinusOne); + runDepth += 1; + offs = (z * 16 + x) * Level::genDepth + y; + } + else + { + top = (byte) Tile::hellSand_Id; + } + } + if (sand) material = (byte) Tile::hellSand_Id; + } + + if (y < waterHeight && top == 0) top = (byte) Tile::calmLava_Id; + + run = runDepth; + // 4J Stu - If sand, then allow adding nether wart at heights below the water level + if (y >= waterHeight - 1 || sand) blocks[offs] = top; + else blocks[offs] = material; + } + else if (run > 0) + { + run--; + blocks[offs] = material; + } + } + } + } + } + } + delete [] sandBuffer.data; + delete [] gravelBuffer.data; + delete [] depthBuffer.data; +} + +LevelChunk *HellRandomLevelSource::create(int x, int z) +{ + return getChunk(x,z); +} + +LevelChunk *HellRandomLevelSource::getChunk(int xOffs, int zOffs) +{ + random->setSeed(xOffs * 341873128712l + zOffs * 132897987541l); + + // 4J - now allocating this with a physical alloc & bypassing general memory management so that it will get cleanly freed + int blocksSize = Level::genDepth * 16 * 16; + byte *tileData = (byte *)XPhysicalAlloc(blocksSize, MAXULONG_PTR, 4096, PAGE_READWRITE); + XMemSet128(tileData,0,blocksSize); + byteArray blocks = byteArray(tileData,blocksSize); +// byteArray blocks = byteArray(16 * level->depth * 16); + + prepareHeights(xOffs, zOffs, blocks); + buildSurfaces(xOffs, zOffs, blocks); + + caveFeature->apply(this, level, xOffs, zOffs, blocks); + netherBridgeFeature->apply(this, level, xOffs, zOffs, blocks); + + // 4J - this now creates compressed block data from the blocks array passed in, so needs to be after data is finalised. + // Also now need to free the passed in blocks as the LevelChunk doesn't use the passed in allocation anymore. + LevelChunk *levelChunk = new LevelChunk(level, blocks, xOffs, zOffs); + levelChunk->setCheckAllLight(); + XPhysicalFree(tileData); + return levelChunk; +} + +// 4J - removed & moved into its own method from getChunk, so we can call recalcHeightmap after the chunk is added into the cache. Without +// doing this, then loads of the lightgaps() calls will fail to add any lights, because adding a light checks if the cache has this chunk in. +// lightgaps also does light 1 block into the neighbouring chunks, and maybe that is somehow enough to get lighting to propagate round the world, +// but this just doesn't seem right - this isn't a new fault in the 360 version, have checked that java does the same. +void HellRandomLevelSource::lightChunk(LevelChunk *lc) +{ + lc->recalcHeightmap(); +} + +doubleArray HellRandomLevelSource::getHeights(doubleArray buffer, int x, int y, int z, int xSize, int ySize, int zSize) +{ + if (buffer.data == NULL) + { + buffer = doubleArray(xSize * ySize * zSize); + } + + double s = 1 * 684.412; + double hs = 1 * 684.412 * 3; + + doubleArray pnr, ar, br, sr, dr, fi, fis; // 4J - used to be declared with class level scope but moved here for thread safety + + sr = scaleNoise->getRegion(sr, x, y, z, xSize, 1, zSize, 1.0, 0, 1.0); + dr = depthNoise->getRegion(dr, x, y, z, xSize, 1, zSize, 100.0, 0, 100.0); + + pnr = perlinNoise1->getRegion(pnr, x, y, z, xSize, ySize, zSize, s / 80.0, hs / 60.0, s / 80.0); + ar = lperlinNoise1->getRegion(ar, x, y, z, xSize, ySize, zSize, s, hs, s); + br = lperlinNoise2->getRegion(br, x, y, z, xSize, ySize, zSize, s, hs, s); + + int p = 0; + int pp = 0; + doubleArray yoffs = doubleArray(ySize); + for (int yy = 0; yy < ySize; yy++) + { + yoffs[yy] = cos(yy * PI * 6 / (double) ySize) * 2; + + double dd = yy; + if (yy > ySize / 2) + { + dd = (ySize - 1) - yy; + } + if (dd < 4) { + dd = 4 - dd; + yoffs[yy] -= dd * dd * dd * 10; + } + } + + for (int xx = 0; xx < xSize; xx++) + { + for (int zz = 0; zz < zSize; zz++) + { + double scale = ((sr[pp] + 256.0) / 512); + if (scale > 1) scale = 1; + + double floating = 0; + + double depth = (dr[pp] / 8000.0); + if (depth < 0) depth = -depth; + depth = depth * 3.0 - 3.0; + + if (depth < 0) + { + depth = depth / 2; + if (depth < -1) depth = -1; + depth = depth / 1.4; + depth /= 2; + scale = 0; + } + else + { + if (depth > 1) depth = 1; + depth = depth / 6; + } + scale = (scale) + 0.5; + depth = depth * ySize / 16; + pp++; + + for (int yy = 0; yy < ySize; yy++) + { + double val = 0; + + double yOffs = yoffs[yy]; + + double bb = ar[p] / 512; + double cc = br[p] / 512; + + double v = (pnr[p] / 10 + 1) / 2; + if (v < 0) val = bb; + else if (v > 1) val = cc; + else val = bb + (cc - bb) * v; + val -= yOffs; + + if (yy > ySize - 4) + { + double slide = (yy - (ySize - 4)) / (4 - 1.0f); + val = val * (1 - slide) + -10 * slide; + } + + if (yy < floating) + { + double slide = (floating - yy) / (4); + if (slide < 0) slide = 0; + if (slide > 1) slide = 1; + val = val * (1 - slide) + -10 * slide; + } + + buffer[p] = val; + p++; + } + } + } + + delete [] pnr.data; + delete [] ar.data; + delete [] br.data; + delete [] sr.data; + delete [] dr.data; + delete [] fi.data; + delete [] fis.data; + delete [] yoffs.data; + + return buffer; +} + +bool HellRandomLevelSource::hasChunk(int x, int y) +{ + return true; +} + +void HellRandomLevelSource::postProcess(ChunkSource *parent, int xt, int zt) +{ + HeavyTile::instaFall = true; + int xo = xt * 16; + int zo = zt * 16; + + // 4J - added. The original java didn't do any setting of the random seed here. We'll be running our postProcess in parallel with getChunk etc. so + // we need to use a separate random - have used the same initialisation code as used in RandomLevelSource::postProcess to make sure this random value + // is consistent for each world generation. Also changed all uses of random here to pprandom. + pprandom->setSeed(level->getSeed()); + __int64 xScale = pprandom->nextLong() / 2 * 2 + 1; + __int64 zScale = pprandom->nextLong() / 2 * 2 + 1; + pprandom->setSeed(((xt * xScale) + (zt * zScale)) ^ level->getSeed()); + + netherBridgeFeature->postProcess(level, pprandom, xt, zt); + + for (int i = 0; i < 8; i++) + { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(Level::genDepth - 8) + 4; + int z = zo + pprandom->nextInt(16) + 8; + HellSpringFeature(Tile::lava_Id).place(level, pprandom, x, y, z); + } + + int count = pprandom->nextInt(pprandom->nextInt(10) + 1) + 1; + + for (int i = 0; i < count; i++) + { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(Level::genDepth - 8) + 4; + int z = zo + pprandom->nextInt(16) + 8; + HellFireFeature().place(level, pprandom, x, y, z); + } + + count = pprandom->nextInt(pprandom->nextInt(10) + 1); + for (int i = 0; i < count; i++) + { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(Level::genDepth - 8) + 4; + int z = zo + pprandom->nextInt(16) + 8; + LightGemFeature().place(level, pprandom, x, y, z); + } + + for (int i = 0; i < 10; i++) + { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(Level::genDepth); + int z = zo + pprandom->nextInt(16) + 8; + HellPortalFeature().place(level, pprandom, x, y, z); + } + + if (pprandom->nextInt(1) == 0) + { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(Level::genDepth); + int z = zo + pprandom->nextInt(16) + 8; + FlowerFeature(Tile::mushroom1_Id).place(level, pprandom, x, y, z); + } + + if (pprandom->nextInt(1) == 0) + { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(Level::genDepth); + int z = zo + pprandom->nextInt(16) + 8; + FlowerFeature(Tile::mushroom2_Id).place(level, pprandom, x, y, z); + } + + OreFeature quartzFeature(Tile::netherQuartz_Id, 13, Tile::hellRock_Id); + for (int i = 0; i < 16; i++) + { + int x = xo + pprandom->nextInt(16); + int y = pprandom->nextInt(Level::genDepth - 20) + 10; + int z = zo + pprandom->nextInt(16); + quartzFeature.place(level, pprandom, x, y, z); + } + + HeavyTile::instaFall = false; + + app.processSchematics(parent->getChunk(xt,zt)); + +} + +bool HellRandomLevelSource::save(bool force, ProgressListener *progressListener) +{ + return true; +} + +bool HellRandomLevelSource::tick() +{ + return false; +} + +bool HellRandomLevelSource::shouldSave() +{ + return true; +} + +wstring HellRandomLevelSource::gatherStats() +{ + return L"HellRandomLevelSource"; +} + +vector *HellRandomLevelSource::getMobsAt(MobCategory *mobCategory, int x, int y, int z) +{ + // check if the coordinates is within a netherbridge + if (mobCategory == MobCategory::monster && netherBridgeFeature->isInsideFeature(x, y, z)) + { + return netherBridgeFeature->getBridgeEnemies(); + } + + Biome *biome = level->getBiome(x, z); + if (biome == NULL) + { + return NULL; + } + return biome->getMobs(mobCategory); +} + +TilePos *HellRandomLevelSource::findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z) +{ + return NULL; +} diff --git a/Minecraft.World/HellRandomLevelSource.h b/Minecraft.World/HellRandomLevelSource.h new file mode 100644 index 00000000..0587e86a --- /dev/null +++ b/Minecraft.World/HellRandomLevelSource.h @@ -0,0 +1,72 @@ +#pragma once +#include "ChunkSource.h" +#include "PerlinNoise.h" +#include "LargeFeature.h" +#include "LargeHellCaveFeature.h" +#include "LevelChunk.h" +#include "HellFireFeature.h" +#include "LightGemFeature.h" +#include "HellPortalFeature.h" +#include "FlowerFeature.h" +#include "HellSpringFeature.h" +#include "NetherBridgeFeature.h" + +class ProgressListener; + +class HellRandomLevelSource : public ChunkSource +{ +public: + static const int CHUNK_HEIGHT = 8; + static const int CHUNK_WIDTH = 4; + +private: + Random *random; + Random *pprandom; // 4J added + + PerlinNoise *lperlinNoise1; + PerlinNoise *lperlinNoise2; + PerlinNoise *perlinNoise1; + PerlinNoise *perlinNoise2; + PerlinNoise *perlinNoise3; + +public: + PerlinNoise *scaleNoise; + PerlinNoise *depthNoise; + +private: + Level *level; + +public: + HellRandomLevelSource(Level *level, __int64 seed); + ~HellRandomLevelSource(); + + NetherBridgeFeature *netherBridgeFeature; + +private: + void prepareHeights(int xOffs, int zOffs, byteArray blocks); + +public: + void buildSurfaces(int xOffs, int zOffs, byteArray blocks); + +private: + LargeFeature *caveFeature; + +public: + LevelChunk *create(int x, int z); + LevelChunk *getChunk(int xOffs, int zOffs); + virtual void lightChunk(LevelChunk *lc); // 4J added + +private: + doubleArray getHeights(doubleArray buffer, int x, int y, int z, int xSize, int ySize, int zSize); + +public: + bool hasChunk(int x, int y); + void postProcess(ChunkSource *parent, int xt, int zt); + bool save(bool force, ProgressListener *progressListener); + bool tick(); + bool shouldSave(); + wstring gatherStats(); + + virtual vector *getMobsAt(MobCategory *mobCategory, int x, int y, int z); + virtual TilePos *findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z); +}; diff --git a/Minecraft.World/HellSandTile.cpp b/Minecraft.World/HellSandTile.cpp new file mode 100644 index 00000000..dbaa6012 --- /dev/null +++ b/Minecraft.World/HellSandTile.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.phys.h" +#include "HellSandTile.h" + + +HellSandTile::HellSandTile(int id) : Tile(id, Material::sand) +{ +} + +AABB *HellSandTile::getAABB(Level *level, int x, int y, int z) +{ + float r = 2 / 16.0f; + return AABB::newTemp(x, y, z, x + 1, y + 1 - r, z + 1); +} + +void HellSandTile::entityInside(Level *level, int x, int y, int z, shared_ptr entity) +{ + entity->xd*=0.4; + entity->zd*=0.4; +} \ No newline at end of file diff --git a/Minecraft.World/HellSandTile.h b/Minecraft.World/HellSandTile.h new file mode 100644 index 00000000..9d8d004e --- /dev/null +++ b/Minecraft.World/HellSandTile.h @@ -0,0 +1,11 @@ +#pragma once +#include "Tile.h" +#include "Definitions.h" + +class HellSandTile : public Tile +{ +public: + HellSandTile(int id); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual void entityInside(Level *level, int x, int y, int z, shared_ptr entity); +}; \ No newline at end of file diff --git a/Minecraft.World/HellSpringFeature.cpp b/Minecraft.World/HellSpringFeature.cpp new file mode 100644 index 00000000..cfd2d74f --- /dev/null +++ b/Minecraft.World/HellSpringFeature.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "HellSpringFeature.h" +#include "net.minecraft.world.level.tile.h" + +HellSpringFeature::HellSpringFeature(int tile) +{ + this->tile = tile; +} + +bool HellSpringFeature::place(Level *level, Random *random, int x, int y, int z) +{ + if (level->getTile(x, y + 1, z) != Tile::hellRock_Id) return false; + if (level->getTile(x, y - 1, z) != Tile::hellRock_Id) return false; + + if (level->getTile(x, y, z) != 0 && level->getTile(x, y, z) != Tile::hellRock_Id) return false; + + int rockCount = 0; + if (level->getTile(x - 1, y, z) == Tile::hellRock_Id) rockCount++; + if (level->getTile(x + 1, y, z) == Tile::hellRock_Id) rockCount++; + if (level->getTile(x, y, z - 1) == Tile::hellRock_Id) rockCount++; + if (level->getTile(x, y, z + 1) == Tile::hellRock_Id) rockCount++; + if (level->getTile(x, y - 1, z) == Tile::hellRock_Id) rockCount++; + + int holeCount = 0; + if (level->isEmptyTile(x - 1, y, z)) holeCount++; + if (level->isEmptyTile(x + 1, y, z)) holeCount++; + if (level->isEmptyTile(x, y, z - 1)) holeCount++; + if (level->isEmptyTile(x, y, z + 1)) holeCount++; + if (level->isEmptyTile(x, y - 1, z)) holeCount++; + + if (rockCount == 4 && holeCount == 1) + { + level->setTile(x, y, z, tile); + level->setInstaTick(true); + Tile::tiles[tile]->tick(level, x, y, z, random); + level->setInstaTick(false); + } + + return true; + +} \ No newline at end of file diff --git a/Minecraft.World/HellSpringFeature.h b/Minecraft.World/HellSpringFeature.h new file mode 100644 index 00000000..a8ffbc00 --- /dev/null +++ b/Minecraft.World/HellSpringFeature.h @@ -0,0 +1,14 @@ +#pragma once +#include "Feature.h" + + +class HellSpringFeature : public Feature +{ +private: + int tile; + +public: + HellSpringFeature(int tile); + + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/HellStoneTile.cpp b/Minecraft.World/HellStoneTile.cpp new file mode 100644 index 00000000..dbfbba20 --- /dev/null +++ b/Minecraft.World/HellStoneTile.cpp @@ -0,0 +1,6 @@ +#include "stdafx.h" +#include "HellStoneTile.h" + +HellStoneTile::HellStoneTile(int id) : Tile(id, Material::stone) +{ +} \ No newline at end of file diff --git a/Minecraft.World/HellStoneTile.h b/Minecraft.World/HellStoneTile.h new file mode 100644 index 00000000..6d0a0466 --- /dev/null +++ b/Minecraft.World/HellStoneTile.h @@ -0,0 +1,8 @@ +#pragma once +#include "Tile.h" + +class HellStoneTile : public Tile +{ +public: + HellStoneTile(int id); +}; \ No newline at end of file diff --git a/Minecraft.World/HitResult.cpp b/Minecraft.World/HitResult.cpp new file mode 100644 index 00000000..ffa93541 --- /dev/null +++ b/Minecraft.World/HitResult.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" + +#include "net.minecraft.world.entity.h" +#include "HitResult.h" + +HitResult::HitResult(int x, int y, int z, int f, Vec3 *pos) +{ + this->type = TILE; + this->x = x; + this->y = y; + this->z = z; + this->f = f; + this->pos = Vec3::newTemp(pos->x, pos->y, pos->z); + + this->entity = nullptr; +} + +HitResult::HitResult(shared_ptr entity) +{ + this->type = ENTITY; + this->entity = entity; + pos = Vec3::newTemp(entity->x, entity->y, entity->z); + + x = y = z = f = 0; +} + +double HitResult::distanceTo(shared_ptr e) +{ + double xd = pos->x - e->x; + double yd = pos->y - e->y; + double zd = pos->z - e->z; + return xd * xd + yd * yd + zd * zd; +} \ No newline at end of file diff --git a/Minecraft.World/HitResult.h b/Minecraft.World/HitResult.h new file mode 100644 index 00000000..efcfa75a --- /dev/null +++ b/Minecraft.World/HitResult.h @@ -0,0 +1,22 @@ +#pragma once +#include "Vec3.h" + +class HitResult +{ +public: + enum Type + { + TILE, ENTITY + }; + + Type type; + int x, y, z, f; + Vec3 *pos; + shared_ptr entity; + + HitResult(int x, int y, int z, int f, Vec3 *pos); + + HitResult(shared_ptr entity); + + double distanceTo(shared_ptr e); +}; \ No newline at end of file diff --git a/Minecraft.World/HoeItem.cpp b/Minecraft.World/HoeItem.cpp new file mode 100644 index 00000000..50dde305 --- /dev/null +++ b/Minecraft.World/HoeItem.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "ItemInstance.h" +#include "HoeItem.h" + +HoeItem::HoeItem(int id, const Tier *tier) : Item(id) +{ + this->tier = tier; + maxStackSize = 1; + setMaxDamage(tier->getUses()); +} + +bool HoeItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + if (!player->mayBuild(x, y, z)) return false; + + // 4J-PB - Adding a test only version to allow tooltips to be displayed + + int targetType = level->getTile(x, y, z); + // Material above = level.getMaterial(x, y + 1, z); + int above = level->getTile(x, y + 1, z); + + // 4J-PB - missing parentheses + if (face != 0 && above == 0 && (targetType == Tile::grass_Id || targetType == Tile::dirt_Id)) + { + if(!bTestUseOnOnly) + { + Tile *tile = Tile::farmland; + level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, tile->soundType->getStepSound(), (tile->soundType->getVolume() + 1) / 2, tile->soundType->getPitch() * 0.8f); + + if (level->isClientSide) return true; + level->setTile(x, y, z, tile->id); + instance->hurt(1, player); + } + return true; + } + + return false; +} + +bool HoeItem::isHandEquipped() +{ + return true; +} + +const Item::Tier *HoeItem::getTier() +{ + return tier; +} diff --git a/Minecraft.World/HoeItem.h b/Minecraft.World/HoeItem.h new file mode 100644 index 00000000..a4d015ac --- /dev/null +++ b/Minecraft.World/HoeItem.h @@ -0,0 +1,19 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class Player; + +class HoeItem : public Item +{ +protected: + const Tier *tier; +public: + HoeItem(int id, const Tier *tier); + + virtual bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + virtual bool isHandEquipped(); + + const Tier *getTier(); +}; \ No newline at end of file diff --git a/Minecraft.World/HouseFeature.cpp b/Minecraft.World/HouseFeature.cpp new file mode 100644 index 00000000..f8c5d173 --- /dev/null +++ b/Minecraft.World/HouseFeature.cpp @@ -0,0 +1,193 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "HouseFeature.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.item.h" + +bool HouseFeature::place(Level *level, Random *random, int x, int y, int z) +{ + while (y > 0 && !level->getMaterial(x, y - 1, z)->blocksMotion()) + y--; + + int w = random->nextInt(7) + 7; + int h = 4 + random->nextInt(3) / 2; + int d = random->nextInt(7) + 7; + + int x0 = x - w / 2; + int y0 = y; + int z0 = z - d / 2; + + int doorSide = random->nextInt(4); + if (doorSide < 2) d += 2; + else w += 2; + + for (int xx = x0; xx < x0 + w; xx++) + { + for (int zz = z0; zz < z0 + d; zz++) + { + Material *m = level->getMaterial(xx, y - 1, zz); + if (!m->blocksMotion() || m == Material::ice) return false; + + bool ok = false; + if (doorSide == 0 && xx < x0 + 2) ok = true; + if (doorSide == 1 && xx > x0 + w - 1 - 2) ok = true; + if (doorSide == 2 && zz < z0 + 2) ok = true; + if (doorSide == 3 && zz > z0 + d - 1 - 2) ok = true; + int t = level->getTile(xx, y, zz); + if (ok) + { + if (t != 0) return false; + } + else + { + if (t == Tile::stoneBrick_Id || t == Tile::mossStone_Id) return false; + } + + } + } + + if (doorSide == 0) + { + x0++; + w--; + } + else if (doorSide == 1) + { + w--; + } + else if (doorSide == 2) + { + z0++; + d--; + } + else if (doorSide == 3) + { + d--; + } + + int xx0 = x0; + int xx1 = x0 + w - 1; + int zz0 = z0; + int zz1 = z0 + d - 1; + if (doorSide >= 2) + { + xx0++; + xx1--; + } + else + { + zz0++; + zz1--; + } + for (int xx = x0; xx < x0 + w; xx++) + { + for (int zz = z0; zz < z0 + d; zz++) + { + int ho = h; + + int d1 = zz - z0; + int d2 = (z0 + d - 1) - zz; + if (doorSide < 2) + { + d1 = xx - x0; + d2 = (x0 + w - 1) - xx; + } + + if (d2 < d1) d1 = d2; + h += d1; + for (int yy = y0 - 1; yy < y0 + h; yy++) + { + int material = -1; + if (yy == y0 + h - 1) + { + material = Tile::wood_Id; + } + else if (xx >= xx0 && xx <= xx1 && zz >= zz0 && zz <= zz1) + { + material = 0; + if (yy == y0 - 1 || yy == y0 + h - 1 || xx == xx0 || zz == zz0 || xx == xx1 || zz == zz1) + { + if (yy <= y0 + random->nextInt(3)) material = Tile::mossStone_Id; + else material = Tile::stoneBrick_Id; + } + } + + if (material >= 0) + { + level->setTileNoUpdate(xx, yy, zz, material); + } + } + h = ho; + } + } + { + int xx = x0 + random->nextInt(w - 4) + 2; + int zz = z0 + random->nextInt(d - 4) + 2; + if (doorSide == 0) xx = x0; + if (doorSide == 1) xx = x0 + w - 1; + if (doorSide == 2) zz = z0; + if (doorSide == 3) zz = z0 + d - 1; + level->setTileNoUpdate(xx, y0, zz, 0); + level->setTileNoUpdate(xx, y0 + 1, zz, 0); + + int dir = 0; + if (doorSide == 0) dir = 0; + if (doorSide == 2) dir = 1; + if (doorSide == 1) dir = 2; + if (doorSide == 3) dir = 3; + + DoorItem::place(level, xx, y0, zz, dir, Tile::door_wood); + } + + for (int i = 0; i < (w * 2 + d * 2) * 3; i++) + { + int xx = x0 + random->nextInt(w - 4) + 2; + int zz = z0 + random->nextInt(d - 4) + 2; + int side = random->nextInt(4); + + if (side == 0) xx = xx0; + if (side == 1) xx = xx1; + if (side == 2) zz = zz0; + if (side == 3) zz = zz1; + + if (level->isSolidBlockingTile(xx, y0 + 1, zz)) + { + int count = 0; + if (level->isSolidBlockingTile(xx - 1, y0 + 1, zz) && level->isSolidBlockingTile(xx + 1, y0 + 1, zz)) count++; + if (level->isSolidBlockingTile(xx, y0 + 1, zz - 1) && level->isSolidBlockingTile(xx, y0 + 1, zz + 1)) count++; + if (count == 1) { + level->setTileNoUpdate(xx, y0 + 1, zz, Tile::glass_Id); + } + } + } + + int ww = xx1 - xx0; + int dd = zz1 - zz0; + for (int i = 0; i < (ww * 2 + dd * 2); i++) + { + int xx = xx0 + random->nextInt(ww - 1) + 1; + int zz = zz0 + random->nextInt(dd - 1) + 1; + int yy = y0; + + if (level->getTile(xx, yy + 2, zz) == 0) + { + int count = 0; + if (level->isSolidBlockingTile(xx - 1, yy + 2, zz)) count++; + if (level->isSolidBlockingTile(xx + 1, yy + 2, zz)) count++; + if (level->isSolidBlockingTile(xx, yy + 2, zz - 1)) count++; + if (level->isSolidBlockingTile(xx, yy + 2, zz + 1)) count++; + if (count == 1) + { + level->setTileNoUpdate(xx, y0 + 2, zz, Tile::torch_Id); + } + } + } + + shared_ptr(pz) = shared_ptr(new PigZombie(level)); + pz->moveTo(x0 + w / 2.0 + 0.5, y0 + 0.5, z0 + d / 2.0 + 0.5, 0, 0); + level->addEntity(pz); + + return true; + +} \ No newline at end of file diff --git a/Minecraft.World/HouseFeature.h b/Minecraft.World/HouseFeature.h new file mode 100644 index 00000000..110780ee --- /dev/null +++ b/Minecraft.World/HouseFeature.h @@ -0,0 +1,9 @@ +#pragma once +#include "Feature.h" +#include "Material.h" + +class HouseFeature : public Feature +{ +public: + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/HugeMushroomFeature.cpp b/Minecraft.World/HugeMushroomFeature.cpp new file mode 100644 index 00000000..f67cf971 --- /dev/null +++ b/Minecraft.World/HugeMushroomFeature.cpp @@ -0,0 +1,111 @@ +#include "stdafx.h" +#include "HugeMushroomFeature.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" + +HugeMushroomFeature::HugeMushroomFeature(int forcedType) : Feature(true) +{ + this->forcedType = forcedType; +} + +HugeMushroomFeature::HugeMushroomFeature() : Feature(false) +{ + this->forcedType = -1; +} + +bool HugeMushroomFeature::place(Level *level, Random *random, int x, int y, int z) +{ + int type = random->nextInt(2); + if (forcedType >= 0) type = forcedType; + + int treeHeight = random->nextInt(3) + 4; + + bool free = true; + if (y < 1 || y + treeHeight + 1 >= Level::maxBuildHeight) return false; + + for (int yy = y; yy <= y + 1 + treeHeight; yy++) + { + int r = 3; + if (yy <= (y + 3) ) r = 0; + for (int xx = x - r; xx <= x + r && free; xx++) + { + for (int zz = z - r; zz <= z + r && free; zz++) + { + if (yy >= 0 && yy < Level::maxBuildHeight) + { + int tt = level->getTile(xx, yy, zz); + if (tt != 0 && tt != Tile::leaves_Id) + { + free = false; + } + } + else + { + free = false; + } + } + } + } + + int belowTile = level->getTile(x, y - 1, z); + if (belowTile != Tile::dirt_Id && belowTile != Tile::grass_Id && belowTile != Tile::mycel_Id) + { + return false; + } + + if (!free) return false; + + //if (!Tile::mushroom1->mayPlace(level, x, y, z)) return false; + + //placeBlock(level, x, y - 1, z, Tile::dirt_Id, 0); + + int low = y + treeHeight; + if (type == 1) { + low = y + treeHeight - 3; + } + for (int yy = low; yy <= y + treeHeight; yy++) + { + int offs = 1; + if (yy < y + treeHeight) offs += 1; + if (type == 0) offs = 3; + for (int xx = x - offs; xx <= x + offs; xx++) + { + for (int zz = z - offs; zz <= z + offs; zz++) + { + int data = 5; + if (xx == x - offs) data--; + if (xx == x + offs) data++; + if (zz == z - offs) data -= 3; + if (zz == z + offs) data += 3; + + if (type == 0 || yy < y + treeHeight) + { + if ((xx == x - offs || xx == x + offs) && (zz == z - offs || zz == z + offs)) continue; + if (xx == x - (offs - 1) && zz == z - offs) data = 1; + if (xx == x - offs && zz == z - (offs - 1)) data = 1; + + if (xx == x + (offs - 1) && zz == z - offs) data = 3; + if (xx == x + offs && zz == z - (offs - 1)) data = 3; + + if (xx == x - (offs - 1) && zz == z + offs) data = 7; + if (xx == x - offs && zz == z + (offs - 1)) data = 7; + + if (xx == x + (offs - 1) && zz == z + offs) data = 9; + if (xx == x + offs && zz == z + (offs - 1)) data = 9; + } + + if (data == 5 && yy < y + treeHeight) data = 0; + if (data != 0 || y >= y + treeHeight - 1) + { + if (!Tile::solid[level->getTile(xx, yy, zz)]) placeBlock(level, xx, yy, zz, Tile::hugeMushroom1_Id + type, data); + } + } + } + } + for (int hh = 0; hh < treeHeight; hh++) + { + int t = level->getTile(x, y + hh, z); + if (!Tile::solid[t]) placeBlock(level, x, y + hh, z, Tile::hugeMushroom1_Id + type, 10); + } + return true; +} diff --git a/Minecraft.World/HugeMushroomFeature.h b/Minecraft.World/HugeMushroomFeature.h new file mode 100644 index 00000000..6523c5fb --- /dev/null +++ b/Minecraft.World/HugeMushroomFeature.h @@ -0,0 +1,13 @@ +#pragma once +#include "Feature.h" + +class HugeMushroomFeature : public Feature +{ +private: + int forcedType; + +public: + HugeMushroomFeature(int forcedType); + HugeMushroomFeature(); + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; diff --git a/Minecraft.World/HugeMushroomTile.cpp b/Minecraft.World/HugeMushroomTile.cpp new file mode 100644 index 00000000..9b8344c0 --- /dev/null +++ b/Minecraft.World/HugeMushroomTile.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "net.minecraft.world.h" +#include "HugeMushroomTile.h" + +const wstring HugeMushroomTile::TEXTURE_STEM = L"mushroom_skin_stem"; +const wstring HugeMushroomTile::TEXTURE_INSIDE = L"mushroom_inside"; +const wstring HugeMushroomTile::TEXTURE_TYPE[] = {L"mushroom_skin_brown", L"mushroom_skin_red"}; + +HugeMushroomTile::HugeMushroomTile(int id, Material *material, int type) : Tile(id, material) +{ + this->type = type; + icons = NULL; + iconStem = NULL; + iconInside = NULL; +} + +Icon *HugeMushroomTile::getTexture(int face, int data) +{ + // 123 + // 456 10 + // 789 + if (data == 10 && face > 1) return iconStem; + if (data >= 1 && data <= 9 && face == 1) return icons[type]; + if (data >= 1 && data <= 3 && face == 2) return icons[type]; + if (data >= 7 && data <= 9 && face == 3) return icons[type]; + + if ((data == 1 || data == 4 || data == 7) && face == 4) return icons[type]; + if ((data == 3 || data == 6 || data == 9) && face == 5) return icons[type]; + + // two special cases requested by rhodox (painterly pack) + if (data == 14) + { + return icons[type]; + } + if (data == 15) + { + return iconStem; + } + + return iconInside; +} + +int HugeMushroomTile::getResourceCount(Random *random) +{ + int count = random->nextInt(10) - 7; + if (count < 0) count = 0; + return count; +} + +int HugeMushroomTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::mushroom1_Id + type; +} + +int HugeMushroomTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Tile::mushroom1_Id + type; +} + +void HugeMushroomTile::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[HUGE_MUSHROOM_TEXTURE_COUNT]; + + for (int i = 0; i < HUGE_MUSHROOM_TEXTURE_COUNT; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURE_TYPE[i]); + } + + iconInside = iconRegister->registerIcon(TEXTURE_INSIDE); + iconStem = iconRegister->registerIcon(TEXTURE_STEM); +} \ No newline at end of file diff --git a/Minecraft.World/HugeMushroomTile.h b/Minecraft.World/HugeMushroomTile.h new file mode 100644 index 00000000..2aa22fbb --- /dev/null +++ b/Minecraft.World/HugeMushroomTile.h @@ -0,0 +1,26 @@ +#pragma once +#include "Tile.h" + +class ChunkRebuildData; +class HugeMushroomTile : public Tile +{ + friend class ChunkRebuildData; +public: + static const wstring TEXTURE_STEM; + static const wstring TEXTURE_INSIDE; + +private: + static const int HUGE_MUSHROOM_TEXTURE_COUNT = 2; + static const wstring TEXTURE_TYPE[]; + int type; + Icon **icons; + Icon *iconStem; + Icon *iconInside; +public: + HugeMushroomTile(int id, Material *material, int type); + Icon *getTexture(int face, int data); + int getResourceCount(Random *random); + int getResource(int data, Random *random, int playerBonusLevel); + int cloneTileId(Level *level, int x, int y, int z); + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/HurtByTargetGoal.cpp b/Minecraft.World/HurtByTargetGoal.cpp new file mode 100644 index 00000000..a146c47d --- /dev/null +++ b/Minecraft.World/HurtByTargetGoal.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "HurtByTargetGoal.h" + +HurtByTargetGoal::HurtByTargetGoal(Mob *mob, bool alertSameType) : TargetGoal(mob, 16, false) +{ + this->alertSameType = alertSameType; + setRequiredControlFlags(TargetGoal::TargetFlag); +} + +bool HurtByTargetGoal::canUse() +{ + return canAttack(mob->getLastHurtByMob(), false); +} + +void HurtByTargetGoal::start() +{ + mob->setTarget(mob->getLastHurtByMob()); + oldHurtByMob = mob->getLastHurtByMob(); + + if (alertSameType) + { + vector > *nearby = mob->level->getEntitiesOfClass(typeid(*mob), AABB::newTemp(mob->x, mob->y, mob->z, mob->x + 1, mob->y + 1, mob->z + 1)->grow(within, 4, within)); + for(AUTO_VAR(it, nearby->begin()); it != nearby->end(); ++it) + { + shared_ptr other = dynamic_pointer_cast(*it); + if (this->mob->shared_from_this() == other) continue; + if (other->getTarget() != NULL) continue; + other->setTarget(mob->getLastHurtByMob()); + } + delete nearby; + } + + TargetGoal::start(); +} + +void HurtByTargetGoal::tick() +{ + if (mob->getLastHurtByMob() != NULL && mob->getLastHurtByMob() != oldHurtByMob) + { + this->start(); + } +} diff --git a/Minecraft.World/HurtByTargetGoal.h b/Minecraft.World/HurtByTargetGoal.h new file mode 100644 index 00000000..4c6ee5fe --- /dev/null +++ b/Minecraft.World/HurtByTargetGoal.h @@ -0,0 +1,17 @@ +#pragma once + +#include "TargetGoal.h" + +class HurtByTargetGoal : public TargetGoal +{ +private: + bool alertSameType; + shared_ptr oldHurtByMob; + +public: + HurtByTargetGoal(Mob *mob, bool alertSameType); + + bool canUse(); + void start(); + void tick(); +}; diff --git a/Minecraft.World/I18n.cpp b/Minecraft.World/I18n.cpp new file mode 100644 index 00000000..81d3dff5 --- /dev/null +++ b/Minecraft.World/I18n.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "Language.h" +#include "I18n.h" + +Language *I18n::lang = Language::getInstance(); +wstring I18n::get(const wstring& id, ...) +{ +#ifdef __PSVITA__ // 4J - vita doesn't like having a reference type as the last parameter passed to va_start - we shouldn't need this method anyway + return L""; +#elif _MSC_VER >= 1930 // VS2022+ also disallows va_start with reference types + return id; +#else + va_list va; + va_start(va, id); + return I18n::get(id, va); +#endif +} + +wstring I18n::get(const wstring& id, va_list args) +{ + return lang->getElement(id, args); +} diff --git a/Minecraft.World/I18n.h b/Minecraft.World/I18n.h new file mode 100644 index 00000000..0a43fe20 --- /dev/null +++ b/Minecraft.World/I18n.h @@ -0,0 +1,15 @@ +#pragma once +using namespace std; + +#include "stdafx.h" +#include "Language.h" + +class I18n +{ +private: + static Language *lang; + +public: + static wstring get(const wstring& id, ...); + static wstring get(const wstring& id, va_list args); +}; \ No newline at end of file diff --git a/Minecraft.World/IceBiome.cpp b/Minecraft.World/IceBiome.cpp new file mode 100644 index 00000000..3156d869 --- /dev/null +++ b/Minecraft.World/IceBiome.cpp @@ -0,0 +1,6 @@ +#include "stdafx.h" +#include "IceBiome.h" + +IceBiome::IceBiome(int id) : Biome(id) +{ +}; \ No newline at end of file diff --git a/Minecraft.World/IceBiome.h b/Minecraft.World/IceBiome.h new file mode 100644 index 00000000..306ba14f --- /dev/null +++ b/Minecraft.World/IceBiome.h @@ -0,0 +1,8 @@ +#pragma once +#include "Biome.h" + +class IceBiome : public Biome +{ +public: + IceBiome(int id); +}; \ No newline at end of file diff --git a/Minecraft.World/IceTile.cpp b/Minecraft.World/IceTile.cpp new file mode 100644 index 00000000..6ca9cc80 --- /dev/null +++ b/Minecraft.World/IceTile.cpp @@ -0,0 +1,83 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.food.h" +#include "net.minecraft.stats.h" +#include "IceTile.h" + +IceTile::IceTile(int id) : HalfTransparentTile(id, L"ice", Material::ice, false) +{ + friction = 0.98f; + setTicking(true); +} + +int IceTile::getRenderLayer() +{ + return 1; +} + +bool IceTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + return HalfTransparentTile::shouldRenderFace(level, x, y, z, 1 - face); +} + +void IceTile::playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data) +{ + player->awardStat(GenericStats::blocksMined(id), GenericStats::param_blocksMined(id,data,1) ); + player->causeFoodExhaustion(FoodConstants::EXHAUSTION_MINE); + + if (isSilkTouchable() && EnchantmentHelper::hasSilkTouch(player->inventory)) + { + shared_ptr item = getSilkTouchItemInstance(data); + if (item != NULL) + { + popResource(level, x, y, z, item); + } + } + else + { + if (level->dimension->ultraWarm) + { + level->setTile(x, y, z, 0); + return; + } + + int playerBonusLevel = EnchantmentHelper::getDiggingLootBonus(player->inventory); + spawnResources(level, x, y, z, data, playerBonusLevel); + Material *below = level->getMaterial(x, y - 1, z); + if (below->blocksMotion() || below->isLiquid()) + { + level->setTile(x, y, z, Tile::water_Id); + } + } +} + +int IceTile::getResourceCount(Random *random) +{ + return 0; +} + +void IceTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (level->getBrightness(LightLayer::Block, x, y, z) > 11 - Tile::lightBlock[id]) + { + if (level->dimension->ultraWarm) + { + level->setTile(x, y, z, 0); + return; + } + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, Tile::calmWater_Id); + } +} + +bool IceTile::shouldTileTick(Level *level, int x,int y,int z) +{ + return level->getBrightness(LightLayer::Block, x, y, z) > 11 - Tile::lightBlock[id]; +} + +int IceTile::getPistonPushReaction() +{ + return Material::PUSH_NORMAL; +} diff --git a/Minecraft.World/IceTile.h b/Minecraft.World/IceTile.h new file mode 100644 index 00000000..b3f1a5e6 --- /dev/null +++ b/Minecraft.World/IceTile.h @@ -0,0 +1,19 @@ +#pragma once +#include "HalfTransparentTile.h" + +class Random; + +class IceTile : public HalfTransparentTile +{ +public: + IceTile(int id); + virtual int getRenderLayer(); + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + virtual void playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data); + virtual int getResourceCount(Random *random); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual int getPistonPushReaction(); + + // 4J Added so we can check before we try to add a tile to the tick list if it's actually going to do seomthing + virtual bool shouldTileTick(Level *level, int x,int y,int z); +}; diff --git a/Minecraft.World/Icon.h b/Minecraft.World/Icon.h new file mode 100644 index 00000000..1435c7b0 --- /dev/null +++ b/Minecraft.World/Icon.h @@ -0,0 +1,34 @@ +#pragma once +using namespace std; + + +class Icon +{ +public: + static const int TYPE_TERRAIN = 0; + static const int TYPE_ITEM = 1; + + static const int IS_GRASS_SIDE = 1; + static const int IS_GRASS_TOP = 2; + +#ifdef __PSVITA__ + // AP - alpha cut out is expensive on vita so we mark which icons require it + static const int IS_ALPHA_CUT_OUT = 4; +#endif + + virtual int getX() const = 0; + virtual int getY() const = 0; + virtual int getWidth() const = 0; + virtual int getHeight() const = 0; + virtual float getU0(bool adjust = false) const = 0; + virtual float getU1(bool adjust = false) const = 0; + virtual float getU(double offset, bool adjust = false) const = 0; + virtual float getV0(bool adjust = false) const = 0; + virtual float getV1(bool adjust = false) const = 0; + virtual float getV(double offset, bool adjust = false) const = 0; + virtual wstring getName() const = 0; + virtual int getSourceWidth() const = 0; + virtual int getSourceHeight() const = 0; + virtual int getFlags() const = 0; // 4J added + virtual void setFlags(int flags) = 0; // 4J added +}; diff --git a/Minecraft.World/IconRegister.h b/Minecraft.World/IconRegister.h new file mode 100644 index 00000000..3ea80bb8 --- /dev/null +++ b/Minecraft.World/IconRegister.h @@ -0,0 +1,12 @@ +#pragma once +using namespace std; + +class Icon; + +class IconRegister +{ +public: + // 4J Stu - register is a reserved keyword in C++ + virtual Icon *registerIcon(const wstring &name) = 0; + virtual int getIconType() = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/ImprovedNoise.cpp b/Minecraft.World/ImprovedNoise.cpp new file mode 100644 index 00000000..8be9e385 --- /dev/null +++ b/Minecraft.World/ImprovedNoise.cpp @@ -0,0 +1,216 @@ +#include "stdafx.h" +#include "ImprovedNoise.h" + +ImprovedNoise::ImprovedNoise() +{ + Random random; + init(&random); +} + +ImprovedNoise::ImprovedNoise(Random *random) +{ + init(random); +} + +void ImprovedNoise::init(Random *random) +{ + p = new int[512]; + + xo = random->nextDouble() * 256; + yo = random->nextDouble() * 256; + zo = random->nextDouble() * 256; + for (int i = 0; i < 256; i++) + { + p[i] = i; + } + + for (int i = 0; i < 256; i++) + { + int j = random->nextInt(256 - i) + i; + int tmp = p[i]; + p[i] = p[j]; + p[j] = tmp; + + p[i + 256] = p[i]; + } +} + +ImprovedNoise::~ImprovedNoise() +{ + delete [] p; +} + +double ImprovedNoise::noise(double _x, double _y, double _z) +{ + double x = _x + xo; + double y = _y + yo; + double z = _z + zo; + + int xf = (int) x; + int yf = (int) y; + int zf = (int) z; + + if (x < xf) xf--; + if (y < yf) yf--; + if (z < zf) zf--; + + int X = xf & 255, // FIND UNIT CUBE THAT + Y = yf & 255, // CONTAINS POINT. + Z = zf & 255; + + x -= xf; // FIND RELATIVE X,Y,Z + y -= yf; // OF POINT IN CUBE. + z -= zf; + + double u = x * x * x * (x * (x * 6 - 15) + 10), // COMPUTE FADE CURVES + v = y * y * y * (y * (y * 6 - 15) + 10), // FOR EACH OF X,Y,Z. + w = z * z * z * (z * (z * 6 - 15) + 10); + + int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z, // HASH COORDINATES OF + B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z; // THE 8 CUBE CORNERS, + + return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), // AND ADD + grad(p[BA], x - 1, y, z)), // BLENDED + lerp(u, grad(p[AB], x, y - 1, z), // RESULTS + grad(p[BB], x - 1, y - 1, z))),// FROM 8 + lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), // CORNERS + grad(p[BA + 1], x - 1, y, z - 1)), // OF CUBE + lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1)))); + +} + +double ImprovedNoise::lerp(double t, double a, double b) +{ + return a + t * (b - a); +} + +double ImprovedNoise::grad2(int hash, double x, double z) +{ + int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE + + double u = (1-((h&8)>>3))*x, // INTO 12 GRADIENT DIRECTIONS. + v = h < 4 ? 0 : h == 12 || h == 14 ? x : z; + + return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); +} + +double ImprovedNoise::grad(int hash, double x, double y, double z) +{ + int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE + + double u = h < 8 ? x : y, // INTO 12 GRADIENT DIRECTIONS. + v = h < 4 ? y : h == 12 || h == 14 ? x : z; + + return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); +} + +double ImprovedNoise::getValue(double x, double y) +{ + return noise(x, y, 0); +} + +double ImprovedNoise::getValue(double x, double y, double z) +{ + return noise(x, y, z); +} + +void ImprovedNoise::add(doubleArray buffer, double _x, double _y, double _z, int xSize, int ySize, int zSize, double xs, double ys, double zs, double pow) +{ + if (ySize==1) + { + int A = 0, AA = 0, B = 0, BA = 0; + double vv0 = 0, vv2 = 0; + int pp = 0; + double scale = 1.0 / pow; + for (int xx = 0; xx < xSize; xx++) + { + double x = _x + (xx) * xs + xo; + int xf = (int) x; + if (x < xf) xf--; + int X = xf & 255; + x -= xf; + double u = x * x * x * (x * (x * 6 - 15) + 10); + + for (int zz = 0; zz < zSize; zz++) + { + double z = _z + (zz) * zs + zo; + int zf = (int) z; + if (z < zf) zf--; + int Z = zf & 255; + z -= zf; + double w = z * z * z * (z * (z * 6 - 15) + 10); + + A = p[X] + 0; + AA = p[A] + Z; + B = p[X + 1] + 0; + BA = p[B] + Z; + vv0 = lerp(u, grad2(p[AA], x, z), grad(p[BA], x - 1, 0, z)); + vv2 = lerp(u, grad(p[AA + 1], x, 0, z - 1), grad(p[BA + 1], x - 1, 0, z - 1)); + + double val = lerp(w, vv0, vv2); + + buffer[pp++] += val * scale; + } + } + return; + } + int pp = 0; + double scale = 1 / pow; + int yOld = -1; + int A = 0, AA = 0, AB = 0, B = 0, BA = 0, BB = 0; + double vv0 = 0, vv1 = 0, vv2 = 0, vv3 = 0; + + for (int xx = 0; xx < xSize; xx++) + { + double x = _x + (xx) * xs + xo; + int xf = (int) x; + if (x < xf) xf--; + int X = xf & 255; + x -= xf; + double u = x * x * x * (x * (x * 6 - 15) + 10); + + + for (int zz = 0; zz < zSize; zz++) + { + double z = _z + (zz) * zs + zo; + int zf = (int) z; + if (z < zf) zf--; + int Z = zf & 255; + z -= zf; + double w = z * z * z * (z * (z * 6 - 15) + 10); + + + for (int yy = 0; yy < ySize; yy++) + { + double y = _y + (yy) * ys + yo; + int yf = (int) y; + if (y < yf) yf--; + int Y = yf & 255; + y -= yf; + double v = y * y * y * (y * (y * 6 - 15) + 10); + + if (yy == 0 || Y != yOld) + { + yOld = Y; + A = p[X] + Y; + AA = p[A] + Z; + AB = p[A + 1] + Z; + B = p[X + 1] + Y; + BA = p[B] + Z; + BB = p[B + 1] + Z; + vv0 = lerp(u, grad(p[AA], x, y, z), grad(p[BA], x - 1, y, z)); + vv1 = lerp(u, grad(p[AB], x, y - 1, z), grad(p[BB], x - 1, y - 1, z)); + vv2 = lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)); + vv3 = lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1)); + } + + + double v0 = lerp(v, vv0, vv1); + double v1 = lerp(v, vv2, vv3); + double val = lerp(w, v0, v1); + + buffer[pp++] += val * scale; + } + } + } +} diff --git a/Minecraft.World/ImprovedNoise.h b/Minecraft.World/ImprovedNoise.h new file mode 100644 index 00000000..b35bb3fd --- /dev/null +++ b/Minecraft.World/ImprovedNoise.h @@ -0,0 +1,29 @@ +#pragma once +#include "Synth.h" + +class ImprovedNoise : public Synth +{ + friend class PerlinNoise_SPU; +private: + int *p; + +public: + double scale; + double xo, yo, zo; + + ImprovedNoise(); + ImprovedNoise(Random *random); + void init(Random *random); + + ~ImprovedNoise(); + + double noise(double _x, double _y, double _z); + + double lerp(double t, double a, double b); + double grad2(int hash, double x, double z); + double grad(int hash, double x, double y, double z); + virtual double getValue(double x, double y); + double getValue(double x, double y, double z); + + void add(doubleArray buffer, double _x, double _y, double _z, int xSize, int ySize, int zSize, double xs, double ys, double zs, double pow); +}; diff --git a/Minecraft.World/IndirectEntityDamageSource.cpp b/Minecraft.World/IndirectEntityDamageSource.cpp new file mode 100644 index 00000000..01c54ed6 --- /dev/null +++ b/Minecraft.World/IndirectEntityDamageSource.cpp @@ -0,0 +1,48 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.network.packet.h" + +//IndirectEntityDamageSource::IndirectEntityDamageSource(const wstring &msgId, shared_ptr entity, shared_ptr owner) : EntityDamageSource(msgId, entity) +IndirectEntityDamageSource::IndirectEntityDamageSource(ChatPacket::EChatPacketMessage msgId, shared_ptr entity, shared_ptr owner) : EntityDamageSource(msgId, entity) +{ + this->owner = owner; +} + +// 4J Stu - Brought forward from 1.2.3 to fix #46422 +shared_ptr IndirectEntityDamageSource::getDirectEntity() +{ + return entity; +} + +shared_ptr IndirectEntityDamageSource::getEntity() +{ + return owner; +} + +//wstring IndirectEntityDamageSource::getLocalizedDeathMessage(shared_ptr player) +//{ +// return L"death." + msgId + player->name + owner->getAName(); +// //return I18n.get("death." + msgId, player.name, owner.getAName()); +//} + +shared_ptr IndirectEntityDamageSource::getDeathMessagePacket(shared_ptr player) +{ + wstring additional = L""; + int type; + if(owner != NULL) + { + type = owner->GetType(); + if(type == eTYPE_SERVERPLAYER) + { + shared_ptr sourcePlayer = dynamic_pointer_cast(owner); + if(sourcePlayer != NULL) additional = sourcePlayer->name; + } + } + else + { + type = entity->GetType(); + } + return shared_ptr( new ChatPacket(player->name, m_msgId, type, additional ) ); +} \ No newline at end of file diff --git a/Minecraft.World/IndirectEntityDamageSource.h b/Minecraft.World/IndirectEntityDamageSource.h new file mode 100644 index 00000000..b7aec18c --- /dev/null +++ b/Minecraft.World/IndirectEntityDamageSource.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "EntityDamageSource.h" + +class Entity; +class Player; + +class IndirectEntityDamageSource : public EntityDamageSource +{ +private: + shared_ptr owner; + +public: + //IndirectEntityDamageSource(const wstring &msgId, shared_ptr entity, shared_ptr owner); + IndirectEntityDamageSource(ChatPacket::EChatPacketMessage msgId, shared_ptr entity, shared_ptr owner); + virtual ~IndirectEntityDamageSource() { } + + virtual shared_ptr getDirectEntity(); // 4J Stu - Brought forward from 1.2.3 to fix #46422 + virtual shared_ptr getEntity(); + + // 4J Stu - Made return a packet + //virtual wstring getLocalizedDeathMessage(shared_ptr player); + virtual shared_ptr getDeathMessagePacket(shared_ptr player); +}; \ No newline at end of file diff --git a/Minecraft.World/InputOutputStream.h b/Minecraft.World/InputOutputStream.h new file mode 100644 index 00000000..1708c213 --- /dev/null +++ b/Minecraft.World/InputOutputStream.h @@ -0,0 +1,20 @@ +#pragma once + +#include "InputStream.h" +#include "ByteArrayInputStream.h" +#include "DataInput.h" +#include "DataInputStream.h" +#include "FileInputStream.h" +#include "GZIPInputStream.h" + +#include "OutputStream.h" +#include "ByteArrayOutputStream.h" +#include "DataOutput.h" +#include "BufferedOutputStream.h" +#include "DataOutputStream.h" +#include "FileOutputStream.h" +#include "GZIPOutputStream.h" + +#include "Reader.h" +#include "BufferedReader.h" +#include "InputStreamReader.h" \ No newline at end of file diff --git a/Minecraft.World/InputStream.cpp b/Minecraft.World/InputStream.cpp new file mode 100644 index 00000000..dbec82cc --- /dev/null +++ b/Minecraft.World/InputStream.cpp @@ -0,0 +1,9 @@ +#include "stdafx.h" +#include "File.h" +#include "InputOutputStream.h" +#include "InputStream.h" + +InputStream *InputStream::getResourceAsStream(const wstring &fileName) +{ + return new FileInputStream( File( fileName ) ); +} \ No newline at end of file diff --git a/Minecraft.World/InputStream.h b/Minecraft.World/InputStream.h new file mode 100644 index 00000000..af0ff593 --- /dev/null +++ b/Minecraft.World/InputStream.h @@ -0,0 +1,17 @@ +#pragma once +using namespace std; +// 4J Stu - Represents Java standard lib abstract + +class InputStream +{ +public: + virtual ~InputStream() {} + + virtual int read() = 0; + virtual int read(byteArray b) = 0; + virtual int read(byteArray b, unsigned int offset, unsigned int length) = 0; + virtual void close() = 0; + virtual __int64 skip(__int64 n) = 0; + + static InputStream *getResourceAsStream(const wstring &fileName); +}; \ No newline at end of file diff --git a/Minecraft.World/InputStreamReader.cpp b/Minecraft.World/InputStreamReader.cpp new file mode 100644 index 00000000..4a565cf4 --- /dev/null +++ b/Minecraft.World/InputStreamReader.cpp @@ -0,0 +1,52 @@ +#include "stdafx.h" + +#include "InputStream.h" +#include "DataInputStream.h" +#include "InputStreamReader.h" + +//Creates an InputStreamReader that uses the default charset. +//Parameters: +//in - An InputStream +InputStreamReader::InputStreamReader(InputStream *in) : stream( new DataInputStream( in ) ) +{ +} + +//Closes the stream and releases any system resources associated with it. +//Once the stream has been closed, further read(), ready(), mark(), reset(), or skip() invocations will throw an IOException. +//Closing a previously closed stream has no effect. +void InputStreamReader::close() +{ + stream->close(); +} + +//Reads a single character. +//Returns: +//The character read, or -1 if the end of the stream has been reached +int InputStreamReader::read() +{ + return stream->readUTFChar(); +} + +//Reads characters into a portion of an array. +//Parameters: +//cbuf - Destination buffer +//offset - Offset at which to start storing characters +//length - Maximum number of characters to read +//Returns: +//The number of characters read, or -1 if the end of the stream has been reached +int InputStreamReader::read(wchar_t cbuf[], unsigned int offset, unsigned int length) +{ + unsigned int charsRead = 0; + for( unsigned int i = offset; i < offset + length; i++ ) + { + wchar_t value = (wchar_t)stream->readUTFChar(); + if( value != -1 ) + { + cbuf[i] = value; + charsRead++; + } + // TODO 4J Stu - The read might throw an exception? In which case we should return -1 + else break; + } + return charsRead; +} \ No newline at end of file diff --git a/Minecraft.World/InputStreamReader.h b/Minecraft.World/InputStreamReader.h new file mode 100644 index 00000000..d26efb0f --- /dev/null +++ b/Minecraft.World/InputStreamReader.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Reader.h" + +class InputStream; + +class InputStreamReader : public Reader +{ +private: + DataInputStream *stream; + +public: + InputStreamReader(InputStream *in); + + virtual void close(); + virtual int read(); + virtual int read(wchar_t cbuf[], unsigned int offset, unsigned int length); +}; \ No newline at end of file diff --git a/Minecraft.World/InstantenousMobEffect.cpp b/Minecraft.World/InstantenousMobEffect.cpp new file mode 100644 index 00000000..0aca1ec4 --- /dev/null +++ b/Minecraft.World/InstantenousMobEffect.cpp @@ -0,0 +1,16 @@ +#include "stdafx.h" +#include "net.minecraft.world.effect.h" + +InstantenousMobEffect::InstantenousMobEffect(int id, bool isHarmful, eMinecraftColour color) : MobEffect(id, isHarmful, color) +{ +} + +bool InstantenousMobEffect::isInstantenous() +{ + return true; +} + +bool InstantenousMobEffect::isDurationEffectTick(int remainingDuration, int amplification) +{ + return remainingDuration >= 1; +} \ No newline at end of file diff --git a/Minecraft.World/InstantenousMobEffect.h b/Minecraft.World/InstantenousMobEffect.h new file mode 100644 index 00000000..8da32820 --- /dev/null +++ b/Minecraft.World/InstantenousMobEffect.h @@ -0,0 +1,11 @@ +#pragma once + +#include "MobEffect.h" + +class InstantenousMobEffect : public MobEffect +{ +public: + InstantenousMobEffect(int id, bool isHarmful, eMinecraftColour color); + bool isInstantenous(); + bool isDurationEffectTick(int remainingDuration, int amplification); +}; \ No newline at end of file diff --git a/Minecraft.World/IntArrayTag.h b/Minecraft.World/IntArrayTag.h new file mode 100644 index 00000000..58339bf6 --- /dev/null +++ b/Minecraft.World/IntArrayTag.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Tag.h" +#include "System.h" + +class IntArrayTag : public Tag +{ +public: + intArray data; + + IntArrayTag(const wstring &name) : Tag(name) + { + } + + IntArrayTag(const wstring &name, intArray data) : Tag(name) + { + this->data = data; + } + + void write(DataOutput *dos) + { + dos->writeInt(data.length); + for (unsigned int i = 0; i < data.length; i++) + { + dos->writeInt(data[i]); + } + } + + void load(DataInput *dis) + { + int length = dis->readInt(); + + if ( data.data ) delete[] data.data; + data = intArray(length); + for (int i = 0; i < length; i++) + { + data[i] = dis->readInt(); + } + } + + byte getId() { return TAG_Int_Array; } + + wstring toString() + { + static wchar_t buf[32]; + swprintf(buf, 32, L"[%d bytes]",data.length); + return wstring( buf ); + } + + bool equals(Tag *obj) + { + if (Tag::equals(obj)) + { + IntArrayTag *o = (IntArrayTag *) obj; + return ((data.data == NULL && o->data.data == NULL) || (data.data != NULL && data.length == o->data.length && memcmp(data.data, o->data.data, data.length) == 0) ); + } + return false; + } + + Tag *copy() + { + intArray cp = intArray(data.length); + System::arraycopy(data, 0, &cp, 0, data.length); + return new IntArrayTag(getName(), cp); + } +}; \ No newline at end of file diff --git a/Minecraft.World/IntBuffer.cpp b/Minecraft.World/IntBuffer.cpp new file mode 100644 index 00000000..1bb5e26a --- /dev/null +++ b/Minecraft.World/IntBuffer.cpp @@ -0,0 +1,113 @@ +#include "stdafx.h" + +#include "IntBuffer.h" + +//Allocates a new int buffer. +//The new buffer's position will be zero, its limit will be its capacity, and its mark will be undefined. +//It will have a backing array, and its array offset will be zero. +// +//Parameters: +//capacity - The new buffer's capacity, in ints +IntBuffer::IntBuffer(unsigned int capacity) : Buffer( capacity ) +{ + buffer = new int[capacity]; + memset( buffer,0,sizeof(int)*capacity); +} + +IntBuffer::IntBuffer( unsigned int capacity, int *backingArray ) : Buffer( capacity ) +{ + hasBackingArray = true; + buffer = backingArray; +} + +IntBuffer::~IntBuffer() +{ + if( !hasBackingArray ) + delete[] buffer; +} + +int *IntBuffer::getBuffer() +{ + return buffer; +} + +//Flips this buffer. The limit is set to the current position and then the position is set to zero. +//If the mark is defined then it is discarded. +// +//Returns: +//This buffer +IntBuffer *IntBuffer::flip() +{ + m_limit = m_position; + m_position = 0; + return this; +} + +//Absolute get method. Reads the int at the given index. +//Parameters: +//index - The index from which the int will be read +//Returns: +//The int at the given index +int IntBuffer::get(unsigned int index) +{ + assert( index < m_limit ); + + return buffer[index]; +} + +//Relative bulk put method (optional operation). +//This method transfers ints into this buffer from the given source array. +//If there are more ints to be copied from the array than remain in this buffer, that is, if length > remaining(), +//then no ints are transferred and a BufferOverflowException is thrown. +// +//Otherwise, this method copies length ints from the given array into this buffer, starting at the given offset in the array +//and at the current position of this buffer. The position of this buffer is then incremented by length. +// +//In other words, an invocation of this method of the form dst.put(src, off, len) has exactly the same effect as the loop +// +// for (int i = off; i < off + len; i++) +// dst.put(a[i]); +//except that it first checks that there is sufficient space in this buffer and it is potentially much more efficient. +//Parameters: +//src - The array from which ints are to be read +//offset - The offset within the array of the first int to be read; must be non-negative and no larger than array.length +//length - The number of ints to be read from the given array; must be non-negative and no larger than array.length - offset +//Returns: +//This buffer +IntBuffer *IntBuffer::put(intArray *inputArray, unsigned int offset, unsigned int length) +{ + assert( offset + length < inputArray->length ); + + std::copy( inputArray->data +offset, inputArray->data +offset+length, buffer+m_position ); + + m_position += length; + + return this; +} + +IntBuffer *IntBuffer::put(intArray inputArray) +{ + if( inputArray.length > remaining() ) + assert( false ); //TODO 4J Stu - Some kind of exception? + + std::copy( inputArray.data, inputArray.data + inputArray.length, buffer+m_position ); + + m_position += inputArray.length; + + return this; +} + +//Writes the given int into this buffer at the current position, and then increments the position. +// +//Parameters: +//i - The int to be written +//Returns: +//This buffer +IntBuffer *IntBuffer::put(int i) +{ + assert( m_position < m_limit ); + + buffer[m_position++] = i; + + return this; +} \ No newline at end of file diff --git a/Minecraft.World/IntBuffer.h b/Minecraft.World/IntBuffer.h new file mode 100644 index 00000000..0d5d304c --- /dev/null +++ b/Minecraft.World/IntBuffer.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Buffer.h" + +class IntBuffer : public Buffer +{ +private: + int *buffer; + +public: + IntBuffer(unsigned int capacity); + IntBuffer( unsigned int capacity, int *backingArray ); + virtual ~IntBuffer(); + + virtual IntBuffer *flip(); + int get(unsigned int index); + int *getBuffer(); + IntBuffer *put(intArray *inputArray, unsigned int offset, unsigned int length); + IntBuffer *put(intArray inputArray); + IntBuffer *put(int i); +}; \ No newline at end of file diff --git a/Minecraft.World/IntCache.cpp b/Minecraft.World/IntCache.cpp new file mode 100644 index 00000000..174d01d3 --- /dev/null +++ b/Minecraft.World/IntCache.cpp @@ -0,0 +1,164 @@ +#include "stdafx.h" +#include "IntCache.h" + +DWORD IntCache::tlsIdx = TlsAlloc(); + +void IntCache::CreateNewThreadStorage() +{ + ThreadStorage *tls = new ThreadStorage(); + TlsSetValue(tlsIdx, (void *)tls); + tls->maxSize = TINY_CUTOFF; +} + +IntCache::ThreadStorage::~ThreadStorage() +{ + for(unsigned int i = 0; i < tcache.size(); i++ ) + { + delete [] tcache[i].data; + } + for(unsigned int i = 0; i < tallocated.size(); i++ ) + { + delete [] tallocated[i].data; + } + for(unsigned int i = 0; i < cache.size(); i++ ) + { + delete [] cache[i].data; + } + for(unsigned int i = 0; i < allocated.size(); i++ ) + { + delete [] allocated[i].data; + } + for( int i = 0; i < toosmall.size(); i++ ) + { + delete [] toosmall[i].data; + } +} + +void IntCache::ReleaseThreadStorage() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); + + + delete tls; +} + +intArray IntCache::allocate(int size) +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); + + if (size <= TINY_CUTOFF) + { + if (tls->tcache.empty()) + { + intArray result = intArray(TINY_CUTOFF, true); + tls->tallocated.push_back(result); + return result; + } + else + { + intArray result = tls->tcache.back(); + tls->tcache.pop_back(); + tls->tallocated.push_back(result); + return result; + } + } + + if (size > tls->maxSize) + { +// app.DebugPrintf("IntCache: New max size: %d\n" , size); + tls->maxSize = size; + + // 4J - added - all the vectors in cache & allocated are smaller than maxSize so should be discarded. However, we + // can't delete them until the next releaseAll so copy into another vector until then + tls->toosmall.insert(tls->toosmall.end(),tls->cache.begin(),tls->cache.end()); + tls->toosmall.insert(tls->toosmall.end(),tls->allocated.begin(),tls->allocated.end()); + + tls->cache.clear(); + tls->allocated.clear(); + + intArray result = intArray(tls->maxSize, true); + tls->allocated.push_back(result); + return result; + } + else + { + if (tls->cache.empty()) + { + intArray result = intArray(tls->maxSize, true); + tls->allocated.push_back(result); + return result; + } + else + { + intArray result = tls->cache.back(); + tls->cache.pop_back(); + tls->allocated.push_back(result); + return result; + } + } +} + +void IntCache::releaseAll() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); + + // 4J - added - we can now remove the vectors that were deemed as too small (see comment in IntCache::allocate) + for( int i = 0; i < tls->toosmall.size(); i++ ) + { + delete [] tls->toosmall[i].data; + } + tls->toosmall.clear(); + + if (!tls->cache.empty()) + { + delete [] tls->cache.back().data; + tls->cache.pop_back(); + } + if (!tls->tcache.empty()) + { + delete [] tls->tcache.back().data; + tls->tcache.pop_back(); + } + + tls->cache.insert(tls->cache.end(),tls->allocated.begin(),tls->allocated.end()); + tls->tcache.insert(tls->tcache.end(),tls->tallocated.begin(),tls->tallocated.end()); + + tls->allocated.clear(); + tls->tallocated.clear(); +} + +// 4J added so that we can fully reset between levels +void IntCache::Reset() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); + tls->maxSize = TINY_CUTOFF; + for( int i = 0; i < tls->allocated.size(); i++ ) + { + delete [] tls->allocated[i].data; + } + tls->allocated.clear(); + + for( int i = 0; i < tls->cache.size(); i++ ) + { + delete [] tls->cache[i].data; + } + tls->cache.clear(); + + for( int i = 0; i < tls->tallocated.size(); i++ ) + { + delete [] tls->tallocated[i].data; + } + tls->tallocated.clear(); + + for( int i = 0; i < tls->tcache.size(); i++ ) + { + delete [] tls->tcache[i].data; + } + tls->tcache.clear(); + + for( int i = 0; i < tls->toosmall.size(); i++ ) + { + delete [] tls->toosmall[i].data; + } + tls->toosmall.clear(); +} \ No newline at end of file diff --git a/Minecraft.World/IntCache.h b/Minecraft.World/IntCache.h new file mode 100644 index 00000000..edc9764d --- /dev/null +++ b/Minecraft.World/IntCache.h @@ -0,0 +1,34 @@ +#pragma once + +#include "ArrayWithLength.h" + + +class IntCache +{ +private: + + class ThreadStorage + { + public: + int maxSize; + + vector tcache; + vector tallocated; + + vector cache; + vector allocated; + vector toosmall; // 4J added + ~ThreadStorage(); + }; + static DWORD tlsIdx; + + static const int TINY_CUTOFF = 256; + +public: + static intArray allocate(int size); + static void releaseAll(); + + static void CreateNewThreadStorage(); + static void ReleaseThreadStorage(); + static void Reset(); // 4J added +}; \ No newline at end of file diff --git a/Minecraft.World/IntTag.h b/Minecraft.World/IntTag.h new file mode 100644 index 00000000..f4d30818 --- /dev/null +++ b/Minecraft.World/IntTag.h @@ -0,0 +1,36 @@ +#pragma once +#include "Tag.h" + +class IntTag : public Tag +{ +public: + int data; + IntTag(const wstring &name) : Tag(name) {} + IntTag(const wstring &name, int data) : Tag(name) {this->data = data; } + + void write(DataOutput *dos) { dos->writeInt(data); } + void load(DataInput *dis) { data = dis->readInt(); } + + byte getId() { return TAG_Int; } + wstring toString() + { + static wchar_t buf[32]; + swprintf(buf, 32, L"%d", data); + return wstring( buf ); + } + + Tag *copy() + { + return new IntTag(getName(), data); + } + + bool equals(Tag *obj) + { + if (Tag::equals(obj)) + { + IntTag *o = (IntTag *) obj; + return data == o->data; + } + return false; + } +}; \ No newline at end of file diff --git a/Minecraft.World/InteractGoal.cpp b/Minecraft.World/InteractGoal.cpp new file mode 100644 index 00000000..04f94c89 --- /dev/null +++ b/Minecraft.World/InteractGoal.cpp @@ -0,0 +1,13 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "InteractGoal.h" + +InteractGoal::InteractGoal(Mob *mob, const type_info& lookAtType, float lookDistance) : LookAtPlayerGoal(mob, lookAtType, lookDistance) +{ + setRequiredControlFlags(Control::LookControlFlag | Control::MoveControlFlag); +} + +InteractGoal::InteractGoal(Mob *mob, const type_info& lookAtType, float lookDistance, float probability) : LookAtPlayerGoal(mob, lookAtType, lookDistance, probability) +{ + setRequiredControlFlags(Control::LookControlFlag | Control::MoveControlFlag); +} \ No newline at end of file diff --git a/Minecraft.World/InteractGoal.h b/Minecraft.World/InteractGoal.h new file mode 100644 index 00000000..d05b57b2 --- /dev/null +++ b/Minecraft.World/InteractGoal.h @@ -0,0 +1,10 @@ +#pragma once + +#include "LookAtPlayerGoal.h" + +class InteractGoal : public LookAtPlayerGoal +{ +public: + InteractGoal(Mob *mob, const type_info& lookAtType, float lookDistance); + InteractGoal(Mob *mob, const type_info& lookAtType, float lookDistance, float probability); +}; \ No newline at end of file diff --git a/Minecraft.World/InteractPacket.cpp b/Minecraft.World/InteractPacket.cpp new file mode 100644 index 00000000..fadeee30 --- /dev/null +++ b/Minecraft.World/InteractPacket.cpp @@ -0,0 +1,48 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "InteractPacket.h" + + + +const int InteractPacket::INTERACT = 0; +const int InteractPacket::ATTACK = 1; + +InteractPacket::InteractPacket() +{ + source = 0; + target = 0; + action = 0; +} + +InteractPacket::InteractPacket(int source, int target, int action) +{ + this->source = source; + this->target = target; + this->action = action; +} + +void InteractPacket::read(DataInputStream *dis) //throws IOException +{ + source = dis->readInt(); + target = dis->readInt(); + action = dis->readByte(); +} + +void InteractPacket::write(DataOutputStream *dos) // throws IOException +{ + dos->writeInt(source); + dos->writeInt(target); + dos->writeByte(action); +} + +void InteractPacket::handle(PacketListener *listener) +{ + listener->handleInteract(shared_from_this()); +} + +int InteractPacket::getEstimatedSize() +{ + return 9; +} diff --git a/Minecraft.World/InteractPacket.h b/Minecraft.World/InteractPacket.h new file mode 100644 index 00000000..0141038d --- /dev/null +++ b/Minecraft.World/InteractPacket.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class InteractPacket : public Packet, public enable_shared_from_this +{ +public: + static const int INTERACT; + static const int ATTACK; + + int source, target, action; + + InteractPacket(); + InteractPacket(int source, int target, int action); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new InteractPacket()); } + virtual int getId() { return 7; } +}; \ No newline at end of file diff --git a/Minecraft.World/Inventory.cpp b/Minecraft.World/Inventory.cpp new file mode 100644 index 00000000..8ef3f085 --- /dev/null +++ b/Minecraft.World/Inventory.cpp @@ -0,0 +1,746 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.stats.h" +#include "Material.h" +#include "Inventory.h" + +const int Inventory::POP_TIME_DURATION = 5; +const int Inventory::MAX_INVENTORY_STACK_SIZE = 64; + +const int Inventory::INVENTORY_SIZE = 4 * 9; +const int Inventory::SELECTION_SIZE = 9; + +// 4J Stu - The Pllayer is managed by shared_ptrs elsewhere, but it owns us so we don't want to also +// keep a shared_ptr of it. If we pass it on we should use shared_from_this() though +Inventory::Inventory(Player *player) +{ + items = ItemInstanceArray( INVENTORY_SIZE ); + armor = ItemInstanceArray( 4 ); + + selected = 0; + + carried = nullptr; + + changed = false; + + this->player = player; +} + +Inventory::~Inventory() +{ + delete [] items.data; + delete [] armor.data; +} + +shared_ptr Inventory::getSelected() +{ + // sanity checking to prevent exploits + if (selected < SELECTION_SIZE && selected >= 0) + { + return items[selected]; + } + return nullptr; +} + +// 4J-PB - Added for the in-game tooltips +bool Inventory::IsHeldItem() +{ + // sanity checking to prevent exploits + if (selected < SELECTION_SIZE && selected >= 0) + { + if(items[selected]) + { + return true; + } + } + return false; +} + +int Inventory::getSelectionSize() +{ + return SELECTION_SIZE; +} + + +int Inventory::getSlot(int tileId) +{ + for (unsigned int i = 0; i < items.length; i++) + { + if (items[i] != NULL && items[i]->id == tileId) return i; + } + return -1; +} + +int Inventory::getSlot(int tileId, int data) +{ + for (int i = 0; i < items.length; i++) + { + if (items[i] != NULL && items[i]->id == tileId && items[i]->getAuxValue() == data) return i; + } + return -1; +} + +int Inventory::getSlotWithRemainingSpace(shared_ptr item) +{ + for (unsigned int i = 0; i < items.length; i++) + { + if (items[i] != NULL && items[i]->id == item->id && items[i]->isStackable() + && items[i]->count < items[i]->getMaxStackSize() && items[i]->count < getMaxStackSize() + && (!items[i]->isStackedByData() || items[i]->getAuxValue() == item->getAuxValue()) + && ItemInstance::tagMatches(items[i], item)) + { + return i; + } + } + return -1; +} + +int Inventory::getFreeSlot() +{ + for (unsigned int i = 0; i < items.length; i++) + { + if (items[i] == NULL) return i; + } + return -1; +} + + +void Inventory::grabTexture(int id, int data, bool checkData, bool mayReplace) +{ + int slot = -1; + heldItem = getSelected(); + if (checkData) + { + slot = getSlot(id, data); + } + else + { + slot = getSlot(id); + } + if (slot >= 0 && slot < 9) + { + selected = slot; + return; + } + + if (mayReplace) + { + if (id > 0) + { + int firstEmpty = getFreeSlot(); + if (firstEmpty >= 0 && firstEmpty < 9) + { + selected = firstEmpty; + } + + replaceSlot(Item::items[id], data); + } + } +} + +void Inventory::swapPaint(int wheel) +{ + if (wheel > 0) wheel = 1; + if (wheel < 0) wheel = -1; + + selected -= wheel; + + while (selected < 0) + selected += 9; + while (selected >= 9) + selected -= 9; +} + +void Inventory::clearInventory() +{ + for (unsigned int i = 0; i < items.length; i++) + { + items[i] = nullptr; + } + for (unsigned int i = 0; i < armor.length; i++) + { + armor[i] = nullptr; + } +} + +void Inventory::replaceSlot(Item *item, int data) +{ + if (item != NULL) + { + int oldSlot = getSlot(item->id, data); + if (oldSlot >= 0) + { + items[oldSlot] = items[selected]; + } + + // It's too easy to accidentally pick block and lose enchanted + // items. + if (heldItem != NULL && heldItem->isEnchantable() && getSlot(heldItem->id, heldItem->getDamageValue()) == selected) + { + return; + } + items[selected] = shared_ptr(new ItemInstance(Item::items[item->id], 1, data)); + } +} + + +int Inventory::addResource(shared_ptr itemInstance) +{ + + int type = itemInstance->id; + int count = itemInstance->count; + + // 4J Stu - Brought forward from 1.2 + if (itemInstance->getMaxStackSize() == 1) + { + int slot = getFreeSlot(); + if (slot < 0) return count; + if (items[slot] == NULL) + { + items[slot] = ItemInstance::clone(itemInstance); + player->handleCollectItem(itemInstance); + } + return 0; + } + + int slot = getSlotWithRemainingSpace(itemInstance); + if (slot < 0) slot = getFreeSlot(); + if (slot < 0) return count; + if (items[slot] == NULL) + { + items[slot] = shared_ptr( new ItemInstance(type, 0, itemInstance->getAuxValue()) ); + // 4J Stu - Brought forward from 1.2 + if (itemInstance->hasTag()) + { + items[slot]->setTag((CompoundTag *) itemInstance->getTag()->copy()); + player->handleCollectItem(itemInstance); + } + } + + int toAdd = count; + if (toAdd > items[slot]->getMaxStackSize() - items[slot]->count) + { + toAdd = items[slot]->getMaxStackSize() - items[slot]->count; + } + if (toAdd > getMaxStackSize() - items[slot]->count) + { + toAdd = getMaxStackSize() - items[slot]->count; + } + + if (toAdd == 0) return count; + + count -= toAdd; + items[slot]->count += toAdd; + items[slot]->popTime = POP_TIME_DURATION; + + return count; +} + + +void Inventory::tick() +{ + for (unsigned int i = 0; i < items.length; i++) + { + if (items[i] != NULL) + { + items[i]->inventoryTick(player->level, player->shared_from_this(), i, selected == i); + } + } +} + +bool Inventory::removeResource(int type) +{ + int slot = getSlot(type); + if (slot < 0) return false; + if (--items[slot]->count <= 0) items[slot] = nullptr; + + return true; +} + +bool Inventory::removeResource(int type,int iAuxVal) +{ + int slot = getSlot(type,iAuxVal); + if (slot < 0) return false; + if (--items[slot]->count <= 0) items[slot] = nullptr; + + return true; +} + +void Inventory::removeResources(shared_ptr item) +{ + if(item == NULL) return; + + int countToRemove = item->count; + for (unsigned int i = 0; i < items.length; i++) + { + if (items[i] != NULL && items[i]->sameItemWithTags(item)) + { + int slotCount = items[i]->count; + items[i]->count -= countToRemove; + if(slotCount < countToRemove) + { + countToRemove -= slotCount; + } + else + { + countToRemove = 0; + } + if(items[i]->count <= 0) items[i] = nullptr; + } + } +} + +shared_ptr Inventory::getResourceItem(int type) +{ + int slot = getSlot(type); + if (slot < 0) return nullptr; + return getItem( slot ); +} + +shared_ptr Inventory::getResourceItem(int type,int iAuxVal) +{ + int slot = getSlot(type,iAuxVal); + if (slot < 0) return nullptr; + return getItem( slot ); +} + +bool Inventory::hasResource(int type) +{ + int slot = getSlot(type); + if (slot < 0) return false; + + return true; +} + +void Inventory::swapSlots(int from, int to) +{ + shared_ptr tmp = items[to]; + items[to] = items[from]; + items[from] = tmp; +} + +bool Inventory::add(shared_ptr item) +{ + // 4J Stu - Fix for duplication glitch + if(item->count <= 0) return true; + + if (!item->isDamaged()) + { + int lastSize; + int count = item->count; + do + { + lastSize = item->count; + item->count = addResource(item); + } while (item->count > 0 && item->count < lastSize); + if (item->count == lastSize && player->abilities.instabuild) + { + // silently destroy the item when having a full inventory + item->count = 0; + return true; + } + if( item->count < lastSize ) + { + player->awardStat( + GenericStats::itemsCollected(item->id, item->getAuxValue()), + GenericStats::param_itemsCollected(item->id, item->getAuxValue(), count) + ); + return true; + } + else + return false; + } + + int slot = getFreeSlot(); + if (slot >= 0) + { + player->handleCollectItem(item); + + player->awardStat( + GenericStats::itemsCollected(item->id, item->getAuxValue()), + GenericStats::param_itemsCollected(item->id, item->getAuxValue(), item->GetCount())); + + items[slot] = ItemInstance::clone(item); + items[slot]->popTime = Inventory::POP_TIME_DURATION; + item->count = 0; + return true; + } + else if (player->abilities.instabuild) + { + // silently destroy the item when having a full inventory + item->count = 0; + return true; + } + return false; +} + +shared_ptr Inventory::removeItem(unsigned int slot, int count) +{ + + ItemInstanceArray pile = items; + if (slot >= items.length) + { + pile = armor; + slot -= items.length; + } + + if (pile[slot] != NULL) + { + if (pile[slot]->count <= count) + { + shared_ptr item = pile[slot]; + pile[slot] = nullptr; + return item; + } + else + { + shared_ptr i = pile[slot]->remove(count); + if (pile[slot]->count == 0) pile[slot] = nullptr; + return i; + } + } + return nullptr; +} + +shared_ptr Inventory::removeItemNoUpdate(int slot) +{ + ItemInstanceArray pile = items; + if (slot >= items.length) + { + pile = armor; + slot -= items.length; + } + + if (pile[slot] != NULL) + { + shared_ptr item = pile[slot]; + pile[slot] = nullptr; + return item; + } + return nullptr; +} + +void Inventory::setItem(unsigned int slot, shared_ptr item) +{ +#ifdef _DEBUG + if(item!=NULL) + { + wstring itemstring=item->toString(); + app.DebugPrintf("Inventory::setItem - slot = %d,\t item = %d ",slot,item->id); + //OutputDebugStringW(itemstring.c_str()); + app.DebugPrintf("\n"); + } +#else + if(item!=NULL) + { + app.DebugPrintf("Inventory::setItem - slot = %d,\t item = %d, aux = %d\n",slot,item->id,item->getAuxValue()); + } +#endif + // 4J Stu - Changed this a little from Java to be less funn + if( slot >= items.length ) + { + armor[slot - items.length] = item; + } + else + { + items[slot] = item; + } + player->handleCollectItem(item); + /* + ItemInstanceArray& pile = items; + if (slot >= pile.length) + { + slot -= pile.length; + pile = armor; + } + + pile[slot] = item; + */ +} + +float Inventory::getDestroySpeed(Tile *tile) +{ + float speed = 1.0f; + if (items[selected] != NULL) speed *= items[selected]->getDestroySpeed(tile); + return speed; +} + +ListTag *Inventory::save(ListTag *listTag) +{ + for (unsigned int i = 0; i < items.length; i++) + { + if (items[i] != NULL) + { + CompoundTag *tag = new CompoundTag(); + tag->putByte(L"Slot", (byte) i); + items[i]->save(tag); + listTag->add(tag); + } + } + for (unsigned int i = 0; i < armor.length; i++) + { + if (armor[i] != NULL) + { + CompoundTag *tag = new CompoundTag(); + tag->putByte(L"Slot", (byte) (i + 100)); + armor[i]->save(tag); + listTag->add(tag); + } + } + return listTag; +} + +void Inventory::load(ListTag *inventoryList) +{ + if( items.data != NULL) + { + delete[] items.data; + items.data = NULL; + } + if( armor.data != NULL) + { + delete[] armor.data; + armor.data = NULL; + + } + items = ItemInstanceArray( INVENTORY_SIZE ); + armor = ItemInstanceArray( 4 ); + for (int i = 0; i < inventoryList->size(); i++) + { + CompoundTag *tag = inventoryList->get(i); + unsigned int slot = tag->getByte(L"Slot") & 0xff; + shared_ptr item = shared_ptr( ItemInstance::fromTag(tag) ); + if (item != NULL) + { + if (slot >= 0 && slot < items.length) items[slot] = item; + if (slot >= 100 && slot < armor.length + 100) armor[slot - 100] = item; + } + } +} + +unsigned int Inventory::getContainerSize() +{ + return items.length + 4; +} + +shared_ptr Inventory::getItem(unsigned int slot) +{ + // 4J Stu - Changed this a little from the Java so it's less funny + if( slot >= items.length ) + { + return armor[ slot - items.length ]; + } + else + { + return items[ slot ]; + } + /* + ItemInstanceArray pile = items; + if (slot >= pile.length) + { + slot -= pile.length; + pile = armor; + } + + return pile[slot]; + */ +} + +int Inventory::getName() +{ + return IDS_INVENTORY; +} + +int Inventory::getMaxStackSize() +{ + return MAX_INVENTORY_STACK_SIZE; +} + +int Inventory::getAttackDamage(shared_ptr entity) +{ + shared_ptr item = getItem(selected); + if (item != NULL) return item->getAttackDamage(entity); + return 1; +} + +bool Inventory::canDestroy(Tile *tile) +{ + if (tile->material->isAlwaysDestroyable()) return true; + + shared_ptr item = getItem(selected); + if (item != NULL) return item->canDestroySpecial(tile); + return false; +} + +shared_ptr Inventory::getArmor(int layer) +{ + return armor[layer]; +} + +int Inventory::getArmorValue() +{ + int val = 0; + for (unsigned int i = 0; i < armor.length; i++) + { + if (armor[i] != NULL && dynamic_cast( armor[i]->getItem() ) != NULL ) + { + int baseProtection = dynamic_cast(armor[i]->getItem())->defense; + + val += baseProtection; + } + } + return val; +} + +void Inventory::hurtArmor(int dmg) +{ + dmg = dmg / 4; + if (dmg < 1) + { + dmg = 1; + } + for (unsigned int i = 0; i < armor.length; i++) + { + if (armor[i] != NULL && dynamic_cast( armor[i]->getItem() ) != NULL ) + { + armor[i]->hurt(dmg, dynamic_pointer_cast( player->shared_from_this() ) ); + if (armor[i]->count == 0) + { + armor[i] = nullptr; + } + } + } +} + +void Inventory::dropAll() +{ + for (unsigned int i = 0; i < items.length; i++) + { + if (items[i] != NULL) + { + player->drop(items[i], true); + items[i] = nullptr; + } + } + for (unsigned int i = 0; i < armor.length; i++) + { + if (armor[i] != NULL) + { + player->drop(armor[i], true); + armor[i] = nullptr; + } + } +} + +void Inventory::setChanged() +{ + changed = true; +} + +bool Inventory::isSame(shared_ptr copy) +{ + for (unsigned int i = 0; i < items.length; i++) + { + if (!isSame( copy->items[i], items[i])) return false; + } + for (unsigned int i = 0; i < armor.length; i++) + { + if (!isSame( copy->armor[i], armor[i])) return false; + } + return true; +} + + +bool Inventory::isSame(shared_ptr a, shared_ptr b) +{ + if (a == NULL && b == NULL) return true; + if (a == NULL || b == NULL) return false; + + return a->id == b->id && a->count == b->count && a->getAuxValue() == b->getAuxValue(); +} + + +shared_ptr Inventory::copy() +{ + shared_ptr copy = shared_ptr( new Inventory(NULL) ); + for (unsigned int i = 0; i < items.length; i++) + { + copy->items[i] = items[i] != NULL ? items[i]->copy() : nullptr; + } + for (unsigned int i = 0; i < armor.length; i++) + { + copy->armor[i] = armor[i] != NULL ? armor[i]->copy() : nullptr; + } + return copy; +} + +void Inventory::setCarried(shared_ptr carried) +{ + this->carried = carried; + player->handleCollectItem(carried); +} + +shared_ptr Inventory::getCarried() +{ + return carried; +} + +bool Inventory::stillValid(shared_ptr player) +{ + if (this->player->removed) return false; + if (player->distanceToSqr(this->player->shared_from_this()) > 8 * 8) return false; + return true; +} + +bool Inventory::contains(shared_ptr itemInstance) +{ + for (unsigned int i = 0; i < armor.length; i++) + { + if (armor[i] != NULL && armor[i]->equals(itemInstance)) return true; + } + for (unsigned int i = 0; i < items.length; i++) + { + if (items[i] != NULL && items[i]->equals(itemInstance)) return true; + } + return false; +} + +void Inventory::startOpen() +{ + // TODO Auto-generated method stub +} + +void Inventory::stopOpen() +{ + // TODO Auto-generated method stub +} + +void Inventory::replaceWith(shared_ptr other) +{ + for (int i = 0; i < items.length; i++) + { + items[i] = ItemInstance::clone(other->items[i]); + } + for (int i = 0; i < armor.length; i++) + { + armor[i] = ItemInstance::clone(other->armor[i]); + } +} + +int Inventory::countMatches(shared_ptr itemInstance) +{ + if(itemInstance == NULL) return 0; + int count = 0; + //for (unsigned int i = 0; i < armor.length; i++) + //{ + // if (armor[i] != NULL && armor[i]->sameItem(itemInstance)) count += items[i]->count; + //} + for (unsigned int i = 0; i < items.length; i++) + { + if (items[i] != NULL && items[i]->sameItemWithTags(itemInstance)) count += items[i]->count; + } + return count; +} diff --git a/Minecraft.World/Inventory.h b/Minecraft.World/Inventory.h new file mode 100644 index 00000000..9d1aa7e0 --- /dev/null +++ b/Minecraft.World/Inventory.h @@ -0,0 +1,136 @@ +#pragma once +using namespace std; +#include "Container.h" +#include "ListTag.h" +#include "ItemInstance.h" + +class Player; +class CompoundTag; + +class Inventory : public Container +{ +public: + static const int POP_TIME_DURATION ; + static const int MAX_INVENTORY_STACK_SIZE; + +private: + static const int INVENTORY_SIZE; + static const int SELECTION_SIZE; + +public: + ItemInstanceArray items; + ItemInstanceArray armor; + + int selected; + Player *player; // This is owned by shared_ptrs, but we are owned by it + +private: + shared_ptr heldItem; + shared_ptr carried; + +public: + bool changed; + + Inventory(Player *player); + ~Inventory(); + + shared_ptr getSelected(); + // 4J-PB - Added for the in-game tooltips + bool IsHeldItem(); + + static int getSelectionSize(); + +private: + int getSlot(int tileId); + int getSlot(int tileId, int data); + + int getSlotWithRemainingSpace(shared_ptr item); + +public: + int getFreeSlot(); + + void grabTexture(int id, int data, bool checkData, bool mayReplace); + + void swapPaint(int wheel); + + void clearInventory(); + + void replaceSlot(Item *item, int data); + +private: + int addResource(shared_ptr itemInstance); + +public: + void tick(); + + bool removeResource(int type); + + // 4J-PB added to get the right resource from the inventory for removal + bool removeResource(int type,int iAuxVal); + void removeResources(shared_ptr item); // 4J Added for trading + + // 4J-Stu added to the get the item that would be affected by the removeResource functions + shared_ptr getResourceItem(int type); + shared_ptr getResourceItem(int type,int iAuxVal); + + bool hasResource(int type); + + void swapSlots(int from, int to); + + bool add(shared_ptr item); + + shared_ptr removeItem(unsigned int slot, int count); + virtual shared_ptr removeItemNoUpdate(int slot); + + void setItem(unsigned int slot, shared_ptr item); + + float getDestroySpeed(Tile *tile); + + ListTag *save(ListTag *listTag); + + void load(ListTag *inventoryList); + + unsigned int getContainerSize(); + + shared_ptr getItem(unsigned int slot); + + int getName(); + + int getMaxStackSize(); + + int getAttackDamage(shared_ptr entity); + + bool canDestroy(Tile *tile); + + shared_ptr getArmor(int layer); + + int getArmorValue(); + + void hurtArmor(int dmg); + + void dropAll(); + + void setChanged(); + + bool isSame(shared_ptr copy); + +private: + bool isSame(shared_ptr a, shared_ptr b); + +public: + shared_ptr copy(); + + void setCarried(shared_ptr carried); + + shared_ptr getCarried(); + + bool stillValid(shared_ptr player); + + bool contains(shared_ptr itemInstance); + + virtual void startOpen(); + virtual void stopOpen(); + void replaceWith(shared_ptr other); + + int countMatches(shared_ptr itemInstance); // 4J Added +}; \ No newline at end of file diff --git a/Minecraft.World/InventoryMenu.cpp b/Minecraft.World/InventoryMenu.cpp new file mode 100644 index 00000000..979243c1 --- /dev/null +++ b/Minecraft.World/InventoryMenu.cpp @@ -0,0 +1,247 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.crafting.h" +#include "net.minecraft.world.entity.player.h" +#include "ResultSlot.h" +#include "ArmorSlot.h" +#include "CraftingContainer.h" +#include "ResultContainer.h" +#include "InventoryMenu.h" +#include "Tile.h" +#include "GenericStats.h" + +const int InventoryMenu::RESULT_SLOT = 0; +const int InventoryMenu::CRAFT_SLOT_START = 1; +const int InventoryMenu::CRAFT_SLOT_END = InventoryMenu::CRAFT_SLOT_START + 4; +const int InventoryMenu::ARMOR_SLOT_START = InventoryMenu::CRAFT_SLOT_END; +const int InventoryMenu::ARMOR_SLOT_END = InventoryMenu::ARMOR_SLOT_START + 4; +const int InventoryMenu::INV_SLOT_START = InventoryMenu::ARMOR_SLOT_END; +const int InventoryMenu::INV_SLOT_END = InventoryMenu::INV_SLOT_START + 9 * 3; +const int InventoryMenu::USE_ROW_SLOT_START = InventoryMenu::INV_SLOT_END; +const int InventoryMenu::USE_ROW_SLOT_END = InventoryMenu::USE_ROW_SLOT_START + 9; + +InventoryMenu::InventoryMenu(shared_ptr inventory, bool active, Player *player) : AbstractContainerMenu() +{ + owner = player; + _init( inventory, active ); +} + +void InventoryMenu::_init(shared_ptr inventory, bool active) +{ + craftSlots = shared_ptr( new CraftingContainer(this, 2, 2) ); + resultSlots = shared_ptr( new ResultContainer() ); + + this->active = active; + addSlot(new ResultSlot( inventory->player, craftSlots, resultSlots, 0, 144, 36)); + + for (int y = 0; y < 2; y++) + { + for (int x = 0; x < 2; x++) + { + addSlot(new Slot(craftSlots, x + y * 2, 88 + x * 18, 26 + y * 18)); + } + } + + for (int i = 0; i < 4; i++) + { + // 4J Stu I removed an anonymous class that was here whose only purpose seemed to be a way of using the + // loop counter i within the functions, rather than making it a member of the object. I have moved all that + // out to the ArmorSlot class + addSlot(new ArmorSlot(i, inventory, inventory->getContainerSize() - 1 - i, 8, 8 + i * 18) ); + } + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x + (y + 1) * 9, 8 + x * 18, 84 + y * 18)); + } + } + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x, 8 + x * 18, 142)); + } + + slotsChanged(); // 4J removed craftSlots parameter, see comment below +} + +void InventoryMenu::slotsChanged() // 4J used to take a shared_ptr but wasn't using it, so removed to simplify things +{ + MemSect(23); + resultSlots->setItem(0, Recipes::getInstance()->getItemFor(craftSlots, owner->level) ); + MemSect(0); +} + +void InventoryMenu::removed(shared_ptr player) +{ + AbstractContainerMenu::removed(player); + for (int i = 0; i < 4; i++) + { + shared_ptr item = craftSlots->removeItemNoUpdate(i); + if (item != NULL) + { + player->drop(item); + craftSlots->setItem(i, nullptr); + } + } + resultSlots->setItem(0, nullptr); +} + +bool InventoryMenu::stillValid(shared_ptr player) +{ + return true; +} + +shared_ptr InventoryMenu::quickMoveStack(shared_ptr player, int slotIndex) +{ + shared_ptr clicked = nullptr; + Slot *slot = slots->at(slotIndex); + + Slot *HelmetSlot = slots->at(ARMOR_SLOT_START); + Slot *ChestplateSlot = slots->at(ARMOR_SLOT_START+1); + Slot *LeggingsSlot = slots->at(ARMOR_SLOT_START+2); + Slot *BootsSlot = slots->at(ARMOR_SLOT_START+3); + + + if (slot != NULL && slot->hasItem()) + { + shared_ptr stack = slot->getItem(); + clicked = stack->copy(); + + if (slotIndex == RESULT_SLOT) + { + // 4J Stu - Brought forward change from 1.2 + if(!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, true)) + { + return nullptr; + } + slot->onQuickCraft(stack, clicked); + } + else if (slotIndex >= INV_SLOT_START && slotIndex < INV_SLOT_END) + { + // 4J-PB - added for quick equip + if(ArmorRecipes::GetArmorType(stack->id)==ArmorRecipes::eArmorType_Helmet && (!HelmetSlot->hasItem() ) ) + { + if(!moveItemStackTo(stack, ARMOR_SLOT_START, ARMOR_SLOT_START+1, false)) + { + return nullptr; + } + } + else if(ArmorRecipes::GetArmorType(stack->id)==ArmorRecipes::eArmorType_Chestplate && (!ChestplateSlot->hasItem() ) ) + { + if(!moveItemStackTo(stack, ARMOR_SLOT_START+1, ARMOR_SLOT_START+2, false)) + { + return nullptr; + } + } + else if(ArmorRecipes::GetArmorType(stack->id)==ArmorRecipes::eArmorType_Leggings && (!LeggingsSlot->hasItem() ) ) + { + if(!moveItemStackTo(stack, ARMOR_SLOT_START+2, ARMOR_SLOT_START+3, false)) + { + return nullptr; + } + } + else if(ArmorRecipes::GetArmorType(stack->id)==ArmorRecipes::eArmorType_Boots && (!BootsSlot->hasItem() ) ) + { + if(!moveItemStackTo(stack, ARMOR_SLOT_START+3, ARMOR_SLOT_START+4, false)) + { + return nullptr; + } + } + // 4J Stu - Brought forward change from 1.2 + else if(!moveItemStackTo(stack, USE_ROW_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + } + else if (slotIndex >= USE_ROW_SLOT_START && slotIndex < USE_ROW_SLOT_END) + { + //ArmorRecipes::_eArmorType eArmourType=ArmorRecipes::GetArmorType(stack->id); + + if(ArmorRecipes::GetArmorType(stack->id)==ArmorRecipes::eArmorType_Helmet && (!HelmetSlot->hasItem() ) ) + { + if(!moveItemStackTo(stack, ARMOR_SLOT_START, ARMOR_SLOT_START+1, false)) + { + return nullptr; + } + } + else if(ArmorRecipes::GetArmorType(stack->id)==ArmorRecipes::eArmorType_Chestplate && (!ChestplateSlot->hasItem() ) ) + { + if(!moveItemStackTo(stack, ARMOR_SLOT_START+1, ARMOR_SLOT_START+2, false)) + { + return nullptr; + } + } + else if(ArmorRecipes::GetArmorType(stack->id)==ArmorRecipes::eArmorType_Leggings && (!LeggingsSlot->hasItem() ) ) + { + if(!moveItemStackTo(stack, ARMOR_SLOT_START+2, ARMOR_SLOT_START+3, false)) + { + return nullptr; + } + } + else if(ArmorRecipes::GetArmorType(stack->id)==ArmorRecipes::eArmorType_Boots && (!BootsSlot->hasItem() ) ) + { + if(!moveItemStackTo(stack, ARMOR_SLOT_START+3, ARMOR_SLOT_START+4, false)) + { + return nullptr; + } + } + // 4J Stu - Brought forward change from 1.2 + else if(!moveItemStackTo(stack, INV_SLOT_START, INV_SLOT_END, false)) + { + return nullptr; + } + } + else + { + // 4J Stu - Brought forward change from 1.2 + if(!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + } + if (stack->count == 0) + { + slot->set(nullptr); + } + else + { + slot->setChanged(); + } + if (stack->count == clicked->count) + { + // nothing moved + return nullptr; + } + else + { + slot->onTake(player, stack); + } + } + return clicked; +} + +bool InventoryMenu::mayCombine(Slot *slot, shared_ptr item) +{ + return slot->mayCombine(item); +} + +// 4J-JEV: Added for achievement 'Iron Man'. +shared_ptr InventoryMenu::clicked(int slotIndex, int buttonNum, int clickType, shared_ptr player) +{ + shared_ptr out = AbstractContainerMenu::clicked(slotIndex, buttonNum, clickType, player); + +#ifdef _EXTENDED_ACHIEVEMENTS + static int ironItems[4] = {Item::helmet_iron_Id,Item::chestplate_iron_Id,Item::leggings_iron_Id,Item::boots_iron_Id}; + for (int i = ARMOR_SLOT_START; i < ARMOR_SLOT_END; i++) + { + Slot *slot = slots->at(i); + if ( (slot==NULL) || (!slot->hasItem()) || (slot->getItem()->getItem()->id != ironItems[i-ARMOR_SLOT_START]) ) + { + return out; + } + } + player->awardStat(GenericStats::ironMan(),GenericStats::param_ironMan()); +#endif + + return out; +} diff --git a/Minecraft.World/InventoryMenu.h b/Minecraft.World/InventoryMenu.h new file mode 100644 index 00000000..b37a4be9 --- /dev/null +++ b/Minecraft.World/InventoryMenu.h @@ -0,0 +1,44 @@ +#pragma once + +#include "AbstractContainerMenu.h" + +class CraftingContainer; +class Container; + +class InventoryMenu : public AbstractContainerMenu +{ +private: + Player *owner; + + // 4J Stu Made these public for UI menus, perhaps should make friend class? +public: + static const int RESULT_SLOT; + static const int CRAFT_SLOT_START; + static const int CRAFT_SLOT_END; + static const int ARMOR_SLOT_START; + static const int ARMOR_SLOT_END; + static const int INV_SLOT_START; + static const int INV_SLOT_END; + static const int USE_ROW_SLOT_START; + static const int USE_ROW_SLOT_END; + +public: + shared_ptr craftSlots; + shared_ptr resultSlots; + bool active; + + InventoryMenu(shared_ptr inventory, bool active, Player *player); + +private: + void _init(shared_ptr inventory, bool active); + +public: + virtual void slotsChanged(); // 4J used to take a shared_ptr but wasn't using it, so removed to simplify things + virtual void removed(shared_ptr player); + virtual bool stillValid(shared_ptr player); + virtual shared_ptr quickMoveStack(shared_ptr player, int slotIndex); + virtual bool mayCombine(Slot *slot, shared_ptr item); + + // 4J ADDED, + virtual shared_ptr clicked(int slotIndex, int buttonNum, int clickType, shared_ptr player); +}; diff --git a/Minecraft.World/IslandLayer.cpp b/Minecraft.World/IslandLayer.cpp new file mode 100644 index 00000000..b502f384 --- /dev/null +++ b/Minecraft.World/IslandLayer.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" + +IslandLayer::IslandLayer(__int64 seedMixup) : Layer(seedMixup) +{ +} + +intArray IslandLayer::getArea(int xo, int yo, int w, int h) +{ + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + initRandom(xo + x, yo + y); + result[x + y * w] = (nextRandom(10) == 0) ? 1 : 0; + } + } + // if (0, 0) is located here, place an island + if (xo > -w && xo <= 0 && yo > -h && yo <= 0) + { + result[-xo + -yo * w] = 1; + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/IslandLayer.h b/Minecraft.World/IslandLayer.h new file mode 100644 index 00000000..09e90eb3 --- /dev/null +++ b/Minecraft.World/IslandLayer.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Layer.h" + +class IslandLayer : public Layer +{ +public: + IslandLayer(__int64 seedMixup); + + intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/Item.cpp b/Minecraft.World/Item.cpp new file mode 100644 index 00000000..5d723ec5 --- /dev/null +++ b/Minecraft.World/Item.cpp @@ -0,0 +1,1110 @@ +#include "stdafx.h" + +#include "net.minecraft.locale.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.alchemy.h" +#include "net.minecraft.world.food.h" +#include "net.minecraft.world.effect.h" +#include "net.minecraft.stats.h" +#include "MapItem.h" +#include "Item.h" +#include "HangingEntityItem.h" + +typedef Item::Tier _Tier; + +wstring Item::ICON_DESCRIPTION_PREFIX = L"item."; + +const _Tier *_Tier::WOOD = new _Tier(0, 59, 2, 0, 15); // +const _Tier *_Tier::STONE = new _Tier(1, 131, 4, 1, 5); // +const _Tier *_Tier::IRON = new _Tier(2, 250, 6, 2, 14); // +const _Tier *_Tier::DIAMOND = new _Tier(3, 1561, 8, 3, 10); // +const _Tier *_Tier::GOLD = new _Tier(0, 32, 12, 0, 22); + +Random *Item::random = new Random(); + +ItemArray Item::items = ItemArray( ITEM_NUM_COUNT ); + +Item *Item::shovel_iron = NULL; +Item *Item::pickAxe_iron = NULL; +Item *Item::hatchet_iron = NULL; +Item *Item::flintAndSteel = NULL; +Item *Item::apple = NULL; +BowItem *Item::bow = NULL; +Item *Item::arrow = NULL; +Item *Item::coal = NULL; +Item *Item::diamond = NULL; +Item *Item::ironIngot = NULL; +Item *Item::goldIngot = NULL; +Item *Item::sword_iron = NULL; + +Item *Item::sword_wood = NULL; +Item *Item::shovel_wood = NULL; +Item *Item::pickAxe_wood = NULL; +Item *Item::hatchet_wood = NULL; + +Item *Item::sword_stone = NULL; +Item *Item::shovel_stone = NULL; +Item *Item::pickAxe_stone = NULL; +Item *Item::hatchet_stone = NULL; + +Item *Item::sword_diamond = NULL; +Item *Item::shovel_diamond = NULL; +Item *Item::pickAxe_diamond = NULL; +Item *Item::hatchet_diamond = NULL; + +Item *Item::stick = NULL; +Item *Item::bowl = NULL; +Item *Item::mushroomStew = NULL; + +Item *Item::sword_gold = NULL; +Item *Item::shovel_gold = NULL; +Item *Item::pickAxe_gold = NULL; +Item *Item::hatchet_gold = NULL; + +Item *Item::string = NULL; +Item *Item::feather = NULL; +Item *Item::sulphur = NULL; + +Item *Item::hoe_wood = NULL; +Item *Item::hoe_stone = NULL; +Item *Item::hoe_iron = NULL; +Item *Item::hoe_diamond = NULL; +Item *Item::hoe_gold = NULL; + +Item *Item::seeds_wheat = NULL; +Item *Item::wheat = NULL; +Item *Item::bread = NULL; + +ArmorItem *Item::helmet_cloth = NULL; +ArmorItem *Item::chestplate_cloth = NULL; +ArmorItem *Item::leggings_cloth = NULL; +ArmorItem *Item::boots_cloth = NULL; + +ArmorItem *Item::helmet_chain = NULL; +ArmorItem *Item::chestplate_chain = NULL; +ArmorItem *Item::leggings_chain = NULL; +ArmorItem *Item::boots_chain = NULL; + +ArmorItem *Item::helmet_iron = NULL; +ArmorItem *Item::chestplate_iron = NULL; +ArmorItem *Item::leggings_iron = NULL; +ArmorItem *Item::boots_iron = NULL; + +ArmorItem *Item::helmet_diamond = NULL; +ArmorItem *Item::chestplate_diamond = NULL; +ArmorItem *Item::leggings_diamond = NULL; +ArmorItem *Item::boots_diamond = NULL; + +ArmorItem *Item::helmet_gold = NULL; +ArmorItem *Item::chestplate_gold = NULL; +ArmorItem *Item::leggings_gold = NULL; +ArmorItem *Item::boots_gold = NULL; + +Item *Item::flint = NULL; +Item *Item::porkChop_raw = NULL; +Item *Item::porkChop_cooked = NULL; +Item *Item::painting = NULL; + +Item *Item::apple_gold = NULL; + +Item *Item::sign = NULL; +Item *Item::door_wood = NULL; + +Item *Item::bucket_empty = NULL; +Item *Item::bucket_water = NULL; +Item *Item::bucket_lava = NULL; + +Item *Item::minecart = NULL; +Item *Item::saddle = NULL; +Item *Item::door_iron = NULL; +Item *Item::redStone = NULL; +Item *Item::snowBall = NULL; + +Item *Item::boat = NULL; + +Item *Item::leather = NULL; +Item *Item::milk = NULL; +Item *Item::brick = NULL; +Item *Item::clay = NULL; +Item *Item::reeds = NULL; +Item *Item::paper = NULL; +Item *Item::book = NULL; +Item *Item::slimeBall = NULL; +Item *Item::minecart_chest = NULL; +Item *Item::minecart_furnace = NULL; +Item *Item::egg = NULL; +Item *Item::compass = NULL; +FishingRodItem *Item::fishingRod = NULL; +Item *Item::clock = NULL; +Item *Item::yellowDust = NULL; +Item *Item::fish_raw = NULL; +Item *Item::fish_cooked = NULL; + +Item *Item::dye_powder = NULL; +Item *Item::bone = NULL; +Item *Item::sugar = NULL; +Item *Item::cake = NULL; + +Item *Item::bed = NULL; + +Item *Item::diode = NULL; +Item *Item::cookie = NULL; + +MapItem *Item::map = NULL; + +Item *Item::record_01 = NULL; +Item *Item::record_02 = NULL; +Item *Item::record_03 = NULL; +Item *Item::record_04 = NULL; +Item *Item::record_05 = NULL; +Item *Item::record_06 = NULL; +Item *Item::record_07 = NULL; +Item *Item::record_08 = NULL; +Item *Item::record_09 = NULL; +Item *Item::record_10 = NULL; +Item *Item::record_11 = NULL; +Item *Item::record_12 = NULL; + +ShearsItem *Item::shears = NULL; + +Item *Item::melon = NULL; + +Item *Item::seeds_pumpkin = NULL; +Item *Item::seeds_melon = NULL; + +Item *Item::beef_raw = NULL; +Item *Item::beef_cooked = NULL; +Item *Item::chicken_raw = NULL; +Item *Item::chicken_cooked = NULL; +Item *Item::rotten_flesh = NULL; + +Item *Item::enderPearl = NULL; + +Item *Item::blazeRod = NULL; +Item *Item::ghastTear = NULL; +Item *Item::goldNugget = NULL; +Item *Item::netherStalkSeeds = NULL; +PotionItem *Item::potion = NULL; +Item *Item::glassBottle = NULL; +Item *Item::spiderEye = NULL; +Item *Item::fermentedSpiderEye = NULL; +Item *Item::blazePowder = NULL; +Item *Item::magmaCream = NULL; +Item *Item::brewingStand = NULL; +Item *Item::cauldron = NULL; +Item *Item::eyeOfEnder = NULL; +Item *Item::speckledMelon = NULL; + +Item *Item::monsterPlacer = NULL; + +Item *Item::expBottle = NULL; + +// TU9 +Item *Item::fireball = NULL; +Item *Item::frame = NULL; +Item *Item::netherbrick = NULL; + +Item *Item::skull = NULL; + + +// TU14 +//Item *Item::writingBook = NULL; +//Item *Item::writtenBook = NULL; + +Item *Item::emerald = NULL; + +Item *Item::flowerPot = NULL; + +Item *Item::carrots = NULL; +Item *Item::potato = NULL; +Item *Item::potatoBaked = NULL; +Item *Item::potatoPoisonous = NULL; + +Item *Item::carrotGolden = NULL; + +Item *Item::carrotOnAStick = NULL; +Item *Item::pumpkinPie = NULL; + +EnchantedBookItem *Item::enchantedBook = NULL; +Item *Item::netherQuartz = NULL; + + +void Item::staticCtor() +{ + + + Item::sword_wood = ( new WeaponItem(12, _Tier::WOOD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_sword, eMaterial_wood) ->setTextureName(L"swordWood")->setDescriptionId(IDS_ITEM_SWORD_WOOD)->setUseDescriptionId(IDS_DESC_SWORD); + Item::sword_stone = ( new WeaponItem(16, _Tier::STONE) ) ->setBaseItemTypeAndMaterial(eBaseItemType_sword, eMaterial_stone) ->setTextureName(L"swordStone")->setDescriptionId(IDS_ITEM_SWORD_STONE)->setUseDescriptionId(IDS_DESC_SWORD); + Item::sword_iron = ( new WeaponItem(11, _Tier::IRON) ) ->setBaseItemTypeAndMaterial(eBaseItemType_sword, eMaterial_iron) ->setTextureName(L"swordIron")->setDescriptionId(IDS_ITEM_SWORD_IRON)->setUseDescriptionId(IDS_DESC_SWORD); + Item::sword_diamond = ( new WeaponItem(20, _Tier::DIAMOND) ) ->setBaseItemTypeAndMaterial(eBaseItemType_sword, eMaterial_diamond) ->setTextureName(L"swordDiamond")->setDescriptionId(IDS_ITEM_SWORD_DIAMOND)->setUseDescriptionId(IDS_DESC_SWORD); + Item::sword_gold = ( new WeaponItem(27, _Tier::GOLD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_sword, eMaterial_gold) ->setTextureName(L"swordGold")->setDescriptionId(IDS_ITEM_SWORD_GOLD)->setUseDescriptionId(IDS_DESC_SWORD); + + Item::shovel_wood = ( new ShovelItem(13, _Tier::WOOD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_shovel, eMaterial_wood) ->setTextureName(L"shovelWood")->setDescriptionId(IDS_ITEM_SHOVEL_WOOD)->setUseDescriptionId(IDS_DESC_SHOVEL); + Item::shovel_stone = ( new ShovelItem(17, _Tier::STONE) ) ->setBaseItemTypeAndMaterial(eBaseItemType_shovel, eMaterial_stone) ->setTextureName(L"shovelStone")->setDescriptionId(IDS_ITEM_SHOVEL_STONE)->setUseDescriptionId(IDS_DESC_SHOVEL); + Item::shovel_iron = ( new ShovelItem(0, _Tier::IRON) ) ->setBaseItemTypeAndMaterial(eBaseItemType_shovel, eMaterial_iron) ->setTextureName(L"shovelIron")->setDescriptionId(IDS_ITEM_SHOVEL_IRON)->setUseDescriptionId(IDS_DESC_SHOVEL); + Item::shovel_diamond = ( new ShovelItem(21, _Tier::DIAMOND) ) ->setBaseItemTypeAndMaterial(eBaseItemType_shovel, eMaterial_diamond) ->setTextureName(L"shovelDiamond")->setDescriptionId(IDS_ITEM_SHOVEL_DIAMOND)->setUseDescriptionId(IDS_DESC_SHOVEL); + Item::shovel_gold = ( new ShovelItem(28, _Tier::GOLD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_shovel, eMaterial_gold) ->setTextureName(L"shovelGold")->setDescriptionId(IDS_ITEM_SHOVEL_GOLD)->setUseDescriptionId(IDS_DESC_SHOVEL); + + Item::pickAxe_wood = ( new PickaxeItem(14, _Tier::WOOD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_pickaxe, eMaterial_wood) ->setTextureName(L"pickaxeWood")->setDescriptionId(IDS_ITEM_PICKAXE_WOOD)->setUseDescriptionId(IDS_DESC_PICKAXE); + Item::pickAxe_stone = ( new PickaxeItem(18, _Tier::STONE) ) ->setBaseItemTypeAndMaterial(eBaseItemType_pickaxe, eMaterial_stone) ->setTextureName(L"pickaxeStone")->setDescriptionId(IDS_ITEM_PICKAXE_STONE)->setUseDescriptionId(IDS_DESC_PICKAXE); + Item::pickAxe_iron = ( new PickaxeItem(1, _Tier::IRON) ) ->setBaseItemTypeAndMaterial(eBaseItemType_pickaxe, eMaterial_iron) ->setTextureName(L"pickaxeIron")->setDescriptionId(IDS_ITEM_PICKAXE_IRON)->setUseDescriptionId(IDS_DESC_PICKAXE); + Item::pickAxe_diamond = ( new PickaxeItem(22, _Tier::DIAMOND) ) ->setBaseItemTypeAndMaterial(eBaseItemType_pickaxe, eMaterial_diamond) ->setTextureName(L"pickaxeDiamond")->setDescriptionId(IDS_ITEM_PICKAXE_DIAMOND)->setUseDescriptionId(IDS_DESC_PICKAXE); + Item::pickAxe_gold = ( new PickaxeItem(29, _Tier::GOLD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_pickaxe, eMaterial_gold) ->setTextureName(L"pickaxeGold")->setDescriptionId(IDS_ITEM_PICKAXE_GOLD)->setUseDescriptionId(IDS_DESC_PICKAXE); + + Item::hatchet_wood = ( new HatchetItem(15, _Tier::WOOD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_hatchet, eMaterial_wood) ->setTextureName(L"hatchetWood")->setDescriptionId(IDS_ITEM_HATCHET_WOOD)->setUseDescriptionId(IDS_DESC_HATCHET); + Item::hatchet_stone = ( new HatchetItem(19, _Tier::STONE) ) ->setBaseItemTypeAndMaterial(eBaseItemType_hatchet, eMaterial_stone) ->setTextureName(L"hatchetStone")->setDescriptionId(IDS_ITEM_HATCHET_STONE)->setUseDescriptionId(IDS_DESC_HATCHET); + Item::hatchet_iron = ( new HatchetItem(2, _Tier::IRON) ) ->setBaseItemTypeAndMaterial(eBaseItemType_hatchet, eMaterial_iron) ->setTextureName(L"hatchetIron")->setDescriptionId(IDS_ITEM_HATCHET_IRON)->setUseDescriptionId(IDS_DESC_HATCHET); + Item::hatchet_diamond = ( new HatchetItem(23, _Tier::DIAMOND) ) ->setBaseItemTypeAndMaterial(eBaseItemType_hatchet, eMaterial_diamond) ->setTextureName(L"hatchetDiamond")->setDescriptionId(IDS_ITEM_HATCHET_DIAMOND)->setUseDescriptionId(IDS_DESC_HATCHET); + Item::hatchet_gold = ( new HatchetItem(30, _Tier::GOLD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_hatchet, eMaterial_gold) ->setTextureName(L"hatchetGold")->setDescriptionId(IDS_ITEM_HATCHET_GOLD)->setUseDescriptionId(IDS_DESC_HATCHET); + + Item::hoe_wood = ( new HoeItem(34, _Tier::WOOD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_hoe, eMaterial_wood) ->setTextureName(L"hoeWood")->setDescriptionId(IDS_ITEM_HOE_WOOD)->setUseDescriptionId(IDS_DESC_HOE); + Item::hoe_stone = ( new HoeItem(35, _Tier::STONE) ) ->setBaseItemTypeAndMaterial(eBaseItemType_hoe, eMaterial_stone) ->setTextureName(L"hoeStone")->setDescriptionId(IDS_ITEM_HOE_STONE)->setUseDescriptionId(IDS_DESC_HOE); + Item::hoe_iron = ( new HoeItem(36, _Tier::IRON) ) ->setBaseItemTypeAndMaterial(eBaseItemType_hoe, eMaterial_iron) ->setTextureName(L"hoeIron")->setDescriptionId(IDS_ITEM_HOE_IRON)->setUseDescriptionId(IDS_DESC_HOE); + Item::hoe_diamond = ( new HoeItem(37, _Tier::DIAMOND) ) ->setBaseItemTypeAndMaterial(eBaseItemType_hoe, eMaterial_diamond) ->setTextureName(L"hoeDiamond")->setDescriptionId(IDS_ITEM_HOE_DIAMOND)->setUseDescriptionId(IDS_DESC_HOE); + Item::hoe_gold = ( new HoeItem(38, _Tier::GOLD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_hoe, eMaterial_gold) ->setTextureName(L"hoeGold")->setDescriptionId(IDS_ITEM_HOE_GOLD)->setUseDescriptionId(IDS_DESC_HOE); + + Item::door_wood = ( new DoorItem(68, Material::wood) ) ->setBaseItemTypeAndMaterial(eBaseItemType_door, eMaterial_wood)->setTextureName(L"doorWood")->setDescriptionId(IDS_ITEM_DOOR_WOOD)->setUseDescriptionId(IDS_DESC_DOOR_WOOD); + Item::door_iron = ( new DoorItem(74, Material::metal) ) ->setBaseItemTypeAndMaterial(eBaseItemType_door, eMaterial_iron)->setTextureName(L"doorIron")->setDescriptionId(IDS_ITEM_DOOR_IRON)->setUseDescriptionId(IDS_DESC_DOOR_IRON); + + Item::helmet_cloth = (ArmorItem *) ( ( new ArmorItem(42, ArmorItem::ArmorMaterial::CLOTH, 0, ArmorItem::SLOT_HEAD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_helmet, eMaterial_cloth) ->setTextureName(L"helmetCloth")->setDescriptionId(IDS_ITEM_HELMET_CLOTH)->setUseDescriptionId(IDS_DESC_HELMET_LEATHER) ); + Item::helmet_iron = (ArmorItem *) ( ( new ArmorItem(50, ArmorItem::ArmorMaterial::IRON, 2, ArmorItem::SLOT_HEAD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_helmet, eMaterial_iron) ->setTextureName(L"helmetIron")->setDescriptionId(IDS_ITEM_HELMET_IRON)->setUseDescriptionId(IDS_DESC_HELMET_IRON) ); + Item::helmet_diamond = (ArmorItem *) ( ( new ArmorItem(54, ArmorItem::ArmorMaterial::DIAMOND, 3, ArmorItem::SLOT_HEAD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_helmet, eMaterial_diamond) ->setTextureName(L"helmetDiamond")->setDescriptionId(IDS_ITEM_HELMET_DIAMOND)->setUseDescriptionId(IDS_DESC_HELMET_DIAMOND) ); + Item::helmet_gold = (ArmorItem *) ( ( new ArmorItem(58, ArmorItem::ArmorMaterial::GOLD, 4, ArmorItem::SLOT_HEAD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_helmet, eMaterial_gold) ->setTextureName(L"helmetGold")->setDescriptionId(IDS_ITEM_HELMET_GOLD)->setUseDescriptionId(IDS_DESC_HELMET_GOLD) ); + + Item::chestplate_cloth = (ArmorItem *) ( ( new ArmorItem(43, ArmorItem::ArmorMaterial::CLOTH, 0, ArmorItem::SLOT_TORSO) ) ->setBaseItemTypeAndMaterial(eBaseItemType_chestplate, eMaterial_cloth) ->setTextureName(L"chestplateCloth")->setDescriptionId(IDS_ITEM_CHESTPLATE_CLOTH)->setUseDescriptionId(IDS_DESC_CHESTPLATE_LEATHER) ); + Item::chestplate_iron = (ArmorItem *) ( ( new ArmorItem(51, ArmorItem::ArmorMaterial::IRON, 2, ArmorItem::SLOT_TORSO) ) ->setBaseItemTypeAndMaterial(eBaseItemType_chestplate, eMaterial_iron) ->setTextureName(L"chestplateIron")->setDescriptionId(IDS_ITEM_CHESTPLATE_IRON)->setUseDescriptionId(IDS_DESC_CHESTPLATE_IRON) ); + Item::chestplate_diamond = (ArmorItem *) ( ( new ArmorItem(55, ArmorItem::ArmorMaterial::DIAMOND, 3, ArmorItem::SLOT_TORSO) ) ->setBaseItemTypeAndMaterial(eBaseItemType_chestplate, eMaterial_diamond) ->setTextureName(L"chestplateDiamond")->setDescriptionId(IDS_ITEM_CHESTPLATE_DIAMOND)->setUseDescriptionId(IDS_DESC_CHESTPLATE_DIAMOND) ); + Item::chestplate_gold = (ArmorItem *) ( ( new ArmorItem(59, ArmorItem::ArmorMaterial::GOLD, 4, ArmorItem::SLOT_TORSO) ) ->setBaseItemTypeAndMaterial(eBaseItemType_chestplate, eMaterial_gold) ->setTextureName(L"chestplateGold")->setDescriptionId(IDS_ITEM_CHESTPLATE_GOLD)->setUseDescriptionId(IDS_DESC_CHESTPLATE_GOLD) ); + + Item::leggings_cloth = (ArmorItem *) ( ( new ArmorItem(44, ArmorItem::ArmorMaterial::CLOTH, 0, ArmorItem::SLOT_LEGS) ) ->setBaseItemTypeAndMaterial(eBaseItemType_leggings, eMaterial_cloth) ->setTextureName(L"leggingsCloth")->setDescriptionId(IDS_ITEM_LEGGINGS_CLOTH)->setUseDescriptionId(IDS_DESC_LEGGINGS_LEATHER) ); + Item::leggings_iron = (ArmorItem *) ( ( new ArmorItem(52, ArmorItem::ArmorMaterial::IRON, 2, ArmorItem::SLOT_LEGS) ) ->setBaseItemTypeAndMaterial(eBaseItemType_leggings, eMaterial_iron) ->setTextureName(L"leggingsIron")->setDescriptionId(IDS_ITEM_LEGGINGS_IRON)->setUseDescriptionId(IDS_DESC_LEGGINGS_IRON) ); + Item::leggings_diamond = (ArmorItem *) ( ( new ArmorItem(56, ArmorItem::ArmorMaterial::DIAMOND, 3, ArmorItem::SLOT_LEGS) ) ->setBaseItemTypeAndMaterial(eBaseItemType_leggings, eMaterial_diamond) ->setTextureName(L"leggingsDiamond")->setDescriptionId(IDS_ITEM_LEGGINGS_DIAMOND)->setUseDescriptionId(IDS_DESC_LEGGINGS_DIAMOND) ); + Item::leggings_gold = (ArmorItem *) ( ( new ArmorItem(60, ArmorItem::ArmorMaterial::GOLD, 4, ArmorItem::SLOT_LEGS) ) ->setBaseItemTypeAndMaterial(eBaseItemType_leggings, eMaterial_gold) ->setTextureName(L"leggingsGold")->setDescriptionId(IDS_ITEM_LEGGINGS_GOLD)->setUseDescriptionId(IDS_DESC_LEGGINGS_GOLD) ); + + Item::helmet_chain = (ArmorItem *) ( ( new ArmorItem(46, ArmorItem::ArmorMaterial::CHAIN, 1, ArmorItem::SLOT_HEAD) ) ->setBaseItemTypeAndMaterial(eBaseItemType_helmet, eMaterial_chain) ->setTextureName(L"helmetChain")->setDescriptionId(IDS_ITEM_HELMET_CHAIN)->setUseDescriptionId(IDS_DESC_HELMET_CHAIN) ); + Item::chestplate_chain = (ArmorItem *) ( ( new ArmorItem(47, ArmorItem::ArmorMaterial::CHAIN, 1, ArmorItem::SLOT_TORSO) ) ->setBaseItemTypeAndMaterial(eBaseItemType_chestplate, eMaterial_chain) ->setTextureName(L"chestplateChain")->setDescriptionId(IDS_ITEM_CHESTPLATE_CHAIN)->setUseDescriptionId(IDS_DESC_CHESTPLATE_CHAIN) ); + Item::leggings_chain = (ArmorItem *) ( ( new ArmorItem(48, ArmorItem::ArmorMaterial::CHAIN, 1, ArmorItem::SLOT_LEGS) ) ->setBaseItemTypeAndMaterial(eBaseItemType_leggings, eMaterial_chain) ->setTextureName(L"leggingsChain")->setDescriptionId(IDS_ITEM_LEGGINGS_CHAIN)->setUseDescriptionId(IDS_DESC_LEGGINGS_CHAIN) ); + Item::boots_chain = (ArmorItem *) ( ( new ArmorItem(49, ArmorItem::ArmorMaterial::CHAIN, 1, ArmorItem::SLOT_FEET) ) ->setBaseItemTypeAndMaterial(eBaseItemType_boots, eMaterial_chain) ->setTextureName(L"bootsChain")->setDescriptionId(IDS_ITEM_BOOTS_CHAIN)->setUseDescriptionId(IDS_DESC_BOOTS_CHAIN) ); + + Item::boots_cloth = (ArmorItem *) ( ( new ArmorItem(45, ArmorItem::ArmorMaterial::CLOTH, 0, ArmorItem::SLOT_FEET) ) ->setBaseItemTypeAndMaterial(eBaseItemType_boots, eMaterial_cloth) ->setTextureName(L"bootsCloth")->setDescriptionId(IDS_ITEM_BOOTS_CLOTH)->setUseDescriptionId(IDS_DESC_BOOTS_LEATHER) ); + Item::boots_iron = (ArmorItem *) ( ( new ArmorItem(53, ArmorItem::ArmorMaterial::IRON, 2, ArmorItem::SLOT_FEET) ) ->setBaseItemTypeAndMaterial(eBaseItemType_boots, eMaterial_iron) ->setTextureName(L"bootsIron")->setDescriptionId(IDS_ITEM_BOOTS_IRON)->setUseDescriptionId(IDS_DESC_BOOTS_IRON) ); + Item::boots_diamond = (ArmorItem *) ( ( new ArmorItem(57, ArmorItem::ArmorMaterial::DIAMOND, 3, ArmorItem::SLOT_FEET) ) ->setBaseItemTypeAndMaterial(eBaseItemType_boots, eMaterial_diamond) ->setTextureName(L"bootsDiamond")->setDescriptionId(IDS_ITEM_BOOTS_DIAMOND)->setUseDescriptionId(IDS_DESC_BOOTS_DIAMOND) ); + Item::boots_gold = (ArmorItem *) ( ( new ArmorItem(61, ArmorItem::ArmorMaterial::GOLD, 4, ArmorItem::SLOT_FEET) ) ->setBaseItemTypeAndMaterial(eBaseItemType_boots, eMaterial_gold) ->setTextureName(L"bootsGold")->setDescriptionId(IDS_ITEM_BOOTS_GOLD)->setUseDescriptionId(IDS_DESC_BOOTS_GOLD) ); + + Item::ironIngot = ( new Item(9) )->setTextureName(L"ingotIron") ->setBaseItemTypeAndMaterial(eBaseItemType_treasure, eMaterial_iron)->setDescriptionId(IDS_ITEM_INGOT_IRON)->setUseDescriptionId(IDS_DESC_INGOT); + Item::goldIngot = ( new Item(10) )->setTextureName(L"ingotGold") ->setBaseItemTypeAndMaterial(eBaseItemType_treasure, eMaterial_gold)->setDescriptionId(IDS_ITEM_INGOT_GOLD)->setUseDescriptionId(IDS_DESC_INGOT); + + + // 4J-PB - todo - add materials and base types to the ones below + Item::bucket_empty = ( new BucketItem(69, 0) ) ->setBaseItemTypeAndMaterial(eBaseItemType_utensil, eMaterial_water)->setTextureName(L"bucket")->setDescriptionId(IDS_ITEM_BUCKET)->setUseDescriptionId(IDS_DESC_BUCKET)->setMaxStackSize(16); + Item::bowl = ( new Item(25) ) ->setBaseItemTypeAndMaterial(eBaseItemType_utensil, eMaterial_wood)->setTextureName(L"bowl")->setDescriptionId(IDS_ITEM_BOWL)->setUseDescriptionId(IDS_DESC_BOWL)->setMaxStackSize(64); + + Item::bucket_water = ( new BucketItem(70, Tile::water_Id) ) ->setTextureName(L"bucketWater")->setDescriptionId(IDS_ITEM_BUCKET_WATER)->setCraftingRemainingItem(Item::bucket_empty)->setUseDescriptionId(IDS_DESC_BUCKET_WATER); + Item::bucket_lava = ( new BucketItem(71, Tile::lava_Id) ) ->setTextureName(L"bucketLava")->setDescriptionId(IDS_ITEM_BUCKET_LAVA)->setCraftingRemainingItem(Item::bucket_empty)->setUseDescriptionId(IDS_DESC_BUCKET_LAVA); + Item::milk = ( new MilkBucketItem(79) )->setTextureName(L"milk")->setDescriptionId(IDS_ITEM_BUCKET_MILK)->setCraftingRemainingItem(Item::bucket_empty)->setUseDescriptionId(IDS_DESC_BUCKET_MILK); + + Item::bow = (BowItem *)( new BowItem(5) ) ->setTextureName(L"bow")->setBaseItemTypeAndMaterial(eBaseItemType_bow, eMaterial_bow) ->setDescriptionId(IDS_ITEM_BOW)->setUseDescriptionId(IDS_DESC_BOW); + Item::arrow = ( new Item(6) ) ->setTextureName(L"arrow")->setBaseItemTypeAndMaterial(eBaseItemType_bow, eMaterial_arrow) ->setDescriptionId(IDS_ITEM_ARROW)->setUseDescriptionId(IDS_DESC_ARROW); + + Item::compass = ( new CompassItem(89) ) ->setTextureName(L"compass")->setBaseItemTypeAndMaterial(eBaseItemType_pockettool, eMaterial_compass) ->setDescriptionId(IDS_ITEM_COMPASS)->setUseDescriptionId(IDS_DESC_COMPASS); + Item::clock = ( new ClockItem(91) ) ->setTextureName(L"clock")->setBaseItemTypeAndMaterial(eBaseItemType_pockettool, eMaterial_clock) ->setDescriptionId(IDS_ITEM_CLOCK)->setUseDescriptionId(IDS_DESC_CLOCK); + Item::map = (MapItem *) ( new MapItem(102) ) ->setTextureName(L"map")->setBaseItemTypeAndMaterial(eBaseItemType_pockettool, eMaterial_map) ->setDescriptionId(IDS_ITEM_MAP)->setUseDescriptionId(IDS_DESC_MAP); + + Item::flintAndSteel = ( new FlintAndSteelItem(3) ) ->setTextureName(L"flintAndSteel")->setBaseItemTypeAndMaterial(eBaseItemType_devicetool, eMaterial_flintandsteel)->setDescriptionId(IDS_ITEM_FLINT_AND_STEEL)->setUseDescriptionId(IDS_DESC_FLINTANDSTEEL); + Item::apple = ( new FoodItem(4, 4, FoodConstants::FOOD_SATURATION_LOW, false) ) ->setTextureName(L"apple")->setDescriptionId(IDS_ITEM_APPLE)->setUseDescriptionId(IDS_DESC_APPLE); + Item::coal = ( new CoalItem(7) ) ->setTextureName(L"coal")->setDescriptionId(IDS_ITEM_COAL)->setUseDescriptionId(IDS_DESC_COAL); + Item::diamond = ( new Item(8) ) ->setBaseItemTypeAndMaterial(eBaseItemType_treasure, eMaterial_diamond)->setTextureName(L"diamond")->setDescriptionId(IDS_ITEM_DIAMOND)->setUseDescriptionId(IDS_DESC_DIAMONDS); + Item::stick = ( new Item(24) ) ->setTextureName(L"stick")->handEquipped()->setDescriptionId(IDS_ITEM_STICK)->setUseDescriptionId(IDS_DESC_STICK); + Item::mushroomStew = ( new BowlFoodItem(26, 6) ) ->setTextureName(L"mushroomStew")->setDescriptionId(IDS_ITEM_MUSHROOM_STEW)->setUseDescriptionId(IDS_DESC_MUSHROOMSTEW); + + Item::string = ( new TilePlanterItem(31, Tile::tripWire) ) ->setTextureName(L"string")->setDescriptionId(IDS_ITEM_STRING)->setUseDescriptionId(IDS_DESC_STRING); + Item::feather = ( new Item(32) ) ->setTextureName(L"feather")->setDescriptionId(IDS_ITEM_FEATHER)->setUseDescriptionId(IDS_DESC_FEATHER); + Item::sulphur = ( new Item(33) ) ->setTextureName(L"sulphur")->setDescriptionId(IDS_ITEM_SULPHUR)->setUseDescriptionId(IDS_DESC_SULPHUR)->setPotionBrewingFormula(PotionBrewing::MOD_GUNPOWDER); + + + Item::seeds_wheat = ( new SeedItem(39, Tile::crops_Id, Tile::farmland_Id) ) ->setTextureName(L"seeds")->setDescriptionId(IDS_ITEM_WHEAT_SEEDS)->setUseDescriptionId(IDS_DESC_WHEAT_SEEDS); + Item::wheat = ( new Item(40) ) ->setTextureName(L"wheat")->setDescriptionId(IDS_ITEM_WHEAT)->setUseDescriptionId(IDS_DESC_WHEAT); + Item::bread = ( new FoodItem(41, 5, FoodConstants::FOOD_SATURATION_NORMAL, false) ) ->setTextureName(L"bread")->setDescriptionId(IDS_ITEM_BREAD)->setUseDescriptionId(IDS_DESC_BREAD); + + + Item::flint = ( new Item(62) ) ->setTextureName(L"flint")->setDescriptionId(IDS_ITEM_FLINT)->setUseDescriptionId(IDS_DESC_FLINT); + Item::porkChop_raw = ( new FoodItem(63, 3, FoodConstants::FOOD_SATURATION_LOW, true) ) ->setTextureName(L"porkchopRaw")->setDescriptionId(IDS_ITEM_PORKCHOP_RAW)->setUseDescriptionId(IDS_DESC_PORKCHOP_RAW); + Item::porkChop_cooked = ( new FoodItem(64, 8, FoodConstants::FOOD_SATURATION_GOOD, true) ) ->setTextureName(L"porkchopCooked")->setDescriptionId(IDS_ITEM_PORKCHOP_COOKED)->setUseDescriptionId(IDS_DESC_PORKCHOP_COOKED); + Item::painting = ( new HangingEntityItem(65,eTYPE_PAINTING) ) ->setBaseItemTypeAndMaterial(eBaseItemType_HangingItem, eMaterial_cloth)->setTextureName(L"painting")->setDescriptionId(IDS_ITEM_PAINTING)->setUseDescriptionId(IDS_DESC_PICTURE); + + Item::apple_gold = ( new GoldenAppleItem(66, 4, FoodConstants::FOOD_SATURATION_SUPERNATURAL, false) )->setCanAlwaysEat()->setEatEffect(MobEffect::regeneration->id, 5, 0, 1.0f) + ->setBaseItemTypeAndMaterial(eBaseItemType_giltFruit,eMaterial_apple)->setTextureName(L"appleGold")->setDescriptionId(IDS_ITEM_APPLE_GOLD);//->setUseDescriptionId(IDS_DESC_GOLDENAPPLE); + + Item::sign = ( new SignItem(67) ) ->setBaseItemTypeAndMaterial(eBaseItemType_HangingItem, eMaterial_wood)->setTextureName(L"sign")->setDescriptionId(IDS_ITEM_SIGN)->setUseDescriptionId(IDS_DESC_SIGN); + + + + Item::minecart = ( new MinecartItem(72, Minecart::RIDEABLE) ) ->setTextureName(L"minecart")->setDescriptionId(IDS_ITEM_MINECART)->setUseDescriptionId(IDS_DESC_MINECART); + Item::saddle = ( new SaddleItem(73) ) ->setTextureName(L"saddle")->setDescriptionId(IDS_ITEM_SADDLE)->setUseDescriptionId(IDS_DESC_SADDLE); + Item::redStone = ( new RedStoneItem(75) ) ->setTextureName(L"redstone")->setDescriptionId(IDS_ITEM_REDSTONE)->setUseDescriptionId(IDS_DESC_REDSTONE_DUST)->setPotionBrewingFormula(PotionBrewing::MOD_REDSTONE); + Item::snowBall = ( new SnowballItem(76) ) ->setTextureName(L"snowball")->setDescriptionId(IDS_ITEM_SNOWBALL)->setUseDescriptionId(IDS_DESC_SNOWBALL); + + Item::boat = ( new BoatItem(77) ) ->setTextureName(L"boat")->setDescriptionId(IDS_ITEM_BOAT)->setUseDescriptionId(IDS_DESC_BOAT); + + Item::leather = ( new Item(78) ) ->setTextureName(L"leather")->setDescriptionId(IDS_ITEM_LEATHER)->setUseDescriptionId(IDS_DESC_LEATHER); + Item::brick = ( new Item(80) ) ->setTextureName(L"brick")->setDescriptionId(IDS_ITEM_BRICK)->setUseDescriptionId(IDS_DESC_BRICK); + Item::clay = ( new Item(81) ) ->setTextureName(L"clay")->setDescriptionId(IDS_ITEM_CLAY)->setUseDescriptionId(IDS_DESC_CLAY); + Item::reeds = ( new TilePlanterItem(82, Tile::reeds) ) ->setTextureName(L"reeds")->setDescriptionId(IDS_ITEM_REEDS)->setUseDescriptionId(IDS_DESC_REEDS); + Item::paper = ( new Item(83) ) ->setTextureName(L"paper")->setDescriptionId(IDS_ITEM_PAPER)->setUseDescriptionId(IDS_DESC_PAPER); + Item::book = ( new BookItem(84) ) ->setTextureName(L"book")->setDescriptionId(IDS_ITEM_BOOK)->setUseDescriptionId(IDS_DESC_BOOK); + Item::slimeBall = ( new Item(85) ) ->setTextureName(L"slimeball")->setDescriptionId(IDS_ITEM_SLIMEBALL)->setUseDescriptionId(IDS_DESC_SLIMEBALL); + Item::minecart_chest = ( new MinecartItem(86, Minecart::CHEST) ) ->setTextureName(L"minecartChest")->setDescriptionId(IDS_ITEM_MINECART_CHEST)->setUseDescriptionId(IDS_DESC_MINECARTWITHCHEST); + Item::minecart_furnace = ( new MinecartItem(87, Minecart::FURNACE) )->setTextureName(L"minecartFurnace")->setDescriptionId(IDS_ITEM_MINECART_FURNACE)->setUseDescriptionId(IDS_DESC_MINECARTWITHFURNACE); + Item::egg = ( new EggItem(88) ) ->setTextureName(L"egg")->setDescriptionId(IDS_ITEM_EGG)->setUseDescriptionId(IDS_DESC_EGG); + Item::fishingRod = (FishingRodItem *)( new FishingRodItem(90) ) ->setBaseItemTypeAndMaterial(eBaseItemType_rod, eMaterial_wood)->setTextureName(L"fishingRod")->setDescriptionId(IDS_ITEM_FISHING_ROD)->setUseDescriptionId(IDS_DESC_FISHINGROD); + Item::yellowDust = ( new Item(92) ) ->setTextureName(L"yellowDust")->setDescriptionId(IDS_ITEM_YELLOW_DUST)->setUseDescriptionId(IDS_DESC_YELLOW_DUST)->setPotionBrewingFormula(PotionBrewing::MOD_GLOWSTONE); + Item::fish_raw = ( new FoodItem(93, 2, FoodConstants::FOOD_SATURATION_LOW, false) ) ->setTextureName(L"fishRaw")->setDescriptionId(IDS_ITEM_FISH_RAW)->setUseDescriptionId(IDS_DESC_FISH_RAW); + Item::fish_cooked = ( new FoodItem(94, 5, FoodConstants::FOOD_SATURATION_NORMAL, false) ) ->setTextureName(L"fishCooked")->setDescriptionId(IDS_ITEM_FISH_COOKED)->setUseDescriptionId(IDS_DESC_FISH_COOKED); + + Item::dye_powder = ( new DyePowderItem(95) ) ->setBaseItemTypeAndMaterial(eBaseItemType_dyepowder, eMaterial_dye)->setTextureName(L"dyePowder")->setDescriptionId(IDS_ITEM_DYE_POWDER)->setUseDescriptionId(-1); + + Item::bone = ( new Item(96) ) ->setTextureName(L"bone")->setDescriptionId(IDS_ITEM_BONE)->handEquipped()->setUseDescriptionId(IDS_DESC_BONE); + Item::sugar = ( new Item(97) ) ->setTextureName(L"sugar")->setDescriptionId(IDS_ITEM_SUGAR)->setUseDescriptionId(IDS_DESC_SUGAR)->setPotionBrewingFormula(PotionBrewing::MOD_SUGAR); + // 4J-PB - changing the cake to be stackable - Jens ok'ed this 23/10/12 + //Item::cake = ( new TilePlanterItem(98, Tile::cake) )->setMaxStackSize(1)->setIcon(13, 1)->setDescriptionId(IDS_ITEM_CAKE)->setUseDescriptionId(IDS_DESC_CAKE); + Item::cake = ( new TilePlanterItem(98, Tile::cake) ) ->setTextureName(L"cake")->setDescriptionId(IDS_ITEM_CAKE)->setUseDescriptionId(IDS_DESC_CAKE); + + Item::bed = ( new BedItem(99) ) ->setMaxStackSize(1)->setTextureName(L"bed")->setDescriptionId(IDS_ITEM_BED)->setUseDescriptionId(IDS_DESC_BED); + + Item::diode = ( new TilePlanterItem(100, (Tile *)Tile::diode_off) ) ->setTextureName(L"diode")->setDescriptionId(IDS_ITEM_DIODE)->setUseDescriptionId(IDS_DESC_REDSTONEREPEATER); + Item::cookie = ( new FoodItem(101, 2, FoodConstants::FOOD_SATURATION_POOR, false) ) ->setTextureName(L"cookie")->setDescriptionId(IDS_ITEM_COOKIE)->setUseDescriptionId(IDS_DESC_COOKIE); + + + Item::shears = (ShearsItem *)( new ShearsItem(103) ) ->setTextureName(L"shears")->setBaseItemTypeAndMaterial(eBaseItemType_devicetool, eMaterial_shears)->setDescriptionId(IDS_ITEM_SHEARS)->setUseDescriptionId(IDS_DESC_SHEARS); + + Item::melon = (new FoodItem(104, 2, FoodConstants::FOOD_SATURATION_LOW, false)) ->setTextureName(L"melon")->setDescriptionId(IDS_ITEM_MELON_SLICE)->setUseDescriptionId(IDS_DESC_MELON_SLICE); + + Item::seeds_pumpkin = (new SeedItem(105, Tile::pumpkinStem_Id, Tile::farmland_Id)) ->setTextureName(L"seeds_pumpkin")->setBaseItemTypeAndMaterial(eBaseItemType_seed, eMaterial_pumpkin)->setDescriptionId(IDS_ITEM_PUMPKIN_SEEDS)->setUseDescriptionId(IDS_DESC_PUMPKIN_SEEDS); + Item::seeds_melon = (new SeedItem(106, Tile::melonStem_Id, Tile::farmland_Id)) ->setTextureName(L"seeds_melon")->setBaseItemTypeAndMaterial(eBaseItemType_seed, eMaterial_melon)->setDescriptionId(IDS_ITEM_MELON_SEEDS)->setUseDescriptionId(IDS_DESC_MELON_SEEDS); + + Item::beef_raw = (new FoodItem(107, 3, FoodConstants::FOOD_SATURATION_LOW, true)) ->setTextureName(L"beefRaw")->setDescriptionId(IDS_ITEM_BEEF_RAW)->setUseDescriptionId(IDS_DESC_BEEF_RAW); + Item::beef_cooked = (new FoodItem(108, 8, FoodConstants::FOOD_SATURATION_GOOD, true))->setTextureName(L"beefCooked")->setDescriptionId(IDS_ITEM_BEEF_COOKED)->setUseDescriptionId(IDS_DESC_BEEF_COOKED); + Item::chicken_raw = (new FoodItem(109, 2, FoodConstants::FOOD_SATURATION_LOW, true))->setEatEffect(MobEffect::hunger->id, 30, 0, .3f)->setTextureName(L"chickenRaw")->setDescriptionId(IDS_ITEM_CHICKEN_RAW)->setUseDescriptionId(IDS_DESC_CHICKEN_RAW); + Item::chicken_cooked = (new FoodItem(110, 6, FoodConstants::FOOD_SATURATION_NORMAL, true))->setTextureName(L"chickenCooked")->setDescriptionId(IDS_ITEM_CHICKEN_COOKED)->setUseDescriptionId(IDS_DESC_CHICKEN_COOKED); + Item::rotten_flesh = (new FoodItem(111, 4, FoodConstants::FOOD_SATURATION_POOR, true))->setEatEffect(MobEffect::hunger->id, 30, 0, .8f)->setTextureName(L"rottenFlesh")->setDescriptionId(IDS_ITEM_ROTTEN_FLESH)->setUseDescriptionId(IDS_DESC_ROTTEN_FLESH); + + Item::enderPearl = (new EnderpearlItem(112)) ->setTextureName(L"enderPearl")->setDescriptionId(IDS_ITEM_ENDER_PEARL)->setUseDescriptionId(IDS_DESC_ENDER_PEARL); + + Item::blazeRod = (new Item(113) ) ->setTextureName(L"blazeRod")->setDescriptionId(IDS_ITEM_BLAZE_ROD)->setUseDescriptionId(IDS_DESC_BLAZE_ROD)->handEquipped(); + Item::ghastTear = (new Item(114) ) ->setTextureName(L"ghastTear")->setDescriptionId(IDS_ITEM_GHAST_TEAR)->setUseDescriptionId(IDS_DESC_GHAST_TEAR)->setPotionBrewingFormula(PotionBrewing::MOD_GHASTTEARS); + Item::goldNugget = (new Item(115) ) ->setBaseItemTypeAndMaterial(eBaseItemType_treasure, eMaterial_gold)->setTextureName(L"goldNugget")->setDescriptionId(IDS_ITEM_GOLD_NUGGET)->setUseDescriptionId(IDS_DESC_GOLD_NUGGET); + + Item::netherStalkSeeds = (new SeedItem(116, Tile::netherStalk_Id, Tile::hellSand_Id) ) ->setTextureName(L"netherStalkSeeds")->setDescriptionId(IDS_ITEM_NETHER_STALK_SEEDS)->setUseDescriptionId(IDS_DESC_NETHER_STALK_SEEDS)->setPotionBrewingFormula(PotionBrewing::MOD_NETHERWART); + + Item::potion = (PotionItem *) ( ( new PotionItem(117) ) ->setTextureName(L"potion")->setDescriptionId(IDS_ITEM_POTION)->setUseDescriptionId(IDS_DESC_POTION) ); + Item::glassBottle = (new BottleItem(118) ) ->setBaseItemTypeAndMaterial(eBaseItemType_utensil, eMaterial_glass)->setTextureName(L"glassBottle")->setDescriptionId(IDS_ITEM_GLASS_BOTTLE)->setUseDescriptionId(IDS_DESC_GLASS_BOTTLE); + + Item::spiderEye = (new FoodItem(119, 2, FoodConstants::FOOD_SATURATION_GOOD, false) ) ->setEatEffect(MobEffect::poison->id, 5, 0, 1.0f)->setTextureName(L"spiderEye")->setDescriptionId(IDS_ITEM_SPIDER_EYE)->setUseDescriptionId(IDS_DESC_SPIDER_EYE)->setPotionBrewingFormula(PotionBrewing::MOD_SPIDEREYE); + Item::fermentedSpiderEye = (new Item(120) ) ->setTextureName(L"fermentedSpiderEye")->setDescriptionId(IDS_ITEM_FERMENTED_SPIDER_EYE)->setUseDescriptionId(IDS_DESC_FERMENTED_SPIDER_EYE)->setPotionBrewingFormula(PotionBrewing::MOD_FERMENTEDEYE); + + Item::blazePowder = (new Item(121) ) ->setTextureName(L"blazePowder")->setDescriptionId(IDS_ITEM_BLAZE_POWDER)->setUseDescriptionId(IDS_DESC_BLAZE_POWDER)->setPotionBrewingFormula(PotionBrewing::MOD_BLAZEPOWDER); + Item::magmaCream = (new Item(122) ) ->setTextureName(L"magmaCream")->setDescriptionId(IDS_ITEM_MAGMA_CREAM)->setUseDescriptionId(IDS_DESC_MAGMA_CREAM)->setPotionBrewingFormula(PotionBrewing::MOD_MAGMACREAM); + + Item::brewingStand = (new TilePlanterItem(123, Tile::brewingStand) ) ->setBaseItemTypeAndMaterial(eBaseItemType_device, eMaterial_blaze)->setTextureName(L"brewingStand")->setDescriptionId(IDS_ITEM_BREWING_STAND)->setUseDescriptionId(IDS_DESC_BREWING_STAND); + Item::cauldron = (new TilePlanterItem(124, Tile::cauldron) ) ->setBaseItemTypeAndMaterial(eBaseItemType_utensil, eMaterial_iron)->setTextureName(L"cauldron")->setDescriptionId(IDS_ITEM_CAULDRON)->setUseDescriptionId(IDS_DESC_CAULDRON); + Item::eyeOfEnder = (new EnderEyeItem(125) ) ->setBaseItemTypeAndMaterial(eBaseItemType_pockettool, eMaterial_ender)->setTextureName(L"eyeOfEnder")->setDescriptionId(IDS_ITEM_EYE_OF_ENDER)->setUseDescriptionId(IDS_DESC_EYE_OF_ENDER); + Item::speckledMelon = (new Item(126) ) ->setBaseItemTypeAndMaterial(eBaseItemType_giltFruit, eMaterial_melon)->setTextureName(L"speckledMelon")->setDescriptionId(IDS_ITEM_SPECKLED_MELON)->setUseDescriptionId(IDS_DESC_SPECKLED_MELON)->setPotionBrewingFormula(PotionBrewing::MOD_SPECKLEDMELON); + + Item::monsterPlacer = (new MonsterPlacerItem(127)) ->setTextureName(L"monsterPlacer")->setDescriptionId(IDS_ITEM_MONSTER_SPAWNER)->setUseDescriptionId(IDS_DESC_MONSTER_SPAWNER); + + // 4J Stu - Brought this forward + Item::expBottle = (new ExperienceItem(128)) ->setTextureName(L"expBottle")->setDescriptionId(IDS_ITEM_EXP_BOTTLE)->setUseDescriptionId(IDS_DESC_EXP_BOTTLE); + + Item::record_01 = ( new RecordingItem(2000, L"13") ) ->setTextureName(L"record")->setDescriptionId(IDS_ITEM_RECORD_01)->setUseDescriptionId(IDS_DESC_RECORD); + Item::record_02 = ( new RecordingItem(2001, L"cat") ) ->setTextureName(L"record")->setDescriptionId(IDS_ITEM_RECORD_02)->setUseDescriptionId(IDS_DESC_RECORD); + + // 4J - new records brought forward from 1.2.3 + Item::record_03 = ( new RecordingItem(2002, L"blocks") ) ->setTextureName(L"record")->setDescriptionId(IDS_ITEM_RECORD_03)->setUseDescriptionId(IDS_DESC_RECORD); + Item::record_04 = ( new RecordingItem(2003, L"chirp") ) ->setTextureName(L"record")->setDescriptionId(IDS_ITEM_RECORD_04)->setUseDescriptionId(IDS_DESC_RECORD); + Item::record_05 = ( new RecordingItem(2004, L"far") ) ->setTextureName(L"record")->setDescriptionId(IDS_ITEM_RECORD_05)->setUseDescriptionId(IDS_DESC_RECORD); + Item::record_06 = ( new RecordingItem(2005, L"mall") ) ->setTextureName(L"record")->setDescriptionId(IDS_ITEM_RECORD_06)->setUseDescriptionId(IDS_DESC_RECORD); + Item::record_07 = ( new RecordingItem(2006, L"mellohi") ) ->setTextureName(L"record")->setDescriptionId(IDS_ITEM_RECORD_07)->setUseDescriptionId(IDS_DESC_RECORD); + Item::record_09 = ( new RecordingItem(2007, L"stal") ) ->setTextureName(L"record")->setDescriptionId(IDS_ITEM_RECORD_08)->setUseDescriptionId(IDS_DESC_RECORD); + Item::record_10 = ( new RecordingItem(2008, L"strad") ) ->setTextureName(L"record")->setDescriptionId(IDS_ITEM_RECORD_09)->setUseDescriptionId(IDS_DESC_RECORD); + Item::record_11 = ( new RecordingItem(2009, L"ward") ) ->setTextureName(L"record")->setDescriptionId(IDS_ITEM_RECORD_10)->setUseDescriptionId(IDS_DESC_RECORD); + Item::record_12 = ( new RecordingItem(2010, L"11") ) ->setTextureName(L"record")->setDescriptionId(IDS_ITEM_RECORD_11)->setUseDescriptionId(IDS_DESC_RECORD); + Item::record_08 = ( new RecordingItem(2011, L"where are we now") ) ->setTextureName(L"record")->setDescriptionId(IDS_ITEM_RECORD_12)->setUseDescriptionId(IDS_DESC_RECORD); + + + // TU9 + // putting the fire charge in as a torch, so that it stacks without being near the middle of the selection boxes + Item::fireball = (new FireChargeItem(129)) ->setBaseItemTypeAndMaterial(eBaseItemType_torch, eMaterial_setfire)->setTextureName(L"fireball")->setDescriptionId(IDS_ITEM_FIREBALL)->setUseDescriptionId(IDS_DESC_FIREBALL); + Item::frame = (new HangingEntityItem(133,eTYPE_ITEM_FRAME)) ->setBaseItemTypeAndMaterial(eBaseItemType_HangingItem, eMaterial_glass)->setTextureName(L"frame")->setDescriptionId(IDS_ITEM_ITEMFRAME)->setUseDescriptionId(IDS_DESC_ITEMFRAME); + Item::netherbrick = (new Item(149)) ->setTextureName(L"netherbrick")->setDescriptionId(IDS_ITEM_NETHERBRICK)->setUseDescriptionId(IDS_DESC_ITEM_NETHERBRICK); + + // TU12 + Item::skull = (new SkullItem(141)) ->setTextureName(L"skull")->setDescriptionId(IDS_ITEM_SKULL)->setUseDescriptionId(IDS_DESC_SKULL); + + // TU14 + //Item::writingBook = (new WritingBookItem(130))->setIcon(11, 11)->setDescriptionId("writingBook"); + //Item::writtenBook = (new WrittenBookItem(131))->setIcon(12, 11)->setDescriptionId("writtenBook"); + + Item::emerald = (new Item(132)) ->setBaseItemTypeAndMaterial(eBaseItemType_treasure, eMaterial_emerald)->setTextureName(L"emerald")->setDescriptionId(IDS_ITEM_EMERALD)->setUseDescriptionId(IDS_DESC_EMERALD); + + Item::flowerPot = (new TilePlanterItem(134, Tile::flowerPot)) ->setTextureName(L"flowerPot")->setDescriptionId(IDS_FLOWERPOT)->setUseDescriptionId(IDS_DESC_FLOWERPOT); + + Item::carrots = (new SeedFoodItem(135, 4, FoodConstants::FOOD_SATURATION_NORMAL, Tile::carrots_Id, Tile::farmland_Id)) ->setTextureName(L"carrots")->setDescriptionId(IDS_CARROTS)->setUseDescriptionId(IDS_DESC_CARROTS); + Item::potato = (new SeedFoodItem(136, 1, FoodConstants::FOOD_SATURATION_LOW, Tile::potatoes_Id, Tile::farmland_Id)) ->setTextureName(L"potato")->setDescriptionId(IDS_POTATO)->setUseDescriptionId(IDS_DESC_POTATO); + Item::potatoBaked = (new FoodItem(137, 6, FoodConstants::FOOD_SATURATION_NORMAL, false)) ->setTextureName(L"potatoBaked")->setDescriptionId(IDS_ITEM_POTATO_BAKED)->setUseDescriptionId(IDS_DESC_POTATO_BAKED); + Item::potatoPoisonous = (new FoodItem(138, 2, FoodConstants::FOOD_SATURATION_LOW, false)) ->setEatEffect(MobEffect::poison->id, 5, 0, .6f)->setTextureName(L"potatoPoisonous")->setDescriptionId(IDS_ITEM_POTATO_POISONOUS)->setUseDescriptionId(IDS_DESC_POTATO_POISONOUS); + + Item::carrotGolden = (new FoodItem(140, 6, FoodConstants::FOOD_SATURATION_SUPERNATURAL, false)) ->setBaseItemTypeAndMaterial(eBaseItemType_giltFruit, eMaterial_carrot)->setTextureName(L"carrotGolden")->setPotionBrewingFormula(PotionBrewing::MOD_GOLDENCARROT)->setDescriptionId(IDS_ITEM_CARROT_GOLDEN)->setUseDescriptionId(IDS_DESC_CARROT_GOLDEN); + + Item::carrotOnAStick = (new CarrotOnAStickItem(142)) ->setBaseItemTypeAndMaterial(eBaseItemType_rod, eMaterial_carrot)->setTextureName(L"carrotOnAStick")->setDescriptionId(IDS_ITEM_CARROT_ON_A_STICK)->setUseDescriptionId(IDS_DESC_CARROT_ON_A_STICK); + Item::pumpkinPie = (new FoodItem(144, 8, FoodConstants::FOOD_SATURATION_LOW, false)) ->setTextureName(L"pumpkinPie")->setDescriptionId(IDS_ITEM_PUMPKIN_PIE)->setUseDescriptionId(IDS_DESC_PUMPKIN_PIE); + + EnchantedBookItem::enchantedBook = (EnchantedBookItem *)(new EnchantedBookItem(147)) ->setMaxStackSize(1)->setTextureName(L"enchantedBook")->setDescriptionId(IDS_ITEM_ENCHANTED_BOOK)->setUseDescriptionId(IDS_DESC_ENCHANTED_BOOK); + Item::netherQuartz = (new Item(150))->setTextureName(L"netherquartz")->setDescriptionId(IDS_ITEM_NETHER_QUARTZ)->setUseDescriptionId(IDS_DESC_NETHER_QUARTZ); +} + + +// 4J Stu - We need to do this after the staticCtor AND after staticCtors for other class +// eg Recipes +void Item::staticInit() +{ + Stats::buildItemStats(); +} + + +_Tier::Tier(int level, int uses, float speed, int damage, int enchantmentValue) : +level( level ), + uses( uses ), + speed( speed ), + damage( damage ), + enchantmentValue( enchantmentValue ) +{ +} + + +int _Tier::getUses() const +{ + return uses; +} + +float _Tier::getSpeed() const +{ + return speed; +} + +int _Tier::getAttackDamageBonus() const +{ + return damage; +} + +int _Tier::getLevel() const +{ + return level; +} + +int _Tier::getEnchantmentValue() const +{ + return enchantmentValue; +} + +int _Tier::getTierItemId() const +{ + if (this == Tier::WOOD) + { + return Tile::wood_Id; + } + else if (this == Tier::STONE) + { + return Tile::stoneBrick_Id; + } + else if (this == Tier::GOLD) + { + return Item::goldIngot_Id; + } + else if (this == Tier::IRON) + { + return Item::ironIngot_Id; + } + else if (this == Tier::DIAMOND) + { + return Item::diamond_Id; + } + return 0; +} + +Item::Item(int id) : id( 256 + id ) +{ + maxStackSize = Item::MAX_STACK_SIZE; + maxDamage = 0; + icon = NULL; + m_handEquipped = false; + m_isStackedByData = false; + + craftingRemainingItem = NULL; + potionBrewingFormula = L""; + + m_iMaterial=eMaterial_undefined; + m_iBaseItemType=eBaseItemType_undefined; + m_textureName = L""; + + // TODO Init this string + //string descriptionId; + + //this->id = 256 + id; + if (items[256 + id] != NULL) + { + app.DebugPrintf("CONFLICT @ %d" , id); + } + + items[256 + id] = this; +} + +// 4J-PB - adding so we can class different items together for the new crafting menu +// so pickaxe_stone would get tagged with pickaxe and stone +Item *Item::setBaseItemTypeAndMaterial(int iType,int iMaterial) +{ + this->m_iBaseItemType = iType; + this->m_iMaterial = iMaterial; + return this; +} + +int Item::getBaseItemType() +{ + return this->m_iBaseItemType; +} + +int Item::getMaterial() +{ + return this->m_iMaterial; +} + +Item *Item::setTextureName(const wstring &name) +{ + m_textureName = name; + + return this; +} + +Item *Item::setMaxStackSize(int max) +{ + maxStackSize = max; + return this; +} + +int Item::getIconType() +{ + return Icon::TYPE_ITEM; +} + +Icon *Item::getIcon(int auxValue) +{ + return icon; +} + +Icon *Item::getIcon(shared_ptr itemInstance) +{ + return getIcon(itemInstance->getAuxValue()); +} + +const bool Item::useOn(shared_ptr itemInstance, Level *level, int x, int y, int z, int face, bool bTestUseOnOnly) +{ + return false; +} + +bool Item::useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + return false; +} + +float Item::getDestroySpeed(shared_ptr itemInstance, Tile *tile) +{ + return 1; +} + +bool Item::TestUse(Level *level, shared_ptr player) +{ + return false; +} + +shared_ptr Item::use(shared_ptr itemInstance, Level *level, shared_ptr player) +{ + return itemInstance; +} + +shared_ptr Item::useTimeDepleted(shared_ptr itemInstance, Level *level, shared_ptr player) +{ + return itemInstance; +} + +int Item::getMaxStackSize() +{ + return maxStackSize; +} + +int Item::getLevelDataForAuxValue(int auxValue) +{ + return 0; +} + +bool Item::isStackedByData() +{ + return m_isStackedByData; +} + + +Item *Item::setStackedByData(bool isStackedByData) +{ + this->m_isStackedByData = isStackedByData; + return this; +} + + +int Item::getMaxDamage() +{ + return maxDamage; +} + + +Item *Item::setMaxDamage(int maxDamage) +{ + this->maxDamage = maxDamage; + return this; +} + + +bool Item::canBeDepleted() +{ + return maxDamage > 0 && !m_isStackedByData; +} + +/** +* Returns true when the item was used to deal more than default damage +* +* @param itemInstance +* @param mob +* @param attacker +* @return +*/ +bool Item::hurtEnemy(shared_ptr itemInstance, shared_ptr mob, shared_ptr attacker) +{ + return false; +} + +/** +* Returns true when the item was used to mine more efficiently +* +* @param itemInstance +* @param tile +* @param x +* @param y +* @param z +* @param owner +* @return +*/ +bool Item::mineBlock(shared_ptr itemInstance, Level *level, int tile, int x, int y, int z, shared_ptr owner) +{ + return false; +} + +int Item::getAttackDamage(shared_ptr entity) +{ + return 1; +} + +bool Item::canDestroySpecial(Tile *tile) +{ + return false; +} + +bool Item::interactEnemy(shared_ptr itemInstance, shared_ptr mob) +{ + return false; +} + +Item *Item::handEquipped() +{ + this->m_handEquipped = true; + return this; +} + +bool Item::isHandEquipped() +{ + return m_handEquipped; +} + +bool Item::isMirroredArt() +{ + return false; +} + +Item *Item::setDescriptionId(unsigned int id) +{ + this->descriptionId = id; + return this; +} + +LPCWSTR Item::getDescription() +{ + return app.GetString(getDescriptionId()); + //return I18n::get(getDescriptionId()); +} + +LPCWSTR Item::getDescription(shared_ptr instance) +{ + return app.GetString(getDescriptionId(instance)); + //return I18n::get(getDescriptionId(instance)); +} + +unsigned int Item::getDescriptionId(int iData /*= -1*/) +{ + return descriptionId; +} + +unsigned int Item::getDescriptionId(shared_ptr instance) +{ + return descriptionId; +} + +Item *Item::setUseDescriptionId(unsigned int id) +{ + this->useDescriptionId = id; + return this; +} + +unsigned int Item::getUseDescriptionId() +{ + return useDescriptionId; +} + +unsigned int Item::getUseDescriptionId(shared_ptr instance) +{ + return useDescriptionId; +} + +Item *Item::setCraftingRemainingItem(Item *craftingRemainingItem) +{ + this->craftingRemainingItem = craftingRemainingItem; + return this; +} + +bool Item::shouldMoveCraftingResultToInventory(shared_ptr instance) +{ + // Default is good for the vast majority of items + return true; +} + +bool Item::shouldOverrideMultiplayerNBT() +{ + return true; +} + +Item *Item::getCraftingRemainingItem() +{ + return craftingRemainingItem; +} + +bool Item::hasCraftingRemainingItem() +{ + return craftingRemainingItem != NULL; +} + +wstring Item::getName() +{ + return L"";//I18n::get(getDescriptionId() + L".name"); +} + +int Item::getColor(shared_ptr item, int spriteLayer) +{ + return 0xffffff; +} + +void Item::inventoryTick(shared_ptr itemInstance, Level *level, shared_ptr owner, int slot, bool selected) { +} + +void Item::onCraftedBy(shared_ptr itemInstance, Level *level, shared_ptr player) +{ +} + +bool Item::isComplex() +{ + return false; +} + +UseAnim Item::getUseAnimation(shared_ptr itemInstance) +{ + return UseAnim_none; +} + +int Item::getUseDuration(shared_ptr itemInstance) +{ + return 0; +} + +void Item::releaseUsing(shared_ptr itemInstance, Level *level, shared_ptr player, int durationLeft) +{ +} + +Item *Item::setPotionBrewingFormula(const wstring &potionBrewingFormula) +{ + this->potionBrewingFormula = potionBrewingFormula; + return this; +} + +wstring Item::getPotionBrewingFormula() +{ + return potionBrewingFormula; +} + +bool Item::hasPotionBrewingFormula() +{ + return !potionBrewingFormula.empty(); +} + +void Item::appendHoverText(shared_ptr itemInstance, shared_ptr player, vector *lines, bool advanced, vector &unformattedStrings) +{ +} + +wstring Item::getHoverName(shared_ptr itemInstance) +{ + //String elementName = ("" + Language.getInstance().getElementName(getDescription(itemInstance))).trim(); + //return elementName; + return app.GetString(getDescriptionId(itemInstance)); +} + +bool Item::isFoil(shared_ptr itemInstance) +{ + if (itemInstance->isEnchanted()) return true; + return false; +} + +const Rarity *Item::getRarity(shared_ptr itemInstance) +{ + if (itemInstance->isEnchanted()) return Rarity::rare; + return Rarity::common; +} + +bool Item::isEnchantable(shared_ptr itemInstance) +{ + return getMaxStackSize() == 1 && canBeDepleted(); +} + +HitResult *Item::getPlayerPOVHitResult(Level *level, shared_ptr player, bool alsoPickLiquid) +{ + float a = 1; + + float xRot = player->xRotO + (player->xRot - player->xRotO) * a; + float yRot = player->yRotO + (player->yRot - player->yRotO) * a; + + + double x = player->xo + (player->x - player->xo) * a; + double y = player->yo + (player->y - player->yo) * a + 1.62 - player->heightOffset; + double z = player->zo + (player->z - player->zo) * a; + + Vec3 *from = Vec3::newTemp(x, y, z); + + float yCos = (float) cos(-yRot * Mth::RAD_TO_GRAD - PI); + float ySin = (float) sin(-yRot * Mth::RAD_TO_GRAD - PI); + float xCos = (float) -cos(-xRot * Mth::RAD_TO_GRAD); + float xSin = (float) sin(-xRot * Mth::RAD_TO_GRAD); + + float xa = ySin * xCos; + float ya = xSin; + float za = yCos * xCos; + + double range = 5; + Vec3 *to = from->add(xa * range, ya * range, za * range); + return level->clip(from, to, alsoPickLiquid, !alsoPickLiquid); +} + +int Item::getEnchantmentValue() +{ + return 0; +} + +bool Item::hasMultipleSpriteLayers() +{ + return false; +} + +Icon *Item::getLayerIcon(int auxValue, int spriteLayer) +{ + return getIcon(auxValue); +} + +bool Item::isValidRepairItem(shared_ptr source, shared_ptr repairItem) +{ + return false; +} + +void Item::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(m_textureName); +} + + +/* + 4J: These are necesary on the PS3. + (and 4 and Vita). +*/ +#if (defined __PS3__ || defined __ORBIS__ || defined __PSVITA__) +const int Item::shovel_iron_Id ; +const int Item::pickAxe_iron_Id ; +const int Item::hatchet_iron_Id ; +const int Item::flintAndSteel_Id ; +const int Item::apple_Id ; +const int Item::bow_Id ; +const int Item::arrow_Id ; +const int Item::coal_Id ; +const int Item::diamond_Id ; +const int Item::ironIngot_Id ; +const int Item::goldIngot_Id ; +const int Item::sword_iron_Id ; +const int Item::sword_wood_Id ; +const int Item::shovel_wood_Id ; +const int Item::pickAxe_wood_Id ; +const int Item::hatchet_wood_Id ; +const int Item::sword_stone_Id ; +const int Item::shovel_stone_Id ; +const int Item::pickAxe_stone_Id ; +const int Item::hatchet_stone_Id ; +const int Item::sword_diamond_Id ; +const int Item::shovel_diamond_Id ; +const int Item::pickAxe_diamond_Id ; +const int Item::hatchet_diamond_Id ; +const int Item::stick_Id ; +const int Item::bowl_Id ; +const int Item::mushroomStew_Id ; +const int Item::sword_gold_Id ; +const int Item::shovel_gold_Id ; +const int Item::pickAxe_gold_Id ; +const int Item::hatchet_gold_Id ; +const int Item::string_Id ; +const int Item::feather_Id ; +const int Item::sulphur_Id ; +const int Item::hoe_wood_Id ; +const int Item::hoe_stone_Id ; +const int Item::hoe_iron_Id ; +const int Item::hoe_diamond_Id ; +const int Item::hoe_gold_Id ; +const int Item::seeds_wheat_Id ; +const int Item::wheat_Id ; +const int Item::bread_Id ; +const int Item::helmet_cloth_Id ; +const int Item::chestplate_cloth_Id ; +const int Item::leggings_cloth_Id ; +const int Item::boots_cloth_Id ; +const int Item::helmet_chain_Id ; +const int Item::chestplate_chain_Id ; +const int Item::leggings_chain_Id ; +const int Item::boots_chain_Id ; +const int Item::helmet_iron_Id ; +const int Item::chestplate_iron_Id ; +const int Item::leggings_iron_Id ; +const int Item::boots_iron_Id ; +const int Item::helmet_diamond_Id ; +const int Item::chestplate_diamond_Id; +const int Item::leggings_diamond_Id ; +const int Item::boots_diamond_Id ; +const int Item::helmet_gold_Id ; +const int Item::chestplate_gold_Id ; +const int Item::leggings_gold_Id ; +const int Item::boots_gold_Id ; +const int Item::flint_Id ; +const int Item::porkChop_raw_Id ; +const int Item::porkChop_cooked_Id ; +const int Item::painting_Id ; +const int Item::apple_gold_Id ; +const int Item::sign_Id ; +const int Item::door_wood_Id ; +const int Item::bucket_empty_Id ; +const int Item::bucket_water_Id ; +const int Item::bucket_lava_Id ; +const int Item::minecart_Id ; +const int Item::saddle_Id ; +const int Item::door_iron_Id ; +const int Item::redStone_Id ; +const int Item::snowBall_Id ; +const int Item::boat_Id ; +const int Item::leather_Id ; +const int Item::milk_Id ; +const int Item::brick_Id ; +const int Item::clay_Id ; +const int Item::reeds_Id ; +const int Item::paper_Id ; +const int Item::book_Id ; +const int Item::slimeBall_Id ; +const int Item::minecart_chest_Id ; +const int Item::minecart_furnace_Id ; +const int Item::egg_Id ; +const int Item::compass_Id ; +const int Item::fishingRod_Id ; +const int Item::clock_Id ; +const int Item::yellowDust_Id ; +const int Item::fish_raw_Id ; +const int Item::fish_cooked_Id ; +const int Item::dye_powder_Id ; +const int Item::bone_Id ; +const int Item::sugar_Id ; +const int Item::cake_Id ; +const int Item::bed_Id ; +const int Item::diode_Id ; +const int Item::cookie_Id ; +const int Item::map_Id ; +const int Item::shears_Id ; +const int Item::melon_Id ; +const int Item::seeds_pumpkin_Id ; +const int Item::seeds_melon_Id ; +const int Item::beef_raw_Id ; +const int Item::beef_cooked_Id ; +const int Item::chicken_raw_Id ; +const int Item::chicken_cooked_Id ; +const int Item::rotten_flesh_Id ; +const int Item::enderPearl_Id ; +const int Item::blazeRod_Id ; +const int Item::ghastTear_Id ; +const int Item::goldNugget_Id ; +const int Item::netherStalkSeeds_Id ; +const int Item::potion_Id ; +const int Item::glassBottle_Id ; +const int Item::spiderEye_Id ; +const int Item::fermentedSpiderEye_Id; +const int Item::blazePowder_Id ; +const int Item::magmaCream_Id ; +const int Item::brewingStand_Id ; +const int Item::cauldron_Id ; +const int Item::eyeOfEnder_Id ; +const int Item::speckledMelon_Id ; +const int Item::monsterPlacer_Id ; +const int Item::expBottle_Id ; +const int Item::skull_Id ; +const int Item::record_01_Id ; +const int Item::record_02_Id ; +const int Item::record_03_Id ; +const int Item::record_04_Id ; +const int Item::record_05_Id ; +const int Item::record_06_Id ; +const int Item::record_07_Id ; +const int Item::record_09_Id ; +const int Item::record_10_Id ; +const int Item::record_11_Id ; +const int Item::record_12_Id ; +const int Item::record_08_Id ; +const int Item::fireball_Id ; +const int Item::itemFrame_Id ; +const int Item::netherbrick_Id ; +const int Item::emerald_Id ; +const int Item::flowerPot_Id ; +const int Item::carrots_Id ; +const int Item::potato_Id ; +const int Item::potatoBaked_Id ; +const int Item::potatoPoisonous_Id ; +const int Item::carrotGolden_Id ; +const int Item::carrotOnAStick_Id ; +const int Item::pumpkinPie_Id ; +const int Item::enchantedBook_Id ; +const int Item::netherQuartz_Id ; +#endif diff --git a/Minecraft.World/Item.h b/Minecraft.World/Item.h new file mode 100644 index 00000000..727fc661 --- /dev/null +++ b/Minecraft.World/Item.h @@ -0,0 +1,718 @@ +#pragma once + +using namespace std; + +#include "Container.h" +#include "UseAnim.h" +#include "Rarity.h" + +class MapItem; +class Mob; +class Player; +class Random; +class Level; +class ShearsItem; +class PotionItem; +class HitResult; +class IconRegister; +class Icon; +class ArmorItem; +class BowItem; +class FishingRodItem; +class EnchantedBookItem; + +#define ITEM_ICON_COLUMNS 16 + + +class Item : public enable_shared_from_this +{ +public: + static const int ITEM_NUM_COUNT = 32000; + + static void staticCtor(); + static void staticInit(); + + // 4J-PB - added for new crafting menu + enum + { + eMaterial_undefined=0, + eMaterial_wood, + eMaterial_stone, + eMaterial_iron, + eMaterial_gold, + eMaterial_diamond, + eMaterial_cloth, + eMaterial_chain, // 4J Stu - It's available in creative in 1.8 + eMaterial_detector, + eMaterial_lapis, + eMaterial_music, + eMaterial_dye, + eMaterial_sand, + eMaterial_brick, + eMaterial_clay, + eMaterial_snow, + eMaterial_bow, + eMaterial_arrow, + eMaterial_compass, + eMaterial_clock, + eMaterial_map, + eMaterial_pumpkin, + eMaterial_glowstone, + eMaterial_water, + eMaterial_trap, + eMaterial_flintandsteel, + eMaterial_shears, + eMaterial_piston, + eMaterial_stickypiston, + eMaterial_gate, + eMaterial_stoneSmooth, + eMaterial_netherbrick, + eMaterial_ender, + eMaterial_glass, + eMaterial_blaze, + eMaterial_magic, + eMaterial_melon, + eMaterial_setfire, + eMaterial_sprucewood, + eMaterial_birchwood, + eMaterial_junglewood, + eMaterial_emerald, + eMaterial_quartz, + eMaterial_apple, + eMaterial_carrot + } + eMaterial; + + enum + { + eBaseItemType_undefined=0, + eBaseItemType_sword, + eBaseItemType_shovel, + eBaseItemType_pickaxe, + eBaseItemType_hatchet, + eBaseItemType_hoe, + eBaseItemType_door, + eBaseItemType_helmet, + eBaseItemType_chestplate, + eBaseItemType_leggings, + eBaseItemType_boots, + eBaseItemType_ingot, + eBaseItemType_rail, + eBaseItemType_block, + eBaseItemType_pressureplate, + eBaseItemType_stairs, + eBaseItemType_cloth, + eBaseItemType_dyepowder, + eBaseItemType_structwoodstuff, + eBaseItemType_structblock, + eBaseItemType_slab, + eBaseItemType_halfslab, + eBaseItemType_torch, + eBaseItemType_bow, + eBaseItemType_pockettool, + eBaseItemType_utensil, + eBaseItemType_piston, + eBaseItemType_devicetool, + eBaseItemType_fence, + eBaseItemType_device, + eBaseItemType_treasure, + eBaseItemType_seed, + eBaseItemType_HangingItem, + eBaseItemType_button, + eBaseItemType_chest, + eBaseItemType_rod, + eBaseItemType_giltFruit, + eBaseItemType_carpet, + eBaseItemType_MAXTYPES, + } + eBaseItemType; + +protected: + static const int ICON_COLUMNS = ITEM_ICON_COLUMNS; + static wstring ICON_DESCRIPTION_PREFIX; // 4J Stu - Was const but we have to static initialise it outside of this class + +public: + + class Tier + { + public: + static const Tier *WOOD; // + static const Tier *STONE; // + static const Tier *IRON; // + static const Tier *DIAMOND; // + static const Tier *GOLD; + + private: + const int level; + const int uses; + const float speed; + const int damage; + const int enchantmentValue; + + // 4J Stu - Had to make this public but was protected + // We shouldn't be creating these except the static initialisation + public: + Tier(int level, int uses, float speed, int damage, int enchantmentValue); + + public: + int getUses() const; + + float getSpeed() const; + + int getAttackDamageBonus() const; + + int getLevel() const; + + int getEnchantmentValue() const; + + int getTierItemId() const; + }; + +protected: + static Random *random; + +private: + static const int MAX_STACK_SIZE = Container::LARGE_MAX_STACK_SIZE; + +public: + static ItemArray items; + + static Item *shovel_iron; + static Item *pickAxe_iron; + static Item *hatchet_iron; + static Item *flintAndSteel; + static Item *apple; + static BowItem *bow; + static Item *arrow; + static Item *coal; + static Item *diamond; + static Item *ironIngot; + static Item *goldIngot; + static Item *sword_iron; + + static Item *sword_wood; + static Item *shovel_wood; + static Item *pickAxe_wood; + static Item *hatchet_wood; + + static Item *sword_stone; + static Item *shovel_stone; + static Item *pickAxe_stone; + static Item *hatchet_stone; + + static Item *sword_diamond; + static Item *shovel_diamond; + static Item *pickAxe_diamond; + static Item *hatchet_diamond; + + static Item *stick; + static Item *bowl; + static Item *mushroomStew; + + static Item *sword_gold; + static Item *shovel_gold; + static Item *pickAxe_gold; + static Item *hatchet_gold; + + static Item *string; + static Item *feather; + static Item *sulphur; + + static Item *hoe_wood; + static Item *hoe_stone; + static Item *hoe_iron; + static Item *hoe_diamond; + static Item *hoe_gold; + + static Item *seeds_wheat; + static Item *wheat; + static Item *bread; + + static ArmorItem *helmet_cloth; + static ArmorItem *chestplate_cloth; + static ArmorItem *leggings_cloth; + static ArmorItem *boots_cloth; + + static ArmorItem *helmet_chain; + static ArmorItem *chestplate_chain; + static ArmorItem *leggings_chain; + static ArmorItem *boots_chain; + + static ArmorItem *helmet_iron; + static ArmorItem *chestplate_iron; + static ArmorItem *leggings_iron; + static ArmorItem *boots_iron; + + static ArmorItem *helmet_diamond; + static ArmorItem *chestplate_diamond; + static ArmorItem *leggings_diamond; + static ArmorItem *boots_diamond; + + static ArmorItem *helmet_gold; + static ArmorItem *chestplate_gold; + static ArmorItem *leggings_gold; + static ArmorItem *boots_gold; + + static Item *flint; + static Item *porkChop_raw; + static Item *porkChop_cooked; + static Item *painting; + + static Item *apple_gold; + + static Item *sign; + static Item *door_wood; + + static Item *bucket_empty; + static Item *bucket_water; + static Item *bucket_lava; + + static Item *minecart; + static Item *saddle; + static Item *door_iron; + static Item *redStone; + static Item *snowBall; + + static Item *boat; + + static Item *leather; + static Item *milk; + static Item *brick; + static Item *clay; + static Item *reeds; + static Item *paper; + static Item *book; + static Item *slimeBall; + static Item *minecart_chest; + static Item *minecart_furnace; + static Item *egg; + static Item *compass; + static FishingRodItem *fishingRod; + static Item *clock; + static Item *yellowDust; + static Item *fish_raw; + static Item *fish_cooked; + + static Item *dye_powder; + static Item *bone; + static Item *sugar; + static Item *cake; + + static Item *bed; + + static Item *diode; + static Item *cookie; + + static MapItem *map; + + static ShearsItem *shears; + + static Item *melon; + + static Item *seeds_pumpkin; + static Item *seeds_melon; + + static Item *beef_raw; + static Item *beef_cooked; + static Item *chicken_raw; + static Item *chicken_cooked; + static Item *rotten_flesh; + + static Item *enderPearl; + + static Item *blazeRod; + static Item *ghastTear; + static Item *goldNugget; + + static Item *netherStalkSeeds; + + static PotionItem *potion; + static Item *glassBottle; + + static Item *spiderEye; + static Item *fermentedSpiderEye; + + static Item *blazePowder; + static Item *magmaCream; + + static Item *brewingStand; + static Item *cauldron; + static Item *eyeOfEnder; + static Item *speckledMelon; + + static Item *monsterPlacer; + + static Item *expBottle; + + static Item *skull; + + static Item *record_01; + static Item *record_02; + static Item *record_03; + static Item *record_04; + static Item *record_05; + static Item *record_06; + static Item *record_07; + static Item *record_08; + static Item *record_09; + static Item *record_10; + static Item *record_11; + static Item *record_12; + + // TU9 + static Item *fireball; + static Item *frame; + static Item *netherbrick; + + // TU14 + //static Item writingBook; + //static Item writtenBook; + + static Item *emerald; + + static Item *flowerPot; + + static Item *carrots; + static Item *potato; + static Item *potatoBaked; + static Item *potatoPoisonous; + + static Item *carrotGolden; + + static Item *carrotOnAStick; + static Item *pumpkinPie; + static Item *netherQuartz; + + static EnchantedBookItem *enchantedBook; + + + static const int shovel_iron_Id = 256; + static const int pickAxe_iron_Id = 257; + static const int hatchet_iron_Id = 258; + static const int flintAndSteel_Id = 259; + static const int apple_Id = 260; + static const int bow_Id = 261; + static const int arrow_Id = 262; + static const int coal_Id = 263; + static const int diamond_Id = 264; + static const int ironIngot_Id = 265; + static const int goldIngot_Id = 266; + static const int sword_iron_Id = 267; + static const int sword_wood_Id = 268; + static const int shovel_wood_Id = 269; + static const int pickAxe_wood_Id = 270; + static const int hatchet_wood_Id = 271; + static const int sword_stone_Id = 272; + static const int shovel_stone_Id = 273; + static const int pickAxe_stone_Id = 274; + static const int hatchet_stone_Id = 275; + static const int sword_diamond_Id = 276; + static const int shovel_diamond_Id = 277; + static const int pickAxe_diamond_Id = 278; + static const int hatchet_diamond_Id = 279; + static const int stick_Id = 280; + static const int bowl_Id = 281; + static const int mushroomStew_Id = 282; + static const int sword_gold_Id = 283; + static const int shovel_gold_Id = 284; + static const int pickAxe_gold_Id = 285; + static const int hatchet_gold_Id = 286; + static const int string_Id = 287; + static const int feather_Id = 288; + static const int sulphur_Id = 289; + static const int hoe_wood_Id = 290; + static const int hoe_stone_Id = 291; + static const int hoe_iron_Id = 292; + static const int hoe_diamond_Id = 293; + static const int hoe_gold_Id = 294; + static const int seeds_wheat_Id = 295; + static const int wheat_Id = 296; + static const int bread_Id = 297; + + static const int helmet_cloth_Id = 298; + static const int chestplate_cloth_Id = 299; + static const int leggings_cloth_Id = 300; + static const int boots_cloth_Id = 301; + + static const int helmet_chain_Id = 302; + static const int chestplate_chain_Id = 303; + static const int leggings_chain_Id = 304; + static const int boots_chain_Id = 305; + + static const int helmet_iron_Id = 306; + static const int chestplate_iron_Id = 307; + static const int leggings_iron_Id = 308; + static const int boots_iron_Id = 309; + + static const int helmet_diamond_Id = 310; + static const int chestplate_diamond_Id = 311; + static const int leggings_diamond_Id = 312; + static const int boots_diamond_Id = 313; + + static const int helmet_gold_Id = 314; + static const int chestplate_gold_Id = 315; + static const int leggings_gold_Id = 316; + static const int boots_gold_Id = 317; + + static const int flint_Id = 318; + static const int porkChop_raw_Id = 319; + static const int porkChop_cooked_Id = 320; + static const int painting_Id = 321; + static const int apple_gold_Id = 322; + static const int sign_Id = 323; + static const int door_wood_Id = 324; + static const int bucket_empty_Id = 325; + static const int bucket_water_Id = 326; + static const int bucket_lava_Id = 327; + static const int minecart_Id = 328; + static const int saddle_Id = 329; + static const int door_iron_Id = 330; + static const int redStone_Id = 331; + static const int snowBall_Id = 332; + static const int boat_Id = 333; + static const int leather_Id = 334; + static const int milk_Id = 335; + static const int brick_Id = 336; + static const int clay_Id = 337; + static const int reeds_Id = 338; + static const int paper_Id = 339; + static const int book_Id = 340; + static const int slimeBall_Id = 341; + static const int minecart_chest_Id = 342; + static const int minecart_furnace_Id = 343; + static const int egg_Id = 344; + static const int compass_Id = 345; + static const int fishingRod_Id = 346; + static const int clock_Id = 347; + static const int yellowDust_Id = 348; + static const int fish_raw_Id = 349; + static const int fish_cooked_Id = 350; + static const int dye_powder_Id = 351; + static const int bone_Id = 352; + static const int sugar_Id = 353; + static const int cake_Id = 354; + static const int bed_Id = 355; + static const int diode_Id = 356; + static const int cookie_Id = 357; + static const int map_Id = 358; + + // 1.7.3 + static const int shears_Id = 359; + + // 1.8.2 + static const int melon_Id = 360; + static const int seeds_pumpkin_Id = 361; + static const int seeds_melon_Id = 362; + static const int beef_raw_Id = 363; + static const int beef_cooked_Id = 364; + static const int chicken_raw_Id = 365; + static const int chicken_cooked_Id = 366; + static const int rotten_flesh_Id = 367; + static const int enderPearl_Id = 368; + + // 1.0.1 + static const int blazeRod_Id = 369; + static const int ghastTear_Id = 370; + static const int goldNugget_Id = 371; + static const int netherStalkSeeds_Id = 372; + static const int potion_Id = 373; + static const int glassBottle_Id = 374; + static const int spiderEye_Id = 375; + static const int fermentedSpiderEye_Id = 376; + static const int blazePowder_Id = 377; + static const int magmaCream_Id = 378; + static const int brewingStand_Id = 379; + static const int cauldron_Id = 380; + static const int eyeOfEnder_Id = 381; + static const int speckledMelon_Id = 382; + + // 1.1 + static const int monsterPlacer_Id = 383; + + static const int expBottle_Id = 384; + + // TU 12 + static const int skull_Id = 397; + + static const int record_01_Id = 2256; + static const int record_02_Id = 2257; + static const int record_03_Id = 2258; + static const int record_04_Id = 2259; + static const int record_05_Id = 2260; + static const int record_06_Id = 2261; + static const int record_07_Id = 2262; + static const int record_09_Id = 2263; + static const int record_10_Id = 2264; + static const int record_11_Id = 2265; + static const int record_12_Id = 2266; + + // 4J-PB - this one isn't playable in the PC game, but is fine in ours + static const int record_08_Id = 2267; + + // TU9 + static const int fireball_Id = 385; + static const int itemFrame_Id = 389; + static const int netherbrick_Id = 405; + + // TU14 + //static const int writingBook_Id = 130; + //static const int writtenBook_Id = 131; + + static const int emerald_Id = 388; + + static const int flowerPot_Id = 390; + + static const int carrots_Id = 391; + static const int potato_Id = 392; + static const int potatoBaked_Id = 393; + static const int potatoPoisonous_Id = 394; + + + static const int carrotGolden_Id = 396; + + static const int carrotOnAStick_Id = 398; + static const int pumpkinPie_Id = 400; + + static const int enchantedBook_Id = 403; + static const int netherQuartz_Id = 406; + +public: + const int id; + +protected: + int maxStackSize; + +private: + int maxDamage; + +protected: + Icon *icon; + // 4J-PB - added for new crafting menu + int m_iBaseItemType; + int m_iMaterial; + bool m_handEquipped; + bool m_isStackedByData; + +private: + Item *craftingRemainingItem; + wstring potionBrewingFormula; + + // 4J Stu - A value from strings.h, that is the name of the item + unsigned int descriptionId; + + // 4J Stu - A value from strings.h that says what this does + unsigned int useDescriptionId; + + wstring m_textureName; + +protected: + Item(int id); + +public: + // 4J Using per-item textures now + Item *setTextureName(const wstring &name); + Item *setMaxStackSize(int max); + Item *setBaseItemTypeAndMaterial(int iType,int iMaterial); + int getBaseItemType(); + int getMaterial(); + + virtual int getIconType(); + virtual Icon *getIcon(int auxValue); + Icon *getIcon(shared_ptr itemInstance); + + const bool useOn(shared_ptr itemInstance, Level *level, int x, int y, int z, int face, bool bTestUseOnOnly=false); + + virtual bool useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + virtual float getDestroySpeed(shared_ptr itemInstance, Tile *tile); + virtual bool TestUse(Level *level, shared_ptr player); + virtual shared_ptr use(shared_ptr itemInstance, Level *level, shared_ptr player); + virtual shared_ptr useTimeDepleted(shared_ptr itemInstance, Level *level, shared_ptr player); + virtual int getMaxStackSize(); + virtual int getLevelDataForAuxValue(int auxValue); + bool isStackedByData(); + +protected: + Item *setStackedByData(bool isStackedByData); + +public: + int getMaxDamage(); + +protected: + Item *setMaxDamage(int maxDamage); + +public: + bool canBeDepleted(); + + /** + * Returns true when the item was used to deal more than default damage + * + * @param itemInstance + * @param mob + * @param attacker + * @return + */ + virtual bool hurtEnemy(shared_ptr itemInstance, shared_ptr mob, shared_ptr attacker); + + /** + * Returns true when the item was used to mine more efficiently + * + * @param itemInstance + * @param tile + * @param x + * @param yf + * @param z + * @param owner + * @return + */ + virtual bool mineBlock(shared_ptr itemInstance, Level *level, int tile, int x, int y, int z, shared_ptr owner); + virtual int getAttackDamage(shared_ptr entity); + virtual bool canDestroySpecial(Tile *tile); + virtual bool interactEnemy(shared_ptr itemInstance, shared_ptr mob); + Item *handEquipped(); + virtual bool isHandEquipped(); + virtual bool isMirroredArt(); + Item *setDescriptionId(unsigned int id); + LPCWSTR getDescription(); + LPCWSTR getDescription(shared_ptr instance); + virtual unsigned int getDescriptionId(int iData = -1); + virtual unsigned int getDescriptionId(shared_ptr instance); + Item *setUseDescriptionId(unsigned int id); + virtual unsigned int getUseDescriptionId(); + virtual unsigned int getUseDescriptionId(shared_ptr instance); + Item *setCraftingRemainingItem(Item *craftingRemainingItem); + virtual bool shouldMoveCraftingResultToInventory(shared_ptr instance); + virtual bool shouldOverrideMultiplayerNBT(); + Item *getCraftingRemainingItem(); + bool hasCraftingRemainingItem(); + std::wstring getName(); + virtual int getColor(shared_ptr item, int spriteLayer); + virtual void inventoryTick(shared_ptr itemInstance, Level *level, shared_ptr owner, int slot, bool selected); + virtual void onCraftedBy(shared_ptr itemInstance, Level *level, shared_ptr player); + virtual bool isComplex(); + + virtual UseAnim getUseAnimation(shared_ptr itemInstance); + virtual int getUseDuration(shared_ptr itemInstance); + virtual void releaseUsing(shared_ptr itemInstance, Level *level, shared_ptr player, int durationLeft); + +protected: + virtual Item *setPotionBrewingFormula(const wstring &potionBrewingFormula); + +public: + virtual wstring getPotionBrewingFormula(); + virtual bool hasPotionBrewingFormula(); + virtual void appendHoverText(shared_ptr itemInstance, shared_ptr player, vector *lines, bool advanced, vector &unformattedStrings); // 4J Added unformattedStrings + virtual wstring getHoverName(shared_ptr itemInstance); + virtual bool isFoil(shared_ptr itemInstance); + virtual const Rarity *getRarity(shared_ptr itemInstance); + virtual bool isEnchantable(shared_ptr itemInstance); + +protected: + HitResult *getPlayerPOVHitResult(Level *level, shared_ptr player, bool alsoPickLiquid); + +public: + virtual int getEnchantmentValue(); + virtual bool hasMultipleSpriteLayers(); + virtual Icon *getLayerIcon(int auxValue, int spriteLayer); + virtual bool isValidRepairItem(shared_ptr source, shared_ptr repairItem); + virtual void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/ItemEntity.cpp b/Minecraft.World/ItemEntity.cpp new file mode 100644 index 00000000..eb7684a6 --- /dev/null +++ b/Minecraft.World/ItemEntity.cpp @@ -0,0 +1,306 @@ +#include "stdafx.h" +#include "JavaMath.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.damagesource.h" +#include "com.mojang.nbt.h" +#include "ItemEntity.h" +#include "SoundTypes.h" + + + +void ItemEntity::_init() +{ + age = 0; + throwTime = 0; + health = 5; + bobOffs = (float) (Math::random() * PI * 2); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + setSize(0.25f, 0.25f); + heightOffset = bbHeight / 2.0f; +} + +void ItemEntity::_init(Level *level, double x, double y, double z) +{ + _init(); + + setPos(x, y, z); + + yRot = (float) (Math::random() * 360); + + xd = (float) (Math::random() * 0.2f - 0.1f); + yd = +0.2f; + zd = (float) (Math::random() * 0.2f - 0.1f); +} + +ItemEntity::ItemEntity(Level *level, double x, double y, double z) : Entity(level) +{ + _init(level,x,y,z); +} + +ItemEntity::ItemEntity(Level *level, double x, double y, double z, shared_ptr item) : Entity( level ) +{ + _init(level,x,y,z); + setItem(item); +} + +bool ItemEntity::makeStepSound() +{ + return false; +} + +ItemEntity::ItemEntity(Level *level) : Entity( level ) +{ + _init(); +} + +void ItemEntity::defineSynchedData() +{ + getEntityData()->defineNULL(DATA_ITEM, NULL); +} + +void ItemEntity::tick() +{ + Entity::tick(); + + if (throwTime > 0) throwTime--; + xo = x; + yo = y; + zo = z; + + yd -= 0.04f; + noPhysics = checkInTile(x, (bb->y0 + bb->y1) / 2, z); + + // 4J - added parameter here so that these don't care about colliding with other entities + move(xd, yd, zd, true); + + bool moved = (int) xo != (int) x || (int) yo != (int) y || (int) zo != (int) z; + + if (moved || tickCount % 25 == 0) + { + if (level->getMaterial( Mth::floor(x), Mth::floor(y), Mth::floor(z)) == Material::lava) + { + yd = 0.2f; + xd = (random->nextFloat() - random->nextFloat()) * 0.2f; + zd = (random->nextFloat() - random->nextFloat()) * 0.2f; + MemSect(31); + level->playSound(shared_from_this(), eSoundType_RANDOM_FIZZ, 0.4f, 2.0f + random->nextFloat() * 0.4f); + MemSect(0); + } + + if (!level->isClientSide) + { + mergeWithNeighbours(); + } + } + + float friction = 0.98f; + if (onGround) + { + friction = 0.6f * 0.98f; + int t = level->getTile( Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z) ); + if (t > 0) + { + friction = Tile::tiles[t]->friction * 0.98f; + } + } + + xd *= friction; + yd *= 0.98f; + zd *= friction; + + if (onGround) + { + yd *= -0.5f; + } + + tickCount++; + age++; + if (!level->isClientSide && age >= LIFETIME) + { + remove(); + } +} + +void ItemEntity::mergeWithNeighbours() +{ + vector > *neighbours = level->getEntitiesOfClass(typeid(*this), bb->grow(0.5, 0, 0.5)); + for(AUTO_VAR(it, neighbours->begin()); it != neighbours->end(); ++it) + { + shared_ptr entity = dynamic_pointer_cast(*it); + merge(entity); + } + delete neighbours; +} + +bool ItemEntity::merge(shared_ptr target) +{ + if (target == shared_from_this()) return false; + if (!target->isAlive() || !this->isAlive()) return false; + shared_ptr myItem = this->getItem(); + shared_ptr targetItem = target->getItem(); + + if (targetItem->getItem() != myItem->getItem()) return false; + if (targetItem->hasTag() ^ myItem->hasTag()) return false; + if (targetItem->hasTag() && !targetItem->getTag()->equals(myItem->getTag())) return false; + if (targetItem->getItem()->isStackedByData() && targetItem->getAuxValue() != myItem->getAuxValue()) return false; + if (targetItem->count < myItem->count) return target->merge(dynamic_pointer_cast(shared_from_this())); + if (targetItem->count + myItem->count > targetItem->getMaxStackSize()) return false; + + targetItem->count += myItem->count; + target->throwTime = max(target->throwTime, this->throwTime); + target->age = min(target->age, this->age); + target->setItem(targetItem); + remove(); + + return true; +} + +void ItemEntity::setShortLifeTime() +{ + // reduce lifetime to one minute + age = LIFETIME - (60 * SharedConstants::TICKS_PER_SECOND); +} + +bool ItemEntity::updateInWaterState() +{ + return level->checkAndHandleWater(bb, Material::water, shared_from_this()); +} + + +void ItemEntity::burn(int dmg) +{ + hurt(DamageSource::inFire, dmg); +} + + +bool ItemEntity::hurt(DamageSource *source, int damage) +{ + // 4J - added next line: found whilst debugging an issue with item entities getting into a bad state when being created by a cactus, since entities insides cactuses get hurt + // and therefore depending on the timing of things they could get removed from the client when they weren't supposed to be. Are there really any cases were we would want + // an itemEntity to be locally hurt? + if (level->isClientSide ) return false; + + markHurt(); + health -= damage; + if (health <= 0) + { + remove(); + } + return false; +} + +void ItemEntity::addAdditonalSaveData(CompoundTag *entityTag) +{ + entityTag->putShort(L"Health", (byte) health); + entityTag->putShort(L"Age", (short) age); + if (getItem() != NULL) entityTag->putCompound(L"Item", getItem()->save(new CompoundTag())); +} + +void ItemEntity::readAdditionalSaveData(CompoundTag *tag) +{ + health = tag->getShort(L"Health") & 0xff; + age = tag->getShort(L"Age"); + CompoundTag *itemTag = tag->getCompound(L"Item"); + setItem(ItemInstance::fromTag(itemTag)); + if (getItem() == NULL) remove(); +} + +void ItemEntity::playerTouch(shared_ptr player) +{ + if (level->isClientSide) return; + + shared_ptr item = getItem(); + + // 4J Stu - Fix for duplication glitch + if(item->count <= 0) + { + remove(); + return; + } + + int orgCount = item->count; + if (throwTime == 0 && player->inventory->add(item)) + { + //if (item.id == Tile.treeTrunk.id) player.awardStat(Achievements.mineWood); + //if (item.id == Item.leather.id) player.awardStat(Achievements.killCow); + //if (item.id == Item.diamond.id) player.awardStat(Achievements.diamonds); + //if (item.id == Item.blazeRod.id) player.awardStat(Achievements.blazeRod); + if (item->id == Item::diamond_Id) + { + player->awardStat(GenericStats::diamonds(), GenericStats::param_diamonds()); + +#ifdef _EXTENDED_ACHIEVEMENTS + if ( getItem()->getItem()->id ) + { + shared_ptr pThrower = level->getPlayerByName(getThrower()); + if ( (pThrower != nullptr) && (pThrower != player) ) + { + pThrower->awardStat(GenericStats::diamondsToYou(), GenericStats::param_diamondsToYou()); + } + } +#endif + } + if (item->id == Item::blazeRod_Id) + player->awardStat(GenericStats::blazeRod(), GenericStats::param_blazeRod()); + + level->playSound(shared_from_this(), eSoundType_RANDOM_POP, 0.2f, ((random->nextFloat() - random->nextFloat()) * 0.7f + 1.0f) * 2.0f); + player->take(shared_from_this(), orgCount); + // System.out.println(item.count + ", " + orgCount); + if (item->count <= 0) remove(); + } +} + +wstring ItemEntity::getAName() +{ + return L"";//L"item." + getItem()->getDescriptionId(); + //return I18n.get("item." + item.getDescriptionId()); +} + +shared_ptr ItemEntity::getItem() +{ + shared_ptr result = getEntityData()->getItemInstance(DATA_ITEM); + + if (result == NULL) + { + if (level != NULL) + { + app.DebugPrintf("Item entity %d has no item?!\n", entityId); + //level.getLogger().severe("Item entity " + entityId + " has no item?!"); + } + return shared_ptr(new ItemInstance(Tile::rock)); + } + + return result; +} + +void ItemEntity::setItem(shared_ptr item) +{ + getEntityData()->set(DATA_ITEM, item); + getEntityData()->markDirty(DATA_ITEM); +} + +bool ItemEntity::isAttackable() +{ + return false; +} + +void ItemEntity::setThrower(const wstring &thrower) +{ + this->thrower = thrower; +} + +wstring ItemEntity::getThrower() +{ + return this->thrower; +} diff --git a/Minecraft.World/ItemEntity.h b/Minecraft.World/ItemEntity.h new file mode 100644 index 00000000..1f42bc21 --- /dev/null +++ b/Minecraft.World/ItemEntity.h @@ -0,0 +1,74 @@ +#pragma once +#include "Entity.h" +#include "SharedConstants.h" + +class Player; + +class ItemEntity : public Entity +{ +public: + eINSTANCEOF GetType() { return eTYPE_ITEMENTITY;} + static Entity *create(Level *level) { return new ItemEntity(level); } + +private: + static const int DATA_ITEM = 10; + + static const int LIFETIME = 5 * 60 * SharedConstants::TICKS_PER_SECOND; // Five miniutes. + + wstring thrower; + + // 4J Added + void _init(); + void _init(Level *level, double x, double y, double z); + +public: + int age; + int throwTime; + +private: + int health; + +public: + float bobOffs; + + ItemEntity(Level *level, double x, double y, double z); + ItemEntity(Level *level, double x, double y, double z, shared_ptr item); + +protected: + virtual bool makeStepSound(); + +public: + ItemEntity(Level *level); + +protected: + virtual void defineSynchedData(); + +public: + virtual void tick(); + +private: + void mergeWithNeighbours(); + +public: + bool merge(shared_ptr target); + void setShortLifeTime(); + virtual bool updateInWaterState(); + +protected: + virtual void burn(int dmg); + +public: + virtual bool hurt(DamageSource *source, int damage); + virtual void addAdditonalSaveData(CompoundTag *entityTag); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual void playerTouch(shared_ptr player); + + virtual wstring getAName(); + + shared_ptr getItem(); + void setItem(shared_ptr item); + virtual bool isAttackable(); + + void setThrower(const wstring &thrower); + wstring getThrower(); +}; diff --git a/Minecraft.World/ItemFrame.cpp b/Minecraft.World/ItemFrame.cpp new file mode 100644 index 00000000..141f0630 --- /dev/null +++ b/Minecraft.World/ItemFrame.cpp @@ -0,0 +1,145 @@ +#include "stdafx.h" + +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.saveddata.h" +#include "com.mojang.nbt.h" +#include "ItemFrame.h" + + + + +// 4J - added for common ctor code +void ItemFrame::_init() +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); +} + +ItemFrame::ItemFrame(Level *level) : HangingEntity( level ) +{ + _init(); +} + +ItemFrame::ItemFrame(Level *level, int xTile, int yTile, int zTile, int dir) : HangingEntity( level, xTile, yTile, zTile, dir ) +{ + _init(); + setDir(dir); +} + +void ItemFrame::defineSynchedData() +{ + getEntityData()->defineNULL(DATA_ITEM, NULL); + getEntityData()->define(DATA_ROTATION, (byte) 0); +} + +void ItemFrame::dropItem() +{ + spawnAtLocation(shared_ptr(new ItemInstance(Item::frame)), 0.0f); + shared_ptr item = getItem(); + if (item != NULL) + { + shared_ptr data = Item::map->getSavedData(item, level); + data->removeItemFrameDecoration(item); + + shared_ptr itemToDrop = item->copy(); + itemToDrop->setFramed(nullptr); + spawnAtLocation(itemToDrop, 0.0f); + } +} + +shared_ptr ItemFrame::getItem() +{ + return getEntityData()->getItemInstance(DATA_ITEM); +} + +void ItemFrame::setItem(shared_ptr item) +{ + if(item != NULL) + { + item = item->copy(); + item->count = 1; + + item->setFramed(dynamic_pointer_cast( shared_from_this() )); + } + getEntityData()->set(DATA_ITEM, item); + getEntityData()->markDirty(DATA_ITEM); +} + +int ItemFrame::getRotation() +{ + return getEntityData()->getByte(DATA_ROTATION); +} + +void ItemFrame::setRotation(int rotation) +{ + getEntityData()->set(DATA_ROTATION, (byte) (rotation % 4)); +} + +void ItemFrame::addAdditonalSaveData(CompoundTag *tag) +{ + if (getItem() != NULL) + { + tag->putCompound(L"Item", getItem()->save(new CompoundTag())); + tag->putByte(L"ItemRotation", (byte) getRotation()); + //tag->putFloat(L"ItemDropChance", dropChance); + } + HangingEntity::addAdditonalSaveData(tag); +} + +void ItemFrame::readAdditionalSaveData(CompoundTag *tag) +{ + CompoundTag *itemTag = tag->getCompound(L"Item"); + if (itemTag != NULL && !itemTag->isEmpty()) + { + setItem(ItemInstance::fromTag(itemTag)); + setRotation(tag->getByte(L"ItemRotation")); + + //if (tag->contains(L"ItemDropChance")) dropChance = tag->getFloat(L"ItemDropChance"); + } + HangingEntity::readAdditionalSaveData(tag); +} + +bool ItemFrame::interact(shared_ptr player) +{ + if(!player->isAllowedToInteract(shared_from_this())) + { + return false; + } + + if (getItem() == NULL) + { + shared_ptr item = player->getCarriedItem(); + + if (item != NULL) + { + if (!level->isClientSide)//isClientSide) + { + setItem(item); + + if (!player->abilities.instabuild) + { + if (--item->count <= 0) + { + player->inventory->setItem(player->inventory->selected, nullptr); + } + } + } + } + } + else + { + if (!level->isClientSide)//isClientSide) + { + setRotation(getRotation() + 1); + } + } + + return true; +} diff --git a/Minecraft.World/ItemFrame.h b/Minecraft.World/ItemFrame.h new file mode 100644 index 00000000..1af5c2e9 --- /dev/null +++ b/Minecraft.World/ItemFrame.h @@ -0,0 +1,41 @@ +#pragma once +using namespace std; +#include "Entity.h" +#include "HangingEntity.h" + +class Level; + +class ItemFrame : public HangingEntity +{ +public: + eINSTANCEOF GetType() { return eTYPE_ITEM_FRAME; }; + static Entity *create(Level *level) { return new ItemFrame(level); } +private: + static const int DATA_ITEM = 2; + static const int DATA_ROTATION = 3; + +private: + + void _init(); + +public: + ItemFrame(Level *level); + ItemFrame(Level *level, int xTile, int yTile, int zTile, int dir); + +protected: + virtual void defineSynchedData(); + virtual int getWidth() {return 9;} + virtual int getHeight() {return 9;} + virtual void dropItem(); + +public: + + shared_ptr getItem(); + void setItem(shared_ptr item); + int getRotation(); + void setRotation(int rotation); + + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual bool interact(shared_ptr player); +}; \ No newline at end of file diff --git a/Minecraft.World/ItemInstance.cpp b/Minecraft.World/ItemInstance.cpp new file mode 100644 index 00000000..ab042af0 --- /dev/null +++ b/Minecraft.World/ItemInstance.cpp @@ -0,0 +1,727 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.locale.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "Item.h" +#include "ItemInstance.h" + + + +const wchar_t *ItemInstance::TAG_ENCH_ID = L"id"; +const wchar_t *ItemInstance::TAG_ENCH_LEVEL = L"lvl"; + +void ItemInstance::_init(int id, int count, int auxValue) +{ + this->popTime = 0; + this->id = id; + this->count = count; + this->auxValue = auxValue; + this->tag = NULL; + this->frame = nullptr; + // 4J-PB - for trading menu + this->m_bForceNumberDisplay=false; +} + +ItemInstance::ItemInstance(Tile *tile) +{ + _init(tile->id, 1, 0); +} + +ItemInstance::ItemInstance(Tile *tile, int count) +{ + _init(tile->id, count, 0); +} +// 4J-PB - added +ItemInstance::ItemInstance(MapItem *item, int count) +{ + _init(item->id, count, 0); +} + +ItemInstance::ItemInstance(Tile *tile, int count, int auxValue) +{ + _init(tile->id, count, auxValue); +} + +ItemInstance::ItemInstance(Item *item) +{ + _init(item->id, 1, 0); +} + + + +ItemInstance::ItemInstance(Item *item, int count) +{ + _init(item->id, count, 0); +} + +ItemInstance::ItemInstance(Item *item, int count, int auxValue) +{ + _init(item->id, count, auxValue); +} + +ItemInstance::ItemInstance(int id, int count, int damage) +{ + _init(id,count,damage); +} + +shared_ptr ItemInstance::fromTag(CompoundTag *itemTag) +{ + shared_ptr itemInstance = shared_ptr(new ItemInstance()); + itemInstance->load(itemTag); + return itemInstance->getItem() != NULL ? itemInstance : nullptr; +} + +ItemInstance::~ItemInstance() +{ + if(tag != NULL) delete tag; +} + +shared_ptr ItemInstance::remove(int count) +{ + shared_ptr ii = shared_ptr( new ItemInstance(id, count, auxValue) ); + if (tag != NULL) ii->tag = (CompoundTag *) tag->copy(); + this->count -= count; + + // 4J Stu Fix for duplication glitch, make sure that item count is in range + if(this->count <= 0) + { + this->count = 0; + } + return ii; +} + +Item *ItemInstance::getItem() const +{ + return Item::items[id]; +} + +Icon *ItemInstance::getIcon() +{ + return getItem()->getIcon(shared_from_this()); +} + +int ItemInstance::getIconType() +{ + return getItem()->getIconType(); +} + +bool ItemInstance::useOn(shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + return getItem()->useOn(shared_from_this(), player, level, x, y, z, face, clickX, clickY, clickZ, bTestUseOnOnly); +} + +float ItemInstance::getDestroySpeed(Tile *tile) +{ + return getItem()->getDestroySpeed(shared_from_this(), tile); +} + +bool ItemInstance::TestUse(Level *level, shared_ptr player) +{ + return getItem()->TestUse( level, player); +} + +shared_ptr ItemInstance::use(Level *level, shared_ptr player) +{ + return getItem()->use(shared_from_this(), level, player); +} + +shared_ptr ItemInstance::useTimeDepleted(Level *level, shared_ptr player) +{ + return getItem()->useTimeDepleted(shared_from_this(), level, player); +} + +CompoundTag *ItemInstance::save(CompoundTag *compoundTag) +{ + compoundTag->putShort(L"id", (short) id); + compoundTag->putByte(L"Count", (byte) count); + compoundTag->putShort(L"Damage", (short) auxValue); + if (this->tag != NULL) compoundTag->put(L"tag", tag->copy()); + return compoundTag; +} + +void ItemInstance::load(CompoundTag *compoundTag) +{ + popTime = 0; + id = compoundTag->getShort(L"id"); + count = compoundTag->getByte(L"Count"); + auxValue = compoundTag->getShort(L"Damage"); + if (compoundTag->contains(L"tag")) + { + tag = (CompoundTag *)compoundTag->getCompound(L"tag")->copy(); + } +} + +int ItemInstance::getMaxStackSize() +{ + return getItem()->getMaxStackSize(); +} + +bool ItemInstance::isStackable() +{ + return getMaxStackSize() > 1 && (!isDamageableItem() || !isDamaged()); +} + +bool ItemInstance::isDamageableItem() +{ + return Item::items[id]->getMaxDamage() > 0; +} + +/** + * Returns true if this item type only can be stacked with items that have + * the same auxValue data. + * + * @return + */ + +bool ItemInstance::isStackedByData() +{ + return Item::items[id]->isStackedByData(); +} + +bool ItemInstance::isDamaged() +{ + return isDamageableItem() && auxValue > 0; +} + +int ItemInstance::getDamageValue() +{ + return auxValue; +} + +int ItemInstance::getAuxValue() const +{ + return auxValue; +} + +void ItemInstance::setAuxValue(int value) +{ + auxValue = value; +} + +int ItemInstance::getMaxDamage() +{ + return Item::items[id]->getMaxDamage(); +} + +void ItemInstance::hurt(int i, shared_ptr owner) +{ + if (!isDamageableItem()) + { + return; + } + + shared_ptr player = dynamic_pointer_cast(owner); + if (i > 0 && player != NULL) + { + int enchanted = EnchantmentHelper::getDigDurability(player->inventory); + // Fix for #65233 - TU8: Content: Gameplay: Tools Enchanted with "Unbreaking" occasionally repair themselves. + // 4J Stu - If it's the clientside level, then always assume that no damage is done. This stops the case where the client random + // results in damage, but the server random does not + if (enchanted > 0 && (owner->level->isClientSide || owner->level->random->nextInt(enchanted + 1) > 0) ) + { + // enchantment prevents damage + return; + } + } + + // 4J Stu - Changed in TU6 to not damage items in creative mode + if (!(owner != NULL && player->abilities.instabuild)) auxValue += i; + + if (auxValue > getMaxDamage()) + { + owner->breakItem(shared_from_this()); + count--; + if (count < 0) count = 0; + auxValue = 0; + } +} + +void ItemInstance::hurtEnemy(shared_ptr mob, shared_ptr attacker) +{ + //bool used = + Item::items[id]->hurtEnemy(shared_from_this(), mob, attacker); +} + +void ItemInstance::mineBlock(Level *level, int tile, int x, int y, int z, shared_ptr owner) +{ + //bool used = + Item::items[id]->mineBlock( shared_from_this(), level, tile, x, y, z, owner); +} + +int ItemInstance::getAttackDamage(shared_ptr entity) +{ + return Item::items[id]->getAttackDamage(entity); +} + +bool ItemInstance::canDestroySpecial(Tile *tile) +{ + return Item::items[id]->canDestroySpecial(tile); +} + +bool ItemInstance::interactEnemy(shared_ptr mob) +{ + return Item::items[id]->interactEnemy(shared_from_this(), mob); +} + +shared_ptr ItemInstance::copy() const +{ + shared_ptr copy = shared_ptr( new ItemInstance(id, count, auxValue) ); + if (tag != NULL) + { + copy->tag = (CompoundTag *) tag->copy(); + } + return copy; +} + +// 4J Stu - Added this as we need it in the recipe code +ItemInstance *ItemInstance::copy_not_shared() const +{ + ItemInstance *copy = new ItemInstance(id, count, auxValue); + if (tag != NULL) + { + copy->tag = (CompoundTag *) tag->copy(); + if (!copy->tag->equals(tag)) + { + return copy; + } + } + return copy; +} + +// 4J Brought forward from 1.2 +bool ItemInstance::tagMatches(shared_ptr a, shared_ptr b) +{ + if (a == NULL && b == NULL) return true; + if (a == NULL || b == NULL) return false; + + if (a->tag == NULL && b->tag != NULL) + { + return false; + } + if (a->tag != NULL && !a->tag->equals(b->tag)) + { + return false; + } + return true; +} + +bool ItemInstance::matches(shared_ptr a, shared_ptr b) +{ + if (a == NULL && b == NULL) return true; + if (a == NULL || b == NULL) return false; + return a->matches(b); +} + +bool ItemInstance::matches(shared_ptr b) +{ + if (count != b->count) return false; + if (id != b->id) return false; + if (auxValue != b->auxValue) return false; + if (tag == NULL && b->tag != NULL) + { + return false; + } + if (tag != NULL && !tag->equals(b->tag)) + { + return false; + } + return true; +} + +/** + * Checks if this item is the same item as the other one, disregarding the + * 'count' value. + * + * @param b + * @return + */ +bool ItemInstance::sameItem(shared_ptr b) +{ + return id == b->id && auxValue == b->auxValue; +} + +bool ItemInstance::sameItemWithTags(shared_ptr b) +{ + if (id != b->id) return false; + if (auxValue != b->auxValue) return false; + if (tag == NULL && b->tag != NULL) + { + return false; + } + if (tag != NULL && !tag->equals(b->tag)) + { + return false; + } + return true; +} + +// 4J Stu - Added this for the one time when we compare with a non-shared pointer +bool ItemInstance::sameItem_not_shared(ItemInstance *b) +{ + return id == b->id && auxValue == b->auxValue; +} + +unsigned int ItemInstance::getUseDescriptionId() +{ + return Item::items[id]->getUseDescriptionId(shared_from_this()); +} + +unsigned int ItemInstance::getDescriptionId(int iData /*= -1*/) +{ + return Item::items[id]->getDescriptionId(shared_from_this()); +} + +ItemInstance *ItemInstance::setDescriptionId(unsigned int id) +{ + // 4J Stu - I don't think this function is ever used. It if is, it should probably return shared_from_this() + assert(false); + return this; +} + +shared_ptr ItemInstance::clone(shared_ptr item) +{ + return item == NULL ? nullptr : item->copy(); +} + +wstring ItemInstance::toString() +{ + //return count + "x" + Item::items[id]->getDescriptionId() + "@" + auxValue; + + std::wostringstream oss; + // 4J-PB - TODO - temp fix until ore recipe issue is fixed + if(Item::items[id]==NULL) + { + oss << std::dec << count << L"x" << L" Item::items[id] is NULL " << L"@" << auxValue; + } + else + { + oss << std::dec << count << L"x" << Item::items[id]->getDescription(shared_from_this()) << L"@" << auxValue; + } + return oss.str(); +} + +void ItemInstance::inventoryTick(Level *level, shared_ptr owner, int slot, bool selected) +{ + if (popTime > 0) popTime--; + Item::items[id]->inventoryTick(shared_from_this(), level, owner, slot, selected); +} + +void ItemInstance::onCraftedBy(Level *level, shared_ptr player, int craftCount) +{ + // 4J Stu Added for tutorial callback + player->onCrafted(shared_from_this()); + + player->awardStat( + GenericStats::itemsCrafted(id), + GenericStats::param_itemsCrafted(id, auxValue, craftCount) + ); + + Item::items[id]->onCraftedBy(shared_from_this(), level, player); +} + +bool ItemInstance::equals(shared_ptr ii) +{ + return id == ii->id && count == ii->count && auxValue == ii->auxValue; +} + +int ItemInstance::getUseDuration() +{ + return getItem()->getUseDuration(shared_from_this()); +} + +UseAnim ItemInstance::getUseAnimation() +{ + return getItem()->getUseAnimation(shared_from_this()); +} + +void ItemInstance::releaseUsing(Level *level, shared_ptr player, int durationLeft) +{ + getItem()->releaseUsing(shared_from_this(), level, player, durationLeft); +} + +// 4J Stu - Brought forward these functions for enchanting/game rules +bool ItemInstance::hasTag() +{ + return tag != NULL; +} + +CompoundTag *ItemInstance::getTag() +{ + return tag; +} + +ListTag *ItemInstance::getEnchantmentTags() +{ + if (tag == NULL) + { + return NULL; + } + return (ListTag *) tag->get(L"ench"); +} + +void ItemInstance::setTag(CompoundTag *tag) +{ + this->tag = tag; +} + +wstring ItemInstance::getHoverName() +{ + wstring title = getItem()->getHoverName(shared_from_this()); + + if (tag != NULL && tag->contains(L"display")) + { + CompoundTag *display = tag->getCompound(L"display"); + + if (display->contains(L"Name")) + { + title = display->getString(L"Name"); + } + } + + return title; +} + +void ItemInstance::setHoverName(const wstring &name) +{ + if (tag == NULL) tag = new CompoundTag(); + if (!tag->contains(L"display")) tag->putCompound(L"display", new CompoundTag()); + tag->getCompound(L"display")->putString(L"Name", name); +} + +bool ItemInstance::hasCustomHoverName() +{ + if (tag == NULL) return false; + if (!tag->contains(L"display")) return false; + return tag->getCompound(L"display")->contains(L"Name"); +} + +vector *ItemInstance::getHoverText(shared_ptr player, bool advanced, vector &unformattedStrings) +{ + vector *lines = new vector(); + Item *item = Item::items[id]; + wstring title = getHoverName(); + + // 4J Stu - We don't do italics, but do change colour. But handle this later in the process due to text length measuring on the Xbox360 + //if (hasCustomHoverName()) + //{ + // title = L"" + title + L""; + //} + + // 4J Stu - Don't currently have this + //if (advanced) + //{ + // String suffix = ""; + + // if (title.length() > 0) { + // title += " ("; + // suffix = ")"; + // } + + // if (isStackedByData()) + // { + // title += String.format("#%04d/%d%s", id, auxValue, suffix); + // } + // else + // { + // title += String.format("#%04d%s", id, suffix); + // } + //} + //else + // if (!hasCustomHoverName()) + //{ + // if (id == Item::map_Id) + // { + // title += L" #" + _toString(auxValue); + // } + //} + + lines->push_back(title); + unformattedStrings.push_back(title); + item->appendHoverText(shared_from_this(), player, lines, advanced, unformattedStrings); + + if (hasTag()) + { + ListTag *list = getEnchantmentTags(); + if (list != NULL) + { + for (int i = 0; i < list->size(); i++) + { + int type = list->get(i)->getShort((wchar_t *)TAG_ENCH_ID); + int level = list->get(i)->getShort((wchar_t *)TAG_ENCH_LEVEL); + + if (Enchantment::enchantments[type] != NULL) + { + wstring unformatted = L""; + lines->push_back(Enchantment::enchantments[type]->getFullname(level, unformatted)); + unformattedStrings.push_back(unformatted); + } + } + } + } + return lines; +} + +// 4J Added +vector *ItemInstance::getHoverTextOnly(shared_ptr player, bool advanced, vector &unformattedStrings) +{ + vector *lines = new vector(); + Item *item = Item::items[id]; + + item->appendHoverText(shared_from_this(), player, lines, advanced, unformattedStrings); + + if (hasTag()) + { + ListTag *list = getEnchantmentTags(); + if (list != NULL) + { + for (int i = 0; i < list->size(); i++) + { + int type = list->get(i)->getShort((wchar_t *)TAG_ENCH_ID); + int level = list->get(i)->getShort((wchar_t *)TAG_ENCH_LEVEL); + + if (Enchantment::enchantments[type] != NULL) + { + wstring unformatted = L""; + lines->push_back(Enchantment::enchantments[type]->getFullname(level,unformatted)); + unformattedStrings.push_back(unformatted); + } + } + } + } + return lines; +} + +bool ItemInstance::isFoil() +{ + return getItem()->isFoil(shared_from_this()); +} + +const Rarity *ItemInstance::getRarity() +{ + return getItem()->getRarity(shared_from_this()); +} + +bool ItemInstance::isEnchantable() +{ + if (!getItem()->isEnchantable(shared_from_this())) return false; + if (isEnchanted()) return false; + return true; +} + +void ItemInstance::enchant(const Enchantment *enchantment, int level) +{ + if (tag == NULL) this->setTag(new CompoundTag()); + if (!tag->contains(L"ench")) tag->put(L"ench", new ListTag(L"ench")); + + ListTag *list = (ListTag *) tag->get(L"ench"); + CompoundTag *ench = new CompoundTag(); + ench->putShort((wchar_t *)TAG_ENCH_ID, (short) enchantment->id); + ench->putShort((wchar_t *)TAG_ENCH_LEVEL, (byte) level); + list->add(ench); +} + +bool ItemInstance::isEnchanted() +{ + if (tag != NULL && tag->contains(L"ench")) return true; + return false; +} + +void ItemInstance::addTagElement(wstring name, Tag *tag) +{ + if (this->tag == NULL) + { + this->setTag(new CompoundTag()); + } + this->tag->put((wchar_t *)name.c_str(), tag); +} + +void ItemInstance::set4JData(int data) +{ + if(tag == NULL && data == 0) return; + if (tag == NULL) this->setTag(new CompoundTag()); + + if (tag->contains(L"4jdata")) + { + IntTag *dataTag = (IntTag *)tag->get(L"4jdata"); + dataTag->data = data; + } + else if(data != 0) + { + tag->put(L"4jdata", new IntTag(L"4jdata",data)); + } +} + +int ItemInstance::get4JData() +{ + if(tag == NULL || !tag->contains(L"4jdata")) return 0; + else + { + IntTag *dataTag = (IntTag *)tag->get(L"4jdata"); + return dataTag->data; + } +} +// 4J Added - to show strength on potions +bool ItemInstance::hasPotionStrengthBar() +{ + // exclude a bottle of water from this + if((id==Item::potion_Id) && (auxValue !=0))// && (!MACRO_POTION_IS_AKWARD(auxValue))) 4J-PB leaving the bar on an awkward potion so we can differentiate it from a water bottle + { + return true; + } + + return false; +} + +int ItemInstance::GetPotionStrength() +{ + if(MACRO_POTION_IS_INSTANTDAMAGE(auxValue) || MACRO_POTION_IS_INSTANTHEALTH(auxValue) ) + { + // The two instant potions don't have extended versions + return (auxValue&MASK_LEVEL2)>>5; + } + else + { + return (auxValue&MASK_LEVEL2EXTENDED)>>5; + } +} + +// TU9 + +bool ItemInstance::isFramed() +{ + return frame != NULL; +} + +void ItemInstance::setFramed(shared_ptr frame) +{ + this->frame = frame; +} + +shared_ptr ItemInstance::getFrame() +{ + return frame; +} + +int ItemInstance::getBaseRepairCost() +{ + if (hasTag() && tag->contains(L"RepairCost")) + { + return tag->getInt(L"RepairCost"); + } + else + { + return 0; + } +} + +void ItemInstance::setRepairCost(int cost) +{ + if (!hasTag()) tag = new CompoundTag(); + tag->putInt(L"RepairCost", cost); +} diff --git a/Minecraft.World/ItemInstance.h b/Minecraft.World/ItemInstance.h new file mode 100644 index 00000000..c1e52177 --- /dev/null +++ b/Minecraft.World/ItemInstance.h @@ -0,0 +1,154 @@ +#pragma once +using namespace std; + +#include "UseAnim.h" +#include "com.mojang.nbt.h" + +class Entity; +class Level; +class Player; +class Mob; +class CompoundTag; +class Enchantment; +class Rarity; +// 4J-PB - added +class MapItem; +class ItemFrame; +class Icon; + +// 4J Stu - While this is not really an abstract class, we don't want to make new instances of it, +// mainly because there are too many ctors and that doesn't fit well into out macroisation setup +class ItemInstance: public enable_shared_from_this +{ +public: + static const wchar_t *TAG_ENCH_ID; + static const wchar_t *TAG_ENCH_LEVEL; + + int count; + int popTime; + int id; + + // 4J Stu - Brought forward for enchanting/game rules + CompoundTag *tag; + + /** + * This was previously the damage value, but is now used for different stuff + * depending on item / tile. Use the getter methods to make sure the value + * is interpreted correctly. + */ +private: + int auxValue; + // 4J-PB - added for trading menu + bool m_bForceNumberDisplay; + + void _init(int id, int count, int auxValue); + + // TU9 + shared_ptr frame; + +public: + ItemInstance(Tile *tile); + ItemInstance(Tile *tile, int count); + ItemInstance(Tile *tile, int count, int auxValue); + ItemInstance(Item *item); + // 4J-PB - added + ItemInstance(MapItem *item, int count); + + ItemInstance(Item *item, int count); + ItemInstance(Item *item, int count, int auxValue); + ItemInstance(int id, int count, int damage); + + static shared_ptr fromTag(CompoundTag *itemTag); +private: + ItemInstance() { _init(-1,0,0); } + +public: + ~ItemInstance(); + shared_ptr remove(int count); + + Item *getItem() const; + Icon *getIcon(); + int getIconType(); + bool useOn(shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + float getDestroySpeed(Tile *tile); + bool TestUse(Level *level, shared_ptr player); + shared_ptr use(Level *level, shared_ptr player); + shared_ptr useTimeDepleted(Level *level, shared_ptr player); + CompoundTag *save(CompoundTag *compoundTag); + void load(CompoundTag *compoundTag); + int getMaxStackSize(); + bool isStackable(); + bool isDamageableItem(); + bool isStackedByData(); + bool isDamaged(); + int getDamageValue(); + int getAuxValue() const; + void setAuxValue(int value); + int getMaxDamage(); + void hurt(int i, shared_ptr owner); + void hurtEnemy(shared_ptr mob, shared_ptr attacker); + void mineBlock(Level *level, int tile, int x, int y, int z, shared_ptr owner); + int getAttackDamage(shared_ptr entity); + bool canDestroySpecial(Tile *tile); + bool interactEnemy(shared_ptr mob); + shared_ptr copy() const; + ItemInstance *copy_not_shared() const; // 4J Stu - Added for use in recipes + static bool tagMatches(shared_ptr a, shared_ptr b); // 4J Brought forward from 1.2 + static bool matches(shared_ptr a, shared_ptr b); + + // 4J-PB + int GetCount() {return count;} + void ForceNumberDisplay(bool bForce) {m_bForceNumberDisplay=bForce;} // to force the display of 0 and 1 on the required trading items when you have o or 1 of the item + bool GetForceNumberDisplay() {return m_bForceNumberDisplay;} // to force the display of 0 and 1 on the required trading items when you have o or 1 of the item + +private: + bool matches(shared_ptr b); + +public: + bool sameItem(shared_ptr b); + bool sameItemWithTags(shared_ptr b); //4J Added + bool sameItem_not_shared(ItemInstance *b); // 4J Stu - Added this for the one time I need it + virtual unsigned int getUseDescriptionId(); // 4J Added + virtual unsigned int getDescriptionId(int iData = -1); + virtual ItemInstance *setDescriptionId(unsigned int id); + static shared_ptr clone(shared_ptr item); + wstring toString(); + void inventoryTick(Level *level, shared_ptr owner, int slot, bool selected); + void onCraftedBy(Level *level, shared_ptr player, int craftCount); + bool equals(shared_ptr ii); + + int getUseDuration(); + UseAnim getUseAnimation(); + void releaseUsing(Level *level, shared_ptr player, int durationLeft); + + // 4J Stu - Brought forward these functions for enchanting/game rules + bool hasTag(); + CompoundTag *getTag(); + ListTag *getEnchantmentTags(); + void setTag(CompoundTag *tag); + wstring getHoverName(); + void setHoverName(const wstring &name); + bool hasCustomHoverName(); + vector *getHoverText(shared_ptr player, bool advanced, vector &unformattedStrings); + vector *getHoverTextOnly(shared_ptr player, bool advanced, vector &unformattedStrings); // 4J Added + bool isFoil(); + const Rarity *getRarity(); + bool isEnchantable(); + void enchant(const Enchantment *enchantment, int level); + bool isEnchanted(); + void addTagElement(wstring name, Tag *tag); + + // 4J Added + void set4JData(int data); + int get4JData(); + bool hasPotionStrengthBar(); + int GetPotionStrength(); + + // TU9 + bool isFramed(); + void setFramed(shared_ptr frame); + shared_ptr getFrame(); + + int getBaseRepairCost(); + void setRepairCost(int cost); +}; \ No newline at end of file diff --git a/Minecraft.World/ItemStat.cpp b/Minecraft.World/ItemStat.cpp new file mode 100644 index 00000000..18a47442 --- /dev/null +++ b/Minecraft.World/ItemStat.cpp @@ -0,0 +1,11 @@ +#include "stdafx.h" +#include "ItemStat.h" + +ItemStat::ItemStat(int id, const wstring& name, int itemId) : Stat( id, name ), itemId(itemId) +{ +} + +int ItemStat::getItemId() +{ + return itemId; +} diff --git a/Minecraft.World/ItemStat.h b/Minecraft.World/ItemStat.h new file mode 100644 index 00000000..80e4d863 --- /dev/null +++ b/Minecraft.World/ItemStat.h @@ -0,0 +1,14 @@ +#pragma once +using namespace std; + +#include "Stat.h" + +class ItemStat : public Stat +{ +private: + const int itemId; + +public: + ItemStat(int id, const wstring& name, int itemId); + int getItemId(); +}; diff --git a/Minecraft.World/JavaIntHash.h b/Minecraft.World/JavaIntHash.h new file mode 100644 index 00000000..447b8052 --- /dev/null +++ b/Minecraft.World/JavaIntHash.h @@ -0,0 +1,77 @@ +#pragma once + +// Java doesn't have a default hash value for ints, however, the hashmap itself does some "supplemental" hashing, so +// our ints actually get hashed by code as implemented below. std templates *do* have a standard hash for ints, but it +// would appear to be a bit expensive so matching the java one for now anyway. This code implements the supplemental +// hashing that happens in java so we can match what their maps are doing with ints. + +typedef struct +{ + int operator() (const int &k) const + { + int h = k; + h += ~(h << 9); + h ^= (((unsigned int)h) >> 14); + h += (h << 4); + h ^= (((unsigned int)h) >> 10); + return h; + } + +} IntKeyHash; + +typedef struct +{ + bool operator() (const int &x, const int &y) const { return x==y; } +} IntKeyEq; + + +// This hash functor is taken from the IntHashMap java class used by the game, so that we can use a standard std hashmap with this hash rather +// than implement the class itself +typedef struct +{ + int operator() (const int &k) const + { + unsigned int h = (unsigned int)k; + h ^= (h >> 20) ^ (h >> 12); + return (int)(h ^ (h >> 7) ^ (h >> 4)); + } +} IntKeyHash2; + + +// This hash functor is taken from the LongHashMap java class used by the game, so that we can use a standard std hashmap with this hash rather +// than implement the class itself +typedef struct +{ + int hash(const int &k) const + { + unsigned int h = (unsigned int)k; + h ^= (h >> 20) ^ (h >> 12); + return (int)(h ^ (h >> 7) ^ (h >> 4)); + } + + int operator() (const __int64 &k) const + { + return hash((int) ( k ^ (((__uint64)k) >> 32 ))); + } +} LongKeyHash; + +typedef struct +{ + bool operator() (const __int64 &x, const __int64 &y) const { return x==y; } +} LongKeyEq; + +typedef struct +{ + int operator() (const eINSTANCEOF &k) const + { + unsigned int h = (unsigned int)k; + h ^= (h >> 20) ^ (h >> 12); + return (int)(h ^ (h >> 7) ^ (h >> 4)); + } +} eINSTANCEOFKeyHash; + +typedef struct +{ + bool operator() (const eINSTANCEOF &x, const eINSTANCEOF &y) const { return x==y; } +} eINSTANCEOFKeyEq; + diff --git a/Minecraft.World/JavaMath.cpp b/Minecraft.World/JavaMath.cpp new file mode 100644 index 00000000..92843695 --- /dev/null +++ b/Minecraft.World/JavaMath.cpp @@ -0,0 +1,76 @@ +#include "stdafx.h" + +#include "JavaMath.h" + +Random Math::rand = Random(); + +//Returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0. +//Returned values are chosen pseudorandomly with (approximately) uniform distribution from that range. +//When this method is first called, it creates a single new pseudorandom-number generator, exactly as if by the expression +// +//new java.util.Random +//This new pseudorandom-number generator is used thereafter for all calls to this method and is used nowhere else. +//This method is properly synchronized to allow correct use by more than one thread. However, if many threads need to +//generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator. +// +//Returns: +//a pseudorandom double greater than or equal to 0.0 and less than 1.0. +double Math::random() +{ + return Math::rand.nextDouble(); +} + +//Returns the closest long to the argument. The result is rounded to an integer by adding 1/2, taking the floor of the result, +//and casting the result to type long. In other words, the result is equal to the value of the expression: +//(long)Math.floor(a + 0.5d) +//Special cases: +// +//If the argument is NaN, the result is 0. +//If the argument is negative infinity or any value less than or equal to the value of Long.MIN_VALUE, the result is equal to the +//value of Long.MIN_VALUE. +//If the argument is positive infinity or any value greater than or equal to the value of Long.MAX_VALUE, the result is equal to the +//value of Long.MAX_VALUE. +//Parameters: +//a - a floating-point value to be rounded to a long. +//Returns: +//the value of the argument rounded to the nearest long value. +__int64 Math::round( double d ) +{ + return (__int64)floor( d + 0.5 ); +} + +int Math::_max(int a, int b) +{ + return a > b ? a : b; +} + +int Math::_min(int a, int b) +{ + return a < b ? a : b; +} + +float Math::_max(float a, float b) +{ + return a > b ? a : b; +} + +float Math::_min(float a, float b) +{ + return a < b ? a : b; +} + +float Math::wrapDegrees(float input) +{ + while(input>=360.0f)input-=360.0f; + if (input >= 180.0f) input -= 360.0f; + if (input < -180.0f) input += 360.0f; + return input; +} + +double Math::wrapDegrees(double input) +{ + while(input>=360.0)input-=360.0; + if (input >= 180.0) input -= 360.0; + if (input < -180.0) input += 360.0; + return input; +} \ No newline at end of file diff --git a/Minecraft.World/JavaMath.h b/Minecraft.World/JavaMath.h new file mode 100644 index 00000000..fcd095c8 --- /dev/null +++ b/Minecraft.World/JavaMath.h @@ -0,0 +1,19 @@ +#pragma once +#include "Random.h" + +class Math +{ +private: + static Random rand; + +public: + static double random(); + static __int64 round( double d ); + static int _max(int a, int b); + static float _max(float a, float b); + static int _min(int a, int b); + static float _min(float a, float b); + + static float wrapDegrees(float input); + static double wrapDegrees(double input); +}; \ No newline at end of file diff --git a/Minecraft.World/JumpControl.cpp b/Minecraft.World/JumpControl.cpp new file mode 100644 index 00000000..458e6031 --- /dev/null +++ b/Minecraft.World/JumpControl.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "JumpControl.h" + +JumpControl::JumpControl(Mob *mob) +{ + _jump = false; + + this->mob = mob; +} + +void JumpControl::jump() +{ + _jump = true; +} + +void JumpControl::tick() +{ + mob->setJumping(_jump); + _jump = false; +} \ No newline at end of file diff --git a/Minecraft.World/JumpControl.h b/Minecraft.World/JumpControl.h new file mode 100644 index 00000000..b33648c7 --- /dev/null +++ b/Minecraft.World/JumpControl.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Control.h" + +class Mob; + +class JumpControl : public Control +{ +private: + Mob *mob; + bool _jump; + +public: + JumpControl(Mob *mob); + + void jump(); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/JungleBiome.cpp b/Minecraft.World/JungleBiome.cpp new file mode 100644 index 00000000..ba30a896 --- /dev/null +++ b/Minecraft.World/JungleBiome.cpp @@ -0,0 +1,63 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.animal.h" +#include "JungleBiome.h" + +JungleBiome::JungleBiome(int id) : Biome(id) +{ + decorator->treeCount = 50; + decorator->grassCount = 25; + decorator->flowerCount = 4; + + enemies.push_back(new MobSpawnerData(eTYPE_OZELOT, 2, 1, 1)); + + // make chicken a lot more common in the jungle + friendlies.push_back(new MobSpawnerData(eTYPE_CHICKEN, 10, 4, 4)); +} + + +Feature *JungleBiome::getTreeFeature(Random *random) +{ + if (random->nextInt(10) == 0) + { + return new BasicTree(false); // 4J used to return member fancyTree, now returning newly created object so that caller can be consistently resposible for cleanup + } + if (random->nextInt(2) == 0) + { + return new GroundBushFeature(TreeTile::JUNGLE_TRUNK, LeafTile::NORMAL_LEAF); + } + if (random->nextInt(3) == 0) + { + return new MegaTreeFeature(false, 10 + random->nextInt(20), TreeTile::JUNGLE_TRUNK, LeafTile::JUNGLE_LEAF); + } + return new TreeFeature(false, 4 + random->nextInt(7), TreeTile::JUNGLE_TRUNK, LeafTile::JUNGLE_LEAF, true); +} + +Feature *JungleBiome::getGrassFeature(Random *random) +{ + if (random->nextInt(4) == 0) + { + return new TallGrassFeature(Tile::tallgrass_Id, TallGrass::FERN); + } + return new TallGrassFeature(Tile::tallgrass_Id, TallGrass::TALL_GRASS); +} + +void JungleBiome::decorate(Level *level, Random *random, int xo, int zo) +{ + Biome::decorate(level, random, xo, zo); + + PIXBeginNamedEvent(0, "Adding vines"); + VinesFeature *vines = new VinesFeature(); + + for (int i = 0; i < 50; i++) + { + int x = xo + random->nextInt(16) + 8; + int y = Level::genDepth / 2; + int z = zo + random->nextInt(16) + 8; + vines->place(level, random, x, y, z); + } + PIXEndNamedEvent(); +} \ No newline at end of file diff --git a/Minecraft.World/JungleBiome.h b/Minecraft.World/JungleBiome.h new file mode 100644 index 00000000..115afb5d --- /dev/null +++ b/Minecraft.World/JungleBiome.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Biome.h" + +class JungleBiome : public Biome +{ +public: + JungleBiome(int id); + + + Feature *getTreeFeature(Random *random); + Feature *getGrassFeature(Random *random); + void decorate(Level *level, Random *random, int xo, int zo); +}; \ No newline at end of file diff --git a/Minecraft.World/KeepAlivePacket.cpp b/Minecraft.World/KeepAlivePacket.cpp new file mode 100644 index 00000000..fee97910 --- /dev/null +++ b/Minecraft.World/KeepAlivePacket.cpp @@ -0,0 +1,52 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "KeepAlivePacket.h" + + + +KeepAlivePacket::KeepAlivePacket() +{ + id = 0; +} + +KeepAlivePacket::KeepAlivePacket(int id) +{ + this->id = id; +} + +void KeepAlivePacket::handle(PacketListener *listener) +{ + listener->handleKeepAlive(shared_from_this()); +} + +void KeepAlivePacket::read(DataInputStream *dis) //throws IOException +{ + id = dis->readInt(); +} + +void KeepAlivePacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(id); +} + +int KeepAlivePacket::getEstimatedSize() +{ + return 4; +} + +bool KeepAlivePacket::canBeInvalidated() +{ + return true; +} + +bool KeepAlivePacket::isInvalidatedBy(shared_ptr packet) +{ + return true; +} + +bool KeepAlivePacket::isAync() +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/KeepAlivePacket.h b/Minecraft.World/KeepAlivePacket.h new file mode 100644 index 00000000..a44d8745 --- /dev/null +++ b/Minecraft.World/KeepAlivePacket.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class KeepAlivePacket : public Packet, public enable_shared_from_this +{ +public: + int id; + + KeepAlivePacket(); + KeepAlivePacket(int id); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + virtual bool isAync(); + +public: + static shared_ptr create() { return shared_ptr(new KeepAlivePacket()); } + virtual int getId() { return 0; } +}; \ No newline at end of file diff --git a/Minecraft.World/KickPlayerPacket.cpp b/Minecraft.World/KickPlayerPacket.cpp new file mode 100644 index 00000000..cfef71e0 --- /dev/null +++ b/Minecraft.World/KickPlayerPacket.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "KickPlayerPacket.h" + + + +KickPlayerPacket::KickPlayerPacket() +{ + m_networkSmallId = 0; +} + +KickPlayerPacket::KickPlayerPacket(BYTE networkSmallId) +{ + m_networkSmallId = networkSmallId; +} + +void KickPlayerPacket::handle(PacketListener *listener) +{ + listener->handleKickPlayer(shared_from_this()); +} + +void KickPlayerPacket::read(DataInputStream *dis) //throws IOException +{ + m_networkSmallId = dis->readByte(); +} + +void KickPlayerPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeByte(m_networkSmallId); +} + +int KickPlayerPacket::getEstimatedSize() +{ + return 1; +} diff --git a/Minecraft.World/KickPlayerPacket.h b/Minecraft.World/KickPlayerPacket.h new file mode 100644 index 00000000..b9a6ce3e --- /dev/null +++ b/Minecraft.World/KickPlayerPacket.h @@ -0,0 +1,22 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class KickPlayerPacket : public Packet, public enable_shared_from_this +{ +public: + BYTE m_networkSmallId; + + KickPlayerPacket(); + KickPlayerPacket(BYTE networkSmallId); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new KickPlayerPacket()); } + virtual int getId() { return 159; } +}; \ No newline at end of file diff --git a/Minecraft.World/KillCommand.cpp b/Minecraft.World/KillCommand.cpp new file mode 100644 index 00000000..30a7880e --- /dev/null +++ b/Minecraft.World/KillCommand.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" +#include "net.minecraft.commands.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.damagesource.h" +#include "KillCommand.h" + +EGameCommand KillCommand::getId() +{ + return eGameCommand_Kill; +} + +void KillCommand::execute(shared_ptr source, byteArray commandData) +{ + shared_ptr player = dynamic_pointer_cast(source); + + player->hurt(DamageSource::outOfWorld, 1000); + + source->sendMessage(L"Ouch. That look like it hurt."); +} \ No newline at end of file diff --git a/Minecraft.World/KillCommand.h b/Minecraft.World/KillCommand.h new file mode 100644 index 00000000..a88b2069 --- /dev/null +++ b/Minecraft.World/KillCommand.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Command.h" + +class KillCommand : public Command +{ +public: + virtual EGameCommand getId(); + virtual void execute(shared_ptr source, byteArray commandData); +}; \ No newline at end of file diff --git a/Minecraft.World/KnockbackEnchantment.cpp b/Minecraft.World/KnockbackEnchantment.cpp new file mode 100644 index 00000000..981bd8af --- /dev/null +++ b/Minecraft.World/KnockbackEnchantment.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "KnockbackEnchantment.h" + +KnockbackEnchantment::KnockbackEnchantment(int id, int frequency) : Enchantment(id, frequency, EnchantmentCategory::weapon) +{ + setDescriptionId(IDS_ENCHANTMENT_KNOCKBACK); +} + +int KnockbackEnchantment::getMinCost(int level) +{ + return 5 + 20 * (level - 1); +} + +int KnockbackEnchantment::getMaxCost(int level) +{ + return Enchantment::getMinCost(level) + 50; +} + +int KnockbackEnchantment::getMaxLevel() +{ + return 2; +} \ No newline at end of file diff --git a/Minecraft.World/KnockbackEnchantment.h b/Minecraft.World/KnockbackEnchantment.h new file mode 100644 index 00000000..c7ecbe4d --- /dev/null +++ b/Minecraft.World/KnockbackEnchantment.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Enchantment.h" + +class KnockbackEnchantment : public Enchantment +{ +public: + KnockbackEnchantment(int id, int frequency); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); +}; \ No newline at end of file diff --git a/Minecraft.World/LadderTile.cpp b/Minecraft.World/LadderTile.cpp new file mode 100644 index 00000000..32f35ed8 --- /dev/null +++ b/Minecraft.World/LadderTile.cpp @@ -0,0 +1,112 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "LadderTile.h" + + +LadderTile::LadderTile(int id) : Tile(id, Material::decoration,isSolidRender()) +{ +} + +AABB *LadderTile::getAABB(Level *level, int x, int y, int z) +{ + updateShape(level, x, y, z); + return Tile::getAABB(level, x, y, z); +} + +AABB *LadderTile::getTileAABB(Level *level, int x, int y, int z) +{ + updateShape(level, x, y, z); + return Tile::getTileAABB(level, x, y, z); +} + +void LadderTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + setShape(level->getData(x, y, z)); +} + +void LadderTile::setShape(int data) +{ + int dir = data; + float r = 2 / 16.0f; + + if (dir == 2) setShape(0, 0, 1 - r, 1, 1, 1); + if (dir == 3) setShape(0, 0, 0, 1, 1, r); + if (dir == 4) setShape(1 - r, 0, 0, 1, 1, 1); + if (dir == 5) setShape(0, 0, 0, r, 1, 1); +} + +bool LadderTile::blocksLight() +{ + return false; +} + +bool LadderTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool LadderTile::isCubeShaped() +{ + return false; +} + +int LadderTile::getRenderShape() +{ + return Tile::SHAPE_LADDER; +} + +bool LadderTile::mayPlace(Level *level, int x, int y, int z) +{ + if (level->isSolidBlockingTile(x - 1, y, z)) + { + return true; + } + else if (level->isSolidBlockingTile(x + 1, y, z)) + { + return true; + } + else if (level->isSolidBlockingTile(x, y, z - 1)) + { + return true; + } + else if (level->isSolidBlockingTile(x, y, z + 1)) + { + return true; + } + return false; +} + +int LadderTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) +{ + int dir = level->getData(x, y, z); + + if ((dir == 0 || face == 2) && level->isSolidBlockingTile(x, y, z + 1)) dir = 2; + if ((dir == 0 || face == 3) && level->isSolidBlockingTile(x, y, z - 1)) dir = 3; + if ((dir == 0 || face == 4) && level->isSolidBlockingTile(x + 1, y, z)) dir = 4; + if ((dir == 0 || face == 5) && level->isSolidBlockingTile(x - 1, y, z)) dir = 5; + + return dir; +} + +void LadderTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + int face = level->getData(x, y, z); + bool ok = false; + + if (face == 2 && level->isSolidBlockingTile(x, y, z + 1)) ok = true; + if (face == 3 && level->isSolidBlockingTile(x, y, z - 1)) ok = true; + if (face == 4 && level->isSolidBlockingTile(x + 1, y, z)) ok = true; + if (face == 5 && level->isSolidBlockingTile(x - 1, y, z)) ok = true; + if (!ok) + { + spawnResources(level, x, y, z, face, 0); + level->setTile(x, y, z, 0); + } + + Tile::neighborChanged(level, x, y, z, type); +} + +int LadderTile::getResourceCount(Random* random) +{ + return 1; +} \ No newline at end of file diff --git a/Minecraft.World/LadderTile.h b/Minecraft.World/LadderTile.h new file mode 100644 index 00000000..823f35a7 --- /dev/null +++ b/Minecraft.World/LadderTile.h @@ -0,0 +1,26 @@ +#pragma once +#include "Tile.h" +#include "Definitions.h" + +class Random; + +class LadderTile : public Tile +{ + friend class Tile; +protected: + LadderTile(int id); +public: + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual AABB *getTileAABB(Level *level, int x, int y, int z); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + using Tile::setShape; + virtual void setShape(int data); + virtual bool blocksLight(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual int getRenderShape(); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual int getResourceCount(Random* random); +}; \ No newline at end of file diff --git a/Minecraft.World/LakeFeature.cpp b/Minecraft.World/LakeFeature.cpp new file mode 100644 index 00000000..7450450d --- /dev/null +++ b/Minecraft.World/LakeFeature.cpp @@ -0,0 +1,175 @@ +#include "stdafx.h" +#include "LakeFeature.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.biome.h" + +LakeFeature::LakeFeature(int tile) +{ + this->tile = tile; +} + +bool LakeFeature::place(Level *level, Random *random, int x, int y, int z) +{ + x -= 8; + z -= 8; + while (y > 5 && level->isEmptyTile(x, y, z)) + y--; + if (y <= 4) + { + return false; + } + + y -= 4; + + bool grid[16*16*8] = {0}; + + LevelGenerationOptions *levelGenOptions = NULL; + if( app.getLevelGenerationOptions() != NULL ) + { + levelGenOptions = app.getLevelGenerationOptions(); + + int minX = x; + int minY = y; + int minZ = z; + + int maxX = x + 16; + int maxY = y + 8; + int maxZ = z + 16; + + bool intersects = levelGenOptions->checkIntersects(minX, minY, minZ, maxX, maxY, maxZ); + if(intersects) + { + //app.DebugPrintf("Skipping reeds feature generation as it overlaps a game rule structure\n"); + return false; + } + } + + int spots = random->nextInt(4) + 4; + for (int i = 0; i < spots; i++) + { + double xr = random->nextDouble() * 6 + 3; + double yr = random->nextDouble() * 4 + 2; + double zr = random->nextDouble() * 6 + 3; + + double xp = random->nextDouble() * (16 - xr - 2) + 1 + xr / 2; + double yp = random->nextDouble() * (8 - yr - 4) + 2 + yr / 2; + double zp = random->nextDouble() * (16 - zr - 2) + 1 + zr / 2; + + for (int xx = 1; xx < 15; xx++) + { + for (int zz = 1; zz < 15; zz++) + { + for (int yy = 1; yy < 7; yy++) + { + double xd = ((xx - xp) / (xr / 2)); + double yd = ((yy - yp) / (yr / 2)); + double zd = ((zz - zp) / (zr / 2)); + double d = xd * xd + yd * yd + zd * zd; + if (d < 1) grid[((xx) * 16 + (zz)) * 8 + (yy)] = true; + } + } + } + } + + for (int xx = 0; xx < 16; xx++) + { + for (int zz = 0; zz < 16; zz++) + { + for (int yy = 0; yy < 8; yy++) + { + bool check = !grid[((xx) * 16 + (zz)) * 8 + (yy)] && ( + (xx < 15 && grid[((xx + 1) * 16 + (zz)) * 8 + (yy)]) + || (xx > 0 && grid[((xx - 1) * 16 + (zz)) * 8 + (yy)]) + || (zz < 15 && grid[((xx) * 16 + (zz + 1)) * 8 + (yy)]) + || (zz > 0 && grid[((xx) * 16 + (zz - 1)) * 8 + (yy)]) + || (yy < 7 && grid[((xx) * 16 + (zz)) * 8 + (yy + 1)]) + || (yy > 0 && grid[((xx) * 16 + (zz)) * 8 + (yy - 1)])); + + if (check) + { + Material *m = level->getMaterial(x + xx, y + yy, z + zz); + if (yy >= 4 && m->isLiquid()) return false; + if (yy < 4 && (!m->isSolid() && level->getTile(x + xx, y + yy, z + zz) != tile)) return false; + + } + } + } + } + + for (int xx = 0; xx < 16; xx++) + { + for (int zz = 0; zz < 16; zz++) + { + for (int yy = 0; yy < 8; yy++) + { + if (grid[((xx) * 16 + (zz)) * 8 + (yy)]) + { + level->setTileNoUpdate(x + xx, y + yy, z + zz, yy >= 4 ? 0 : tile); + } + } + } + } + + for (int xx = 0; xx < 16; xx++) + { + for (int zz = 0; zz < 16; zz++) + { + for (int yy = 4; yy < 8; yy++) + { + if (grid[((xx) * 16 + (zz)) * 8 + (yy)]) + { + if (level->getTile(x + xx, y + yy - 1, z + zz) == Tile::dirt_Id && level->getBrightness(LightLayer::Sky, x + xx, y + yy, z + zz) > 0) + { + Biome *b = level->getBiome(x + xx, z + zz); + if (b->topMaterial == Tile::mycel_Id) level->setTileNoUpdate(x + xx, y + yy - 1, z + zz, Tile::mycel_Id); + else level->setTileNoUpdate(x + xx, y + yy - 1, z + zz, Tile::grass_Id); + } + } + } + } + } + + if (Tile::tiles[tile]->material == Material::lava) + { + for (int xx = 0; xx < 16; xx++) + { + for (int zz = 0; zz < 16; zz++) + { + for (int yy = 0; yy < 8; yy++) + { + bool check = !grid[((xx) * 16 + (zz)) * 8 + (yy)] && ( + (xx < 15 && grid[(((xx + 1) * 16 + (zz)) * 8 + (yy))]) + || (xx > 0 && grid[(((xx - 1) * 16 + (zz)) * 8 + (yy))]) + || (zz < 15 && grid[(((xx) * 16 + (zz + 1)) * 8 + (yy))]) + || (zz > 0 && grid[(((xx) * 16 + (zz - 1)) * 8 + (yy))]) + || (yy < 7 && grid[(((xx) * 16 + (zz)) * 8 + (yy + 1))]) + || (yy > 0 && grid[(((xx) * 16 + (zz)) * 8 + (yy - 1))])); + + if (check) + { + if ((yy<4 || random->nextInt(2)!=0) && level->getMaterial(x + xx, y + yy, z + zz)->isSolid()) + { + level->setTileNoUpdate(x + xx, y + yy, z + zz, Tile::rock_Id); + } + } + } + } + } + } + + // 4J - brought forward from 1.8.2 + if (Tile::tiles[tile]->material == Material::water) + { + for (int xx = 0; xx < 16; xx++) + { + for (int zz = 0; zz < 16; zz++) + { + int yy = 4; + if (level->shouldFreezeIgnoreNeighbors(x + xx, y + yy, z + zz)) level->setTileNoUpdate(x + xx, y + yy, z + zz, Tile::ice_Id); + } + } + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/LakeFeature.h b/Minecraft.World/LakeFeature.h new file mode 100644 index 00000000..ddaebc14 --- /dev/null +++ b/Minecraft.World/LakeFeature.h @@ -0,0 +1,14 @@ +#pragma once +#include "Feature.h" +#include "Material.h" + +class LakeFeature : public Feature +{ +private: + int tile; + +public: + LakeFeature (int tile); + + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/Language.cpp b/Minecraft.World/Language.cpp new file mode 100644 index 00000000..73e56a47 --- /dev/null +++ b/Minecraft.World/Language.cpp @@ -0,0 +1,50 @@ +#include "stdafx.h" +#include "Language.h" + +// 4J - TODO - properly implement + +Language *Language::singleton = new Language(); + +Language::Language() +{ +} + +Language *Language::getInstance() +{ + return singleton; +} + +/* 4J Jev, creates 2 identical functions. +wstring Language::getElement(const wstring& elementId) +{ + return elementId; +} */ + +wstring Language::getElement(const wstring& elementId, ...) +{ +#ifdef __PSVITA__ // 4J - vita doesn't like having a reference type as the last parameter passed to va_start - we shouldn't need this method anyway + return L""; +#elif _MSC_VER >= 1930 // VS2022+ also disallows va_start with reference types + return elementId; +#else + va_list args; + va_start(args, elementId); + return getElement(elementId, args); +#endif +} + +wstring Language::getElement(const wstring& elementId, va_list args) +{ + // 4J TODO + return elementId; +} + +wstring Language::getElementName(const wstring& elementId) +{ + return elementId; +} + +wstring Language::getElementDescription(const wstring& elementId) +{ + return elementId; +} \ No newline at end of file diff --git a/Minecraft.World/Language.h b/Minecraft.World/Language.h new file mode 100644 index 00000000..10e2951f --- /dev/null +++ b/Minecraft.World/Language.h @@ -0,0 +1,14 @@ +#pragma once + +class Language +{ +private: + static Language *singleton; +public: + Language(); + static Language *getInstance(); + wstring getElement(const wstring& elementId, ...); + wstring getElement(const wstring& elementId, va_list args); + wstring getElementName(const wstring& elementId); + wstring getElementDescription(const wstring& elementId); +}; \ No newline at end of file diff --git a/Minecraft.World/LargeCaveFeature.cpp b/Minecraft.World/LargeCaveFeature.cpp new file mode 100644 index 00000000..20b83a30 --- /dev/null +++ b/Minecraft.World/LargeCaveFeature.cpp @@ -0,0 +1,196 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.biome.h" +#include "LargeCaveFeature.h" + +void LargeCaveFeature::addRoom(__int64 seed, int xOffs, int zOffs, byteArray blocks, double xRoom, double yRoom, double zRoom) +{ + addTunnel(seed, xOffs, zOffs, blocks, xRoom, yRoom, zRoom, 1 + random->nextFloat() * 6, 0, 0, -1, -1, 0.5); +} + +void LargeCaveFeature::addTunnel(__int64 seed, int xOffs, int zOffs, byteArray blocks, double xCave, double yCave, double zCave, float thickness, float yRot, float xRot, int step, int dist, double yScale) +{ + double xMid = xOffs * 16 + 8; + double zMid = zOffs * 16 + 8; + + float yRota = 0; + float xRota = 0; + Random random = Random(seed); + + if (dist <= 0) + { + int max = radius * 16 - 16; + dist = max - random.nextInt(max / 4); + } + bool singleStep = false; + + if (step == -1) + { + step = dist / 2; + singleStep = true; + } + + + int splitPoint = random.nextInt(dist / 2) + dist / 4; + bool steep = random.nextInt(6) == 0; + + for (; step < dist; step++) + { + double rad = 1.5 + (Mth::sin(step * PI / dist) * thickness) * 1; + double yRad = rad * yScale; + + float xc = Mth::cos(xRot); + float xs = Mth::sin(xRot); + xCave += Mth::cos(yRot) * xc; + yCave += xs; + zCave += Mth::sin(yRot) * xc; + + if (steep) + { + xRot *= 0.92f; + } + else + { + xRot *= 0.7f; + } + xRot += xRota * 0.1f; + yRot += yRota * 0.1f; + + xRota *= 0.90f; + yRota *= 0.75f; + xRota += (random.nextFloat() - random.nextFloat()) * random.nextFloat() * 2; + yRota += (random.nextFloat() - random.nextFloat()) * random.nextFloat() * 4; + + + if (!singleStep && step == splitPoint && thickness > 1 && dist > 0) + { + addTunnel(random.nextLong(), xOffs, zOffs, blocks, xCave, yCave, zCave, random.nextFloat() * 0.5f + 0.5f, yRot - PI / 2, xRot / 3, step, dist, 1.0); + addTunnel(random.nextLong(), xOffs, zOffs, blocks, xCave, yCave, zCave, random.nextFloat() * 0.5f + 0.5f, yRot + PI / 2, xRot / 3, step, dist, 1.0); + return; + } + if (!singleStep && random.nextInt(4) == 0) continue; + { + double xd = xCave - xMid; + double zd = zCave - zMid; + double remaining = dist - step; + double rr = (thickness + 2) + 16; + if (xd * xd + zd * zd - (remaining * remaining) > rr * rr) + { + return; + } + } + + if (xCave < xMid - 16 - rad * 2 || zCave < zMid - 16 - rad * 2 || xCave > xMid + 16 + rad * 2 || zCave > zMid + 16 + rad * 2) continue; + + int x0 = Mth::floor(xCave - rad) - xOffs * 16 - 1; + int x1 = Mth::floor(xCave + rad) - xOffs * 16 + 1; + + int y0 = Mth::floor(yCave - yRad) - 1; + int y1 = Mth::floor(yCave + yRad) + 1; + + int z0 = Mth::floor(zCave - rad) - zOffs * 16 - 1; + int z1 = Mth::floor(zCave + rad) - zOffs * 16 + 1; + + if (x0 < 0) x0 = 0; + if (x1 > 16) x1 = 16; + + if (y0 < 1) y0 = 1; + if (y1 > Level::genDepth - 8) y1 = Level::genDepth - 8; + + if (z0 < 0) z0 = 0; + if (z1 > 16) z1 = 16; + + bool detectedWater = false; + for (int xx = x0; !detectedWater && xx < x1; xx++) + { + for (int zz = z0; !detectedWater && zz < z1; zz++) + { + for (int yy = y1 + 1; !detectedWater && yy >= y0 - 1; yy--) + { + int p = (xx * 16 + zz) * Level::genDepth + yy; + if (yy < 0 || yy >= Level::genDepth) continue; + if (blocks[p] == Tile::water_Id || blocks[p] == Tile::calmWater_Id) + { + detectedWater = true; + } + if (yy != y0 - 1 && xx != x0 && xx != x1 - 1 && zz != z0 && zz != z1 - 1) + { + yy = y0; + } + } + } + } + if (detectedWater) continue; + + for (int xx = x0; xx < x1; xx++) + { + double xd = ((xx + xOffs * 16 + 0.5) - xCave) / rad; + for (int zz = z0; zz < z1; zz++) + { + double zd = ((zz + zOffs * 16 + 0.5) - zCave) / rad; + int p = (xx * 16 + zz) * Level::genDepth + y1; + bool hasGrass = false; + if (xd * xd + zd * zd < 1) + { + for (int yy = y1 - 1; yy >= y0; yy--) + { + double yd = (yy + 0.5 - yCave) / yRad; + if (yd > -0.7 && xd * xd + yd * yd + zd * zd < 1) + { + int block = blocks[p]; + if (block == Tile::grass_Id) hasGrass = true; + if (block == Tile::rock_Id || block == Tile::dirt_Id || block == Tile::grass_Id) + { + if (yy < 10) + { + blocks[p] = (byte) Tile::lava_Id; + } + else + { + blocks[p] = (byte) 0; + if (hasGrass && blocks[p - 1] == Tile::dirt_Id) blocks[p - 1] = (byte) level->getBiome(xx + xOffs * 16, zz + zOffs * 16)->topMaterial; + } + } + } + p--; + } + } + } + } + if (singleStep) break; + } + +} + +void LargeCaveFeature::addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks) +{ + int caves = random->nextInt(random->nextInt(random->nextInt(40) + 1) + 1); + if (random->nextInt(15) != 0) caves = 0; + + for (int cave = 0; cave < caves; cave++) + { + double xCave = x * 16 + random->nextInt(16); + double yCave = random->nextInt(random->nextInt(Level::genDepth - 8) + 8); + double zCave = z * 16 + random->nextInt(16); + + int tunnels = 1; + if (random->nextInt(4) == 0) + { + addRoom(random->nextLong(), xOffs, zOffs, blocks, xCave, yCave, zCave); + tunnels += random->nextInt(4); + } + + for (int i = 0; i < tunnels; i++) + { + + float yRot = random->nextFloat() * PI * 2; + float xRot = ((random->nextFloat() - 0.5f) * 2) / 8; + float thickness = random->nextFloat() * 2 + random->nextFloat(); + if (random->nextInt(10) == 0) thickness *= random->nextFloat() * random->nextFloat() * 3 + 1; + + addTunnel(random->nextLong(), xOffs, zOffs, blocks, xCave, yCave, zCave, thickness, yRot, xRot, 0, 0, 1.0); + } + } + +} diff --git a/Minecraft.World/LargeCaveFeature.h b/Minecraft.World/LargeCaveFeature.h new file mode 100644 index 00000000..5188aca7 --- /dev/null +++ b/Minecraft.World/LargeCaveFeature.h @@ -0,0 +1,11 @@ +#pragma once + +#include "LargeFeature.h" + +class LargeCaveFeature : public LargeFeature +{ +protected: + void addRoom(__int64 seed, int xOffs, int zOffs, byteArray blocks, double xRoom, double yRoom, double zRoom); + void addTunnel(__int64 seed, int xOffs, int zOffs, byteArray blocks, double xCave, double yCave, double zCave, float thickness, float yRot, float xRot, int step, int dist, double yScale); + virtual void addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks); +}; diff --git a/Minecraft.World/LargeFeature.cpp b/Minecraft.World/LargeFeature.cpp new file mode 100644 index 00000000..2e43fac7 --- /dev/null +++ b/Minecraft.World/LargeFeature.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "LargeFeature.h" + +const wstring LargeFeature::STRONGHOLD = L"StrongHold"; + +LargeFeature::LargeFeature() +{ + radius = 8; + random = new Random(); +} + +LargeFeature::~LargeFeature() +{ + delete random; +} + +void LargeFeature::apply(ChunkSource *ChunkSource, Level *level, int xOffs, int zOffs, byteArray blocks) +{ + int r = radius; + this->level = level; + + random->setSeed(level->getSeed()); + __int64 xScale = random->nextLong(); + __int64 zScale = random->nextLong(); + + for (int x = xOffs - r; x <= xOffs + r; x++) + { + for (int z = zOffs - r; z <= zOffs + r; z++) + { + __int64 xx = x * xScale; + __int64 zz = z * zScale; + random->setSeed(xx ^ zz ^ level->getSeed()); + addFeature(level, x, z, xOffs, zOffs, blocks); + } + } +} \ No newline at end of file diff --git a/Minecraft.World/LargeFeature.h b/Minecraft.World/LargeFeature.h new file mode 100644 index 00000000..d2df25a0 --- /dev/null +++ b/Minecraft.World/LargeFeature.h @@ -0,0 +1,23 @@ +#pragma once +#include "ChunkSource.h" + +class Level; + +class LargeFeature +{ +public: + static const wstring STRONGHOLD; +protected: + int radius; + Random *random; + Level *level; + +public: + LargeFeature(); + ~LargeFeature(); + + virtual void apply(ChunkSource *ChunkSource, Level *level, int xOffs, int zOffs, byteArray blocks); + +protected: + virtual void addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks) {} +}; \ No newline at end of file diff --git a/Minecraft.World/LargeHellCaveFeature.cpp b/Minecraft.World/LargeHellCaveFeature.cpp new file mode 100644 index 00000000..ac5d0eda --- /dev/null +++ b/Minecraft.World/LargeHellCaveFeature.cpp @@ -0,0 +1,184 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "LargeHellCaveFeature.h" +#include "net.minecraft.world.level.tile.h" + +void LargeHellCaveFeature::addRoom(int xOffs, int zOffs, byteArray blocks, double xRoom, double yRoom, double zRoom) +{ + addTunnel(xOffs, zOffs, blocks, xRoom, yRoom, zRoom, 1 + random->nextFloat() * 6, 0, 0, -1, -1, 0.5); +} + +void LargeHellCaveFeature::addTunnel(int xOffs, int zOffs, byteArray blocks, double xCave, double yCave, double zCave, float thickness, float yRot, float xRot, int step, int dist, double yScale) +{ + double xMid = xOffs * 16 + 8; + double zMid = zOffs * 16 + 8; + + float yRota = 0; + float xRota = 0; + Random *random = new Random(this->random->nextLong()); + + if (dist <= 0) + { + int max = radius * 16 - 16; + dist = max - random->nextInt(max / 4); + } + bool singleStep = false; + + if (step == -1) + { + step = dist / 2; + singleStep = true; + } + + + int splitPoint = random->nextInt(dist / 2) + dist / 4; + bool steep = random->nextInt(6) == 0; + + for (; step < dist; step++) + { + double rad = 1.5 + (Mth::sin(step * PI / dist) * thickness) * 1; + double yRad = rad * yScale; + + float xc = Mth::cos(xRot); + float xs = Mth::sin(xRot); + xCave += Mth::cos(yRot) * xc; + yCave += xs; + zCave += Mth::sin(yRot) * xc; + + if (steep) + { + xRot *= 0.92f; + } + else + { + xRot *= 0.7f; + } + xRot += xRota * 0.1f; + yRot += yRota * 0.1f; + + xRota *= 0.90f; + yRota *= 0.75f; + xRota += (random->nextFloat() - random->nextFloat()) * random->nextFloat() * 2; + yRota += (random->nextFloat() - random->nextFloat()) * random->nextFloat() * 4; + + + if (!singleStep && step == splitPoint && thickness > 1) + { + addTunnel(xOffs, zOffs, blocks, xCave, yCave, zCave, random->nextFloat() * 0.5f + 0.5f, yRot - PI / 2, xRot / 3, step, dist, 1.0); + addTunnel(xOffs, zOffs, blocks, xCave, yCave, zCave, random->nextFloat() * 0.5f + 0.5f, yRot + PI / 2, xRot / 3, step, dist, 1.0); + delete random; + return; + } + if (!singleStep && random->nextInt(4) == 0) continue; + + { + double xd = xCave - xMid; + double zd = zCave - zMid; + double remaining = dist - step; + double rr = (thickness + 2) + 16; + if (xd * xd + zd * zd - (remaining * remaining) > rr * rr) + { + delete random; + return; + } + } + + if (xCave < xMid - 16 - rad * 2 || zCave < zMid - 16 - rad * 2 || xCave > xMid + 16 + rad * 2 || zCave > zMid + 16 + rad * 2) continue; + + int x0 = Mth::floor(xCave - rad) - xOffs * 16 - 1; + int x1 = Mth::floor(xCave + rad) - xOffs * 16 + 1; + + int y0 = Mth::floor(yCave - yRad) - 1; + int y1 = Mth::floor(yCave + yRad) + 1; + + int z0 = Mth::floor(zCave - rad) - zOffs * 16 - 1; + int z1 = Mth::floor(zCave + rad) - zOffs * 16 + 1; + + if (x0 < 0) x0 = 0; + if (x1 > 16) x1 = 16; + + if (y0 < 1) y0 = 1; + if (y1 > Level::genDepth - 8) y1 = Level::genDepth - 8; + + if (z0 < 0) z0 = 0; + if (z1 > 16) z1 = 16; + + bool detectedWater = false; + for (int xx = x0; !detectedWater && xx < x1; xx++) + { + for (int zz = z0; !detectedWater && zz < z1; zz++) + { + for (int yy = y1 + 1; !detectedWater && yy >= y0 - 1; yy--) + { + int p = (xx * 16 + zz) * Level::genDepth + yy; + if (yy < 0 || yy >= Level::genDepth) continue; + if (blocks[p] == Tile::lava_Id || blocks[p] == Tile::calmLava_Id) + { + detectedWater = true; + } + if (yy != y0 - 1 && xx != x0 && xx != x1 - 1 && zz != z0 && zz != z1 - 1) + { + yy = y0; + } + } + } + } + if (detectedWater) continue; + + for (int xx = x0; xx < x1; xx++) + { + double xd = ((xx + xOffs * 16 + 0.5) - xCave) / rad; + for (int zz = z0; zz < z1; zz++) + { + double zd = ((zz + zOffs * 16 + 0.5) - zCave) / rad; + int p = (xx * 16 + zz) * Level::genDepth + y1; + for (int yy = y1 - 1; yy >= y0; yy--) + { + double yd = (yy + 0.5 - yCave) / yRad; + if (yd > -0.7 && xd * xd + yd * yd + zd * zd < 1) + { + int block = blocks[p]; + if (block == Tile::hellRock_Id || block == Tile::dirt_Id || block == Tile::grass_Id) + { + blocks[p] = (byte) 0; + } + } + p--; + } + } + } + if (singleStep) break; + } + delete random; +} + + +void LargeHellCaveFeature::addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks) +{ + int caves = random->nextInt(random->nextInt(random->nextInt(10) + 1) + 1); + if (random->nextInt(5) != 0) caves = 0; + + for (int cave = 0; cave < caves; cave++) + { + double xCave = x * 16 + random->nextInt(16); + double yCave = random->nextInt(Level::genDepth); + double zCave = z * 16 + random->nextInt(16); + + int tunnels = 1; + if (random->nextInt(4) == 0) { + addRoom(xOffs, zOffs, blocks, xCave, yCave, zCave); + tunnels += random->nextInt(4); + } + + for (int i = 0; i < tunnels; i++) { + + float yRot = random->nextFloat() * PI * 2; + float xRot = ((random->nextFloat() - 0.5f) * 2) / 8; + float thickness = random->nextFloat() * 2 + random->nextFloat(); + + addTunnel(xOffs, zOffs, blocks, xCave, yCave, zCave, thickness*2, yRot, xRot, 0, 0, 0.5); + } + } +} + + diff --git a/Minecraft.World/LargeHellCaveFeature.h b/Minecraft.World/LargeHellCaveFeature.h new file mode 100644 index 00000000..53d33692 --- /dev/null +++ b/Minecraft.World/LargeHellCaveFeature.h @@ -0,0 +1,11 @@ +#pragma once + +#include "LargeFeature.h" + +class LargeHellCaveFeature : public LargeFeature +{ +protected: + void addRoom(int xOffs, int zOffs, byteArray blocks, double xRoom, double yRoom, double zRoom); + void addTunnel(int xOffs, int zOffs, byteArray blocks, double xCave, double yCave, double zCave, float thickness, float yRot, float xRot, int step, int dist, double yScale); + virtual void addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks); +}; diff --git a/Minecraft.World/LavaSlime.cpp b/Minecraft.World/LavaSlime.cpp new file mode 100644 index 00000000..3ab32d90 --- /dev/null +++ b/Minecraft.World/LavaSlime.cpp @@ -0,0 +1,144 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.item.h" +#include "..\Minecraft.Client\Textures.h" +#include "LavaSlime.h" +#include "SoundTypes.h" + + + +LavaSlime::LavaSlime(Level *level) : Slime(level) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + // 4J Stu - The Slime ctor has already called this, and as we don't override it here don't need to call it + //this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_LAVA; // 4J was "/mob/lava.png"; + fireImmune = true; + walkingSpeed = .2f; +} + +bool LavaSlime::canSpawn() +{ + return level->difficulty > Difficulty::PEACEFUL && level->isUnobstructed(bb) && level->getCubes(shared_from_this(), bb)->empty() && !level->containsAnyLiquid(bb); +} + +int LavaSlime::getArmorValue() +{ + return getSize() * 3; +} + +int LavaSlime::getLightColor(float a) +{ + return 15 << 20 | 15 << 4; +} + +float LavaSlime::getBrightness(float a) +{ + return 1.0f; +} + +ePARTICLE_TYPE LavaSlime::getParticleName() +{ + return eParticleType_flame; +} + +shared_ptr LavaSlime::createChild() +{ + return shared_ptr( new LavaSlime(level) ); +} + +int LavaSlime::getDeathLoot() +{ + // 4J-PB - brought forward the magma cream drops + return Item::magmaCream_Id; +} + +void LavaSlime::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + int loot = getDeathLoot(); + if (loot > 0 && getSize() > 1) + { + int count = random->nextInt(4) - 2; + if (playerBonusLevel > 0) + { + count += random->nextInt(playerBonusLevel + 1); + } + for (int i = 0; i < count; i++) + { + spawnAtLocation(loot, 1); + } + } +} + +bool LavaSlime::isOnFire() +{ + return false; +} + + +int LavaSlime::getJumpDelay() +{ + return Slime::getJumpDelay() * 4; +} + +void LavaSlime::decreaseSquish() +{ + targetSquish = targetSquish * 0.90f; +} + +void LavaSlime::jumpFromGround() +{ + yd = 0.42f + getSize() * .1f; + this->hasImpulse = true; +} + +void LavaSlime::causeFallDamage(float distance) +{ +} + +bool LavaSlime::isDealsDamage() +{ + return true; +} + +int LavaSlime::getAttackDamage() +{ + return Slime::getAttackDamage() + 2; +} + +int LavaSlime::getHurtSound() +{ + return eSoundType_MOB_SLIME; +} + +int LavaSlime::getDeathSound() +{ + return eSoundType_MOB_SLIME; +} + +int LavaSlime::getSquishSound() +{ + if (getSize() > 1) + { + return eSoundType_MOB_MAGMACUBE_BIG; + } + return eSoundType_MOB_MAGMACUBE_SMALL; +} + +bool LavaSlime::isInLava() +{ + // hack that makes the lava slimes move freely on the bottom of the lava + // oceans + return false; +} + +bool LavaSlime::doPlayLandSound() +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/LavaSlime.h b/Minecraft.World/LavaSlime.h new file mode 100644 index 00000000..08857ca9 --- /dev/null +++ b/Minecraft.World/LavaSlime.h @@ -0,0 +1,46 @@ +#pragma once + +#include "Slime.h" + +class LavaSlime : public Slime +{ +public: + eINSTANCEOF GetType() { return eTYPE_LAVASLIME; } + static Entity *create(Level *level) { return new LavaSlime(level); } + +public: + LavaSlime(Level *level); + + virtual bool canSpawn(); + virtual int getArmorValue(); + +public: + virtual int getLightColor(float a); + virtual float getBrightness(float a); + +protected: + virtual ePARTICLE_TYPE getParticleName(); + virtual shared_ptr createChild(); + virtual int getDeathLoot(); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + +public: + bool isOnFire(); + +protected: + int getJumpDelay(); + virtual void decreaseSquish(); + virtual void jumpFromGround(); + virtual void causeFallDamage(float distance); + virtual bool isDealsDamage(); + virtual int getAttackDamage(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual int getSquishSound(); + +public: + virtual bool isInLava(); + +protected: + virtual bool doPlayLandSound(); +}; \ No newline at end of file diff --git a/Minecraft.World/Layer.cpp b/Minecraft.World/Layer.cpp new file mode 100644 index 00000000..1d010e72 --- /dev/null +++ b/Minecraft.World/Layer.cpp @@ -0,0 +1,199 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" +#include "net.minecraft.world.level.h" +#include "BiomeOverrideLayer.h" + +#ifdef __PSVITA__ +// AP - this is used to perform fast 64bit divides of known values +#include "..\Minecraft.Client\PSVita\PSVitaExtras\libdivide.h" + +libdivide::divider fast_d2(2); +libdivide::divider fast_d3(3); +libdivide::divider fast_d4(4); +libdivide::divider fast_d5(5); +libdivide::divider fast_d6(6); +libdivide::divider fast_d7(7); +libdivide::divider fast_d10(10); +#endif + +LayerArray Layer::getDefaultLayers(__int64 seed, LevelType *levelType) +{ + // 4J - Some changes moved here from 1.2.3. Temperature & downfall layers are no longer created & returned, and a debug layer is isn't. + // For reference with regard to future merging, things NOT brought forward from the 1.2.3 version are new layer types that we + // don't have yet (shores, swamprivers, region hills etc.) + shared_ptrislandLayer = shared_ptr(new IslandLayer(1)); + islandLayer = shared_ptr(new FuzzyZoomLayer(2000, islandLayer)); + islandLayer = shared_ptr(new AddIslandLayer(1, islandLayer)); + islandLayer = shared_ptr(new ZoomLayer(2001, islandLayer)); + islandLayer = shared_ptr(new AddIslandLayer(2, islandLayer)); + islandLayer = shared_ptr(new AddSnowLayer(2, islandLayer)); + islandLayer = shared_ptr(new ZoomLayer(2002, islandLayer)); + islandLayer = shared_ptr(new AddIslandLayer(3, islandLayer)); + islandLayer = shared_ptr(new ZoomLayer(2003, islandLayer)); + islandLayer = shared_ptr(new AddIslandLayer(4, islandLayer)); +// islandLayer = shared_ptr(new AddMushroomIslandLayer(5, islandLayer)); // 4J - old position of mushroom island layer + + int zoomLevel = 4; + if (levelType == LevelType::lvl_largeBiomes) + { + zoomLevel = 6; + } + + shared_ptr riverLayer = islandLayer; + riverLayer = ZoomLayer::zoom(1000, riverLayer, 0); + riverLayer = shared_ptr(new RiverInitLayer(100, riverLayer)); + riverLayer = ZoomLayer::zoom(1000, riverLayer, zoomLevel + 2); + riverLayer = shared_ptr(new RiverLayer(1, riverLayer)); + riverLayer = shared_ptr(new SmoothLayer(1000, riverLayer)); + + shared_ptr biomeLayer = islandLayer; + biomeLayer = ZoomLayer::zoom(1000, biomeLayer, 0); + biomeLayer = shared_ptr(new BiomeInitLayer(200, biomeLayer, levelType)); + + biomeLayer = ZoomLayer::zoom(1000, biomeLayer, 2); + biomeLayer = shared_ptr(new RegionHillsLayer(1000, biomeLayer)); + + for (int i = 0; i < zoomLevel; i++) + { + biomeLayer = shared_ptr(new ZoomLayer(1000 + i, biomeLayer)); + + if (i == 0) biomeLayer = shared_ptr(new AddIslandLayer(3, biomeLayer)); + + if (i == 0) + { + // 4J - moved mushroom islands to here. This skips 3 zooms that the old location of the add was, making them about 1/8 of the original size. Adding + // them at this scale actually lets us place them near enough other land, if we add them at the same scale as java then they have to be too far out to see for + // the scale of our maps + biomeLayer = shared_ptr(new AddMushroomIslandLayer(5, biomeLayer)); + } + + if (i == 1 ) + { + // 4J - now expand mushroom islands up again. This does a simple region grow to add a new mushroom island element when any of the neighbours are also mushroom islands. + // This helps make the islands into nice compact shapes of the type that are actually likely to be able to make an island out of the sea in a small space. Also + // helps the shore layer from doing too much damage in shrinking the islands we are making + biomeLayer = shared_ptr(new GrowMushroomIslandLayer(5, biomeLayer)); + // Note - this reduces the size of mushroom islands by turning their edges into shores. We are doing this at i == 1 rather than i == 0 as the original does + biomeLayer = shared_ptr(new ShoreLayer(1000, biomeLayer)); + + biomeLayer = shared_ptr(new SwampRiversLayer(1000, biomeLayer)); + } + } + + biomeLayer = shared_ptr(new SmoothLayer(1000, biomeLayer)); + + biomeLayer = shared_ptr(new RiverMixerLayer(100, biomeLayer, riverLayer)); + +#ifndef _CONTENT_PACKAGE +#ifdef _BIOME_OVERRIDE + if(app.DebugSettingsOn() && app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<(new BiomeOverrideLayer(1)); + } +#endif +#endif + + shared_ptr debugLayer = biomeLayer; + + shared_ptrzoomedLayer = shared_ptr(new VoronoiZoom(10, biomeLayer)); + + biomeLayer->init(seed); + zoomedLayer->init(seed); + + LayerArray result(3); + result[0] = biomeLayer; + result[1] = zoomedLayer; + result[2] = debugLayer; + return result; +} + +Layer::Layer(__int64 seedMixup) +{ + parent = nullptr; + + this->seedMixup = seedMixup; + this->seedMixup *= this->seedMixup * 6364136223846793005l + 1442695040888963407l; + this->seedMixup += seedMixup; + this->seedMixup *= this->seedMixup * 6364136223846793005l + 1442695040888963407l; + this->seedMixup += seedMixup; + this->seedMixup *= this->seedMixup * 6364136223846793005l + 1442695040888963407l; + this->seedMixup += seedMixup; +} + +void Layer::init(__int64 seed) +{ + this->seed = seed; + if (parent != NULL) parent->init(seed); + this->seed *= this->seed * 6364136223846793005l + 1442695040888963407l; + this->seed += seedMixup; + this->seed *= this->seed * 6364136223846793005l + 1442695040888963407l; + this->seed += seedMixup; + this->seed *= this->seed * 6364136223846793005l + 1442695040888963407l; + this->seed += seedMixup; +} + +void Layer::initRandom(__int64 x, __int64 y) +{ + rval = seed; + rval *= rval * 6364136223846793005l + 1442695040888963407l; + rval += x; + rval *= rval * 6364136223846793005l + 1442695040888963407l; + rval += y; + rval *= rval * 6364136223846793005l + 1442695040888963407l; + rval += x; + rval *= rval * 6364136223846793005l + 1442695040888963407l; + rval += y; +} + +int Layer::nextRandom(int max) +{ +#ifdef __PSVITA__ + // AP - 64bit mods are very slow on Vita. Replaced with a divide/mult for general case and a fast divide library for specific numbers + // todo - this can sometimes yield a different number to the original. There's a strange bug sometimes with Vita where if the line + // "result = (int) ((rval >> 24) % max);" is done twice in a row 'result' will not be the same. Need to speak to Sony about that + // Also need to compare level against a different platform using the same seed + int result; + long long temp = rval; + temp >>= 24; + if( max == 2 ) + { + result = temp-(temp/fast_d2)*2; + } + else if( max == 3 ) + { + result = temp-(temp/fast_d3)*3; + } + else if( max == 4 ) + { + result = temp-(temp/fast_d4)*4; + } + else if( max == 5 ) + { + result = temp-(temp/fast_d5)*5; + } + else if( max == 6 ) + { + result = temp-(temp/fast_d6)*6; + } + else if( max == 7 ) + { + result = temp-(temp/fast_d7)*7; + } + else if( max == 10 ) + { + result = temp-(temp/fast_d10)*10; + } + else + { + result = temp-(temp/max)*max; + } +#else + + int result = (int) ((rval >> 24) % max); +#endif + + if (result < 0) result += max; + rval *= rval * 6364136223846793005l + 1442695040888963407l; + rval += seed; + return result; +} diff --git a/Minecraft.World/Layer.h b/Minecraft.World/Layer.h new file mode 100644 index 00000000..a444a629 --- /dev/null +++ b/Minecraft.World/Layer.h @@ -0,0 +1,36 @@ +#pragma once + +#include "ArrayWithLength.h" + +class LevelType; + +#ifndef _CONTENT_PACAKGE +#define _BIOME_OVERRIDE +#endif + +class Layer +{ +private: + __int64 seed; + +protected: + shared_ptrparent; + +private: + __int64 rval; + __int64 seedMixup; + +public: + static LayerArray getDefaultLayers(__int64 seed, LevelType *levelType); + + Layer(__int64 seedMixup); + + virtual void init(__int64 seed); + virtual void initRandom(__int64 x, __int64 y); + +protected: + int nextRandom(int max); + +public: + virtual intArray getArea(int xo, int yo, int w, int h) = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/LeafTile.cpp b/Minecraft.World/LeafTile.cpp new file mode 100644 index 00000000..8b7d381c --- /dev/null +++ b/Minecraft.World/LeafTile.cpp @@ -0,0 +1,337 @@ +#include "stdafx.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "LeafTile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.h" + +const unsigned int LeafTile::LEAF_NAMES[LEAF_NAMES_LENGTH] = { IDS_TILE_LEAVES_OAK, + IDS_TILE_LEAVES_SPRUCE, + IDS_TILE_LEAVES_BIRCH, + IDS_TILE_LEAVES_JUNGLE, + }; + +const wstring LeafTile::TEXTURES[2][4] = { {L"leaves", L"leaves_spruce", L"leaves", L"leaves_jungle"}, {L"leaves_opaque", L"leaves_spruce_opaque", L"leaves_opaque", L"leaves_jungle_opaque"},}; + +LeafTile::LeafTile(int id) : TransparentTile(id, Material::leaves, false, isSolidRender()) +{ + checkBuffer = NULL; + fancyTextureSet = 0; + setTicking(true); +} + +LeafTile::~LeafTile() +{ + delete [] checkBuffer; +} + +int LeafTile::getColor() const +{ + // 4J Stu - Not using this any more + //double temp = 0.5; + //double rain = 1.0; + + //return FoliageColor::get(temp, rain); + + return Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Foliage_Common ); +} + +int LeafTile::getColor(int data) +{ + if ((data & LEAF_TYPE_MASK) == EVERGREEN_LEAF) + { + return FoliageColor::getEvergreenColor(); + } + if ((data & LEAF_TYPE_MASK) == BIRCH_LEAF) + { + return FoliageColor::getBirchColor(); + } + + return FoliageColor::getDefaultColor(); +} + +int LeafTile::getColor(LevelSource *level, int x, int y, int z) +{ + return getColor(level, x, y, z, level->getData(x, y, z) ); +} + +// 4J - changed interface to have data passed in, and put existing interface as wrapper above +int LeafTile::getColor(LevelSource *level, int x, int y, int z, int data) +{ + if ((data & LEAF_TYPE_MASK) == EVERGREEN_LEAF) + { + return FoliageColor::getEvergreenColor(); + } + if ((data & LEAF_TYPE_MASK) == BIRCH_LEAF) + { + return FoliageColor::getBirchColor(); + } + + int totalRed = 0; + int totalGreen = 0; + int totalBlue = 0; + + for (int oz = -1; oz <= 1; oz++) + { + for (int ox = -1; ox <= 1; ox++) + { + int foliageColor = level->getBiome(x + ox, z + oz)->getFolageColor(); + + totalRed += (foliageColor & 0xff0000) >> 16; + totalGreen += (foliageColor & 0xff00) >> 8; + totalBlue += (foliageColor & 0xff); + } + } + + return (((totalRed / 9) & 0xFF) << 16) | (((totalGreen / 9) & 0xFF) << 8) | (((totalBlue / 9) & 0xFF)); +} + +void LeafTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + int r = 1; + int r2 = r + 1; + + if (level->hasChunksAt(x - r2, y - r2, z - r2, x + r2, y + r2, z + r2)) + { + for (int xo = -r; xo <= r; xo++) + for (int yo = -r; yo <= r; yo++) + for (int zo = -r; zo <= r; zo++) + { + int t = level->getTile(x + xo, y + yo, z + zo); + if (t == Tile::leaves_Id) + { + int currentData = level->getData(x + xo, y + yo, z + zo); + level->setDataNoUpdate(x + xo, y + yo, z + zo, currentData | UPDATE_LEAF_BIT); + } + } + } + +} + +void LeafTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (level->isClientSide) return; + + int currentData = level->getData(x, y, z); + if ((currentData & UPDATE_LEAF_BIT) != 0 && (currentData & PERSISTENT_LEAF_BIT) == 0) + { + int r = LeafTile::REQUIRED_WOOD_RANGE; + int r2 = r + 1; + + int W = 32; + int WW = W * W; + int WO = W / 2; + if (checkBuffer == NULL) + { + checkBuffer = new int[W * W * W]; + } + + if (level->hasChunksAt(x - r2, y - r2, z - r2, x + r2, y + r2, z + r2)) + { + // 4J Stu - Assuming we remain in the same chunk, getTile accesses an array that varies least by y + // Changing the ordering here to loop by y last + for (int xo = -r; xo <= r; xo++) + for (int zo = -r; zo <= r; zo++) + for (int yo = -r; yo <= r; yo++) + { + int t = level->getTile(x + xo, y + yo, z + zo); + if (t == Tile::treeTrunk_Id) + { + checkBuffer[(xo + WO) * WW + (yo + WO) * W + (zo + WO)] = 0; + } + else if (t == Tile::leaves_Id) + { + checkBuffer[(xo + WO) * WW + (yo + WO) * W + (zo + WO)] = -2; + } + else + { + checkBuffer[(xo + WO) * WW + (yo + WO) * W + (zo + WO)] = -1; + } + } + for (int i = 1; i <= LeafTile::REQUIRED_WOOD_RANGE; i++) + { + for (int xo = -r; xo <= r; xo++) + for (int yo = -r; yo <= r; yo++) + for (int zo = -r; zo <= r; zo++) + { + if (checkBuffer[(xo + WO) * WW + (yo + WO) * W + (zo + WO)] == i - 1) + { + if (checkBuffer[(xo + WO - 1) * WW + (yo + WO) * W + (zo + WO)] == -2) + { + checkBuffer[(xo + WO - 1) * WW + (yo + WO) * W + (zo + WO)] = i; + } + if (checkBuffer[(xo + WO + 1) * WW + (yo + WO) * W + (zo + WO)] == -2) + { + checkBuffer[(xo + WO + 1) * WW + (yo + WO) * W + (zo + WO)] = i; + } + if (checkBuffer[(xo + WO) * WW + (yo + WO - 1) * W + (zo + WO)] == -2) + { + checkBuffer[(xo + WO) * WW + (yo + WO - 1) * W + (zo + WO)] = i; + } + if (checkBuffer[(xo + WO) * WW + (yo + WO + 1) * W + (zo + WO)] == -2) + { + checkBuffer[(xo + WO) * WW + (yo + WO + 1) * W + (zo + WO)] = i; + } + if (checkBuffer[(xo + WO) * WW + (yo + WO) * W + (zo + WO - 1)] == -2) + { + checkBuffer[(xo + WO) * WW + (yo + WO) * W + (zo + WO - 1)] = i; + } + if (checkBuffer[(xo + WO) * WW + (yo + WO) * W + (zo + WO + 1)] == -2) + { + checkBuffer[(xo + WO) * WW + (yo + WO) * W + (zo + WO + 1)] = i; + } + } + } + } + } + + int mid = checkBuffer[(WO) * WW + (WO) * W + (WO)]; + if (mid >= 0) + { + level->setDataNoUpdate(x, y, z, currentData & ~UPDATE_LEAF_BIT); + } + else + { + die(level, x, y, z); + } + } + +} + +void LeafTile::animateTick(Level *level, int x, int y, int z, Random *random) +{ + if (level->isRainingAt(x, y + 1, z) && !level->isTopSolidBlocking(x, y - 1, z) && random->nextInt(15) == 1) + { + double xx = x + random->nextFloat(); + double yy = y - 0.05; + double zz = z + random->nextFloat(); + + level->addParticle(eParticleType_dripWater, xx, yy, zz, 0, 0, 0); + } +} + +void LeafTile::die(Level *level, int x, int y, int z) +{ + Tile::spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); +} + +int LeafTile::getResourceCount(Random *random) +{ + return random->nextInt(20) == 0 ? 1 : 0; +} + +int LeafTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::sapling_Id; +} + +// 4J DCR: Brought forward from 1.2 +void LeafTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel) +{ + if (!level->isClientSide) + { + int chance = 20; + if ((data & LEAF_TYPE_MASK) == JUNGLE_LEAF) + { + chance = 40; + } + if (level->random->nextInt(chance) == 0) + { + int type = getResource(data, level->random,playerBonusLevel); + popResource(level, x, y, z, shared_ptr( new ItemInstance(type, 1, getSpawnResourcesAuxValue(data)))); + } + + if ((data & LEAF_TYPE_MASK) == NORMAL_LEAF && level->random->nextInt(200) == 0) + { + popResource(level, x, y, z, shared_ptr(new ItemInstance(Item::apple_Id, 1, 0))); + } + } +} + +void LeafTile::playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data) +{ + if (!level->isClientSide && player->getSelectedItem() != NULL && player->getSelectedItem()->id == Item::shears->id) + { + player->awardStat( + GenericStats::blocksMined(id), + GenericStats::param_blocksMined(id,data,1) + ); + + // drop leaf block instead of sapling + popResource(level, x, y, z, shared_ptr(new ItemInstance(Tile::leaves_Id, 1, data & LEAF_TYPE_MASK))); + } + else + { + TransparentTile::playerDestroy(level, player, x, y, z, data); + } +} + +int LeafTile::getSpawnResourcesAuxValue(int data) +{ + return data & LEAF_TYPE_MASK; +} + +bool LeafTile::isSolidRender(bool isServerLevel) +{ + // 4J Stu - The server level shouldn't care how the tile is rendered! + // Fix for #9407 - Gameplay: Destroying a block of snow on top of trees, removes any adjacent snow. + if(isServerLevel) return true; + return !allowSame; +} + +Icon *LeafTile::getTexture(int face, int data) +{ + if ((data & LEAF_TYPE_MASK) == EVERGREEN_LEAF) + { + return icons[fancyTextureSet][EVERGREEN_LEAF]; + } + if ((data & LEAF_TYPE_MASK) == JUNGLE_LEAF) + { + return icons[fancyTextureSet][JUNGLE_LEAF]; + } + return icons[fancyTextureSet][0]; +} + +void LeafTile::setFancy(bool fancyGraphics) +{ + allowSame = fancyGraphics; + fancyTextureSet = (fancyGraphics ? 0 : 1); +} + +shared_ptr LeafTile::getSilkTouchItemInstance(int data) +{ + return shared_ptr( new ItemInstance(id, 1, data & LEAF_TYPE_MASK) ); +} + +void LeafTile::stepOn(Level *level, int x, int y, int z, shared_ptr entity) +{ + TransparentTile::stepOn(level, x, y, z, entity); +} + +bool LeafTile::shouldTileTick(Level *level, int x,int y,int z) +{ + int currentData = level->getData(x, y, z); + return (currentData & UPDATE_LEAF_BIT) != 0; +} + +unsigned int LeafTile::getDescriptionId(int iData /*= -1*/) +{ + int leafIndex = iData & LEAF_TYPE_MASK; + return LeafTile::LEAF_NAMES[leafIndex]; +} + +void LeafTile::registerIcons(IconRegister *iconRegister) +{ + for (int fancy = 0; fancy < 2; fancy++) + { + //icons[fancy] = new Icon[TEXTURES[fancy].length]; + + for (int i = 0; i < 4; i++) + { + icons[fancy][i] = iconRegister->registerIcon(TEXTURES[fancy][i]); + } + } +} diff --git a/Minecraft.World/LeafTile.h b/Minecraft.World/LeafTile.h new file mode 100644 index 00000000..e6c39a7f --- /dev/null +++ b/Minecraft.World/LeafTile.h @@ -0,0 +1,76 @@ +#pragma once +#include "TransparentTile.h" + +class Random; +class ChunkRebuildData; + +class LeafTile : public TransparentTile +{ + friend class Tile; + friend class ChunkRebuildData; +public: + static const wstring TEXTURES[2][4]; + static const int REQUIRED_WOOD_RANGE = 4; + + static const int UPDATE_LEAF_BIT = 8; + static const int PERSISTENT_LEAF_BIT = 4; // player-placed + static const int NORMAL_LEAF = 0; + static const int EVERGREEN_LEAF = 1; + static const int BIRCH_LEAF = 2; + static const int JUNGLE_LEAF = 3; + + static const int LEAF_NAMES_LENGTH = 4; + + static const unsigned int LEAF_NAMES[LEAF_NAMES_LENGTH]; +private: + static const int LEAF_TYPE_MASK = 3; + + // pppppppppp ppppppppppp pppppppppp ppppppp + // ssssssssss sssssssssss s + + int fancyTextureSet; + Icon *icons[2][4]; + +protected: + LeafTile(int id); + virtual ~LeafTile(); +public: + virtual int getColor() const; + virtual int getColor(int data); + + virtual int getColor(LevelSource *level, int x, int y, int z); + virtual int getColor(LevelSource *level, int x, int y, int z, int data); // 4J added + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); + int *checkBuffer; + + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual void animateTick(Level *level, int x, int y, int z, Random *random); +private: + void die(Level *level, int x, int y, int z); +public: + virtual int getResourceCount(Random *random); + virtual int getResource(int data, Random *random, int playerBonusLevel); + + // 4J DCR: Brought forward from 1.2 + virtual void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel); + + virtual void playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data); +protected: + virtual int getSpawnResourcesAuxValue(int data); +public: + virtual bool isSolidRender(bool isServerLevel = false); + virtual Icon *getTexture(int face, int data); + void setFancy(bool fancyGraphics); + +protected: + virtual shared_ptr getSilkTouchItemInstance(int data); + +public: + virtual void stepOn(Level *level, int x, int y, int z, shared_ptr entity); + + // 4J Added so we can check before we try to add a tile to the tick list if it's actually going to do seomthing + virtual bool shouldTileTick(Level *level, int x,int y,int z); + + virtual unsigned int getDescriptionId(int iData = -1); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/LeafTileItem.cpp b/Minecraft.World/LeafTileItem.cpp new file mode 100644 index 00000000..40036ca0 --- /dev/null +++ b/Minecraft.World/LeafTileItem.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "LeafTileItem.h" +#include "FoliageColor.h" + +LeafTileItem::LeafTileItem(int id) : TileItem(id) +{ + setMaxDamage(0); + setStackedByData(true); +} + +int LeafTileItem::getLevelDataForAuxValue(int auxValue) +{ + return auxValue | LeafTile::PERSISTENT_LEAF_BIT; + +} + +Icon *LeafTileItem::getIcon(int itemAuxValue) +{ + return Tile::leaves->getTexture(0, itemAuxValue); +} + + +int LeafTileItem::getColor(shared_ptr item, int spriteLayer) +{ + int data = item->getAuxValue(); + if ((data & LeafTile::EVERGREEN_LEAF) == LeafTile::EVERGREEN_LEAF) + { + return FoliageColor::getEvergreenColor(); + } + if ((data & LeafTile::BIRCH_LEAF) == LeafTile::BIRCH_LEAF) + { + return FoliageColor::getBirchColor(); + } + return FoliageColor::getDefaultColor(); +} + +unsigned int LeafTileItem::getDescriptionId(shared_ptr instance) +{ + int auxValue = instance->getAuxValue(); + if (auxValue < 0 || auxValue >= LeafTile::LEAF_NAMES_LENGTH) + { + auxValue = 0; + } + return LeafTile::LEAF_NAMES[auxValue]; +} \ No newline at end of file diff --git a/Minecraft.World/LeafTileItem.h b/Minecraft.World/LeafTileItem.h new file mode 100644 index 00000000..cd311590 --- /dev/null +++ b/Minecraft.World/LeafTileItem.h @@ -0,0 +1,17 @@ +#pragma once +using namespace std; + +#include "TileItem.h" + +class LeafTileItem : public TileItem +{ + using TileItem::getColor; +public: + LeafTileItem(int id); + + virtual int getLevelDataForAuxValue(int auxValue); + virtual Icon *getIcon(int itemAuxValue); + virtual int getColor(shared_ptr item, int spriteLayer); + + virtual unsigned int getDescriptionId(shared_ptr instance); +}; diff --git a/Minecraft.World/LeapAtTargetGoal.cpp b/Minecraft.World/LeapAtTargetGoal.cpp new file mode 100644 index 00000000..ea4a1c0e --- /dev/null +++ b/Minecraft.World/LeapAtTargetGoal.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.h" +#include "LeapAtTargetGoal.h" + +LeapAtTargetGoal::LeapAtTargetGoal(Mob *mob, float yd) +{ + target = weak_ptr(); + + this->mob = mob; + this->yd = yd; + setRequiredControlFlags(Control::JumpControlFlag | Control::MoveControlFlag); +} + +bool LeapAtTargetGoal::canUse() +{ + target = weak_ptr(mob->getTarget()); + if (target.lock() == NULL) return false; + double d = mob->distanceToSqr(target.lock()); + if (d < 2 * 2 || d > 4 * 4) return false; + if (!mob->onGround) return false; + if (mob->getRandom()->nextInt(5) != 0) return false; + return true; +} + +bool LeapAtTargetGoal::canContinueToUse() +{ + return target.lock() != NULL && !mob->onGround; +} + +void LeapAtTargetGoal::start() +{ + // TODO: move to control? + double xdd = target.lock()->x - mob->x; + double zdd = target.lock()->z - mob->z; + float dd = sqrt(xdd * xdd + zdd * zdd); + mob->xd += (xdd / dd * 0.5f) * 0.8f + mob->xd * 0.2f; + mob->zd += (zdd / dd * 0.5f) * 0.8f + mob->zd * 0.2f; + mob->yd = yd; +} \ No newline at end of file diff --git a/Minecraft.World/LeapAtTargetGoal.h b/Minecraft.World/LeapAtTargetGoal.h new file mode 100644 index 00000000..6495e131 --- /dev/null +++ b/Minecraft.World/LeapAtTargetGoal.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Goal.h" + +class LeapAtTargetGoal : public Goal +{ +private: + Mob *mob; // Owner of this goal + weak_ptr target; + float yd; + +public: + LeapAtTargetGoal(Mob *mob, float yd); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); +}; \ No newline at end of file diff --git a/Minecraft.World/Level.cpp b/Minecraft.World/Level.cpp new file mode 100644 index 00000000..4b6a64f5 --- /dev/null +++ b/Minecraft.World/Level.cpp @@ -0,0 +1,4754 @@ +#include "stdafx.h" +#include "System.h" +#include "BasicTypeContainers.h" +#include "File.h" +#include "ProgressListener.h" +#include "net.minecraft.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.entity.ai.village.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.global.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.chunk.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.levelgen.h" +#include "net.minecraft.world.level.storage.h" +#include "net.minecraft.world.level.pathfinder.h" +#include "net.minecraft.world.phys.h" +#include "ChunkPos.h" +#include "Explosion.h" +#include "LevelListener.h" +#include "LightLayer.h" +#include "MobSpawner.h" +#include "Region.h" +#include "TickNextTickData.h" +#include "Level.h" +#include "ThreadName.h" +#include "WeighedRandom.h" + +#include "ConsoleSaveFile.h" +#include +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\LevelRenderer.h" +#include "SoundTypes.h" +#include "SparseLightStorage.h" +#include "..\Minecraft.Client\Textures.h" +#include "..\Minecraft.Client\TexturePackRepository.h" +#include "..\Minecraft.Client\DLCTexturePack.h" +#include "..\Minecraft.Client\Common\DLC\DLCPack.h" +#include "..\Minecraft.Client\PS3\PS3Extras\ShutdownManager.h" + + +DWORD Level::tlsIdx = TlsAlloc(); +DWORD Level::tlsIdxLightCache = TlsAlloc(); + +// 4J : WESTY : Added for time played stats. +#include "net.minecraft.stats.h" + +// 4J - Caching of lighting data added. This is implemented as a 16x16x16 cache of ints (ie 16K storage in total). The index of the element to be used in the array is determined by the lower +// four bits of each x/y/z position, and the upper 7/4/7 bits of the x/y/z positions are stored within the element itself along with the cached values etc. The cache can be enabled per thread by +// calling enableLightingCache, otherwise standard non-cached accesses are performed. General method for using caching if enabled on a thread is: +// (1) Call initCache, this invalidates any previous data in the cache +// (2) Use setBrightnessCached, getBrightnessCached, getEmissionCached, getBlockingCached methods to get and set data +// (3) Call flushCache, which writes through any dirty values in cache + +#ifdef _LARGE_WORLDS +// Packing for cache entries in large worlds is as follows ( 64 bits per entry) +// Add the extra x and z data into the top 32 bits, to keep all the masks and code for everything else the same +// xxxxxxxxxxxxxxxxzzzzzzzzzzzzzzzzWEBLllllbbbbeeeexxxxxxyyyyzzzzzz +// +// xxxxxx - middle 6 bits of x position +// yyyy - top 4 bits of y position +// zzzzzz - middle 6 bits of z position +// eeee - light emission +// bbbb - light blocking +// llll - light level +// L - light value valid +// B - blocking value valid +// E - emission value valid +// W - lighting value requires write +// xxxxxxxxxxxxxxxx - top 16 bits of x position +// zzzzzzzzzzzzzzzz - top 16 bits of z position +#else +// Packing for cache entries is as follows ( 32 bits per entry) +// WEBLllllbbbbeeeexxxxxxyyyyzzzzzz +// +// xxxxxx - top 6 bits of x position +// yyyy - top 4 bits of y position +// zzzzzz - top 6 bits of z position +// eeee - light emission +// bbbb - light blocking +// llll - light level +// L - light value valid +// B - blocking value valid +// E - emission value valid +// W - lighting value requires write +#endif + + +void Level::enableLightingCache() +{ + // Allocate 16K (needs 32K for large worlds) for a 16x16x16x4 byte cache of results, plus 128K required for toCheck array. Rounding up to 256 to keep as multiple of alignement - aligning to 128K boundary for possible cache locking. + void *cache = (unsigned char *)XPhysicalAlloc(256 * 1024, MAXULONG_PTR, 128 * 1024, PAGE_READWRITE | MEM_LARGE_PAGES); + TlsSetValue(tlsIdxLightCache,cache); +} + +void Level::destroyLightingCache() +{ + lightCache_t *cache = (lightCache_t *)TlsGetValue(tlsIdxLightCache); + XPhysicalFree(cache); +} + +void Level::initCache(lightCache_t *cache) +{ + cachewritten = false; + if( cache == NULL ) return; + + XMemSet128(cache,0,16*16*16*sizeof(lightCache_t)); +} + +// Set a brightness value, going through the cache if enabled for this thread +void inline Level::setBrightnessCached(lightCache_t *cache, __uint64 *cacheUse, LightLayer::variety layer, int x, int y, int z, int brightness) +{ + if( cache == NULL ) + { + setBrightness(layer, x, y, z, brightness, true); + return; + } + if( y & 0xffffff00 ) return; // Eliminate -ve ys and values > 255 + + int idx = ( ( x & 15 ) << 8 ) | + ( ( y & 15 ) << 4 ) | + ( z & 15 ); + lightCache_t posbits = ( ( x & 0x3f0 ) << 6 ) | + ( ( y & 0x0f0 ) << 2 ) | + ( ( z & 0x3f0 ) >> 4 ); +#ifdef _LARGE_WORLDS + // Add in the higher bits for x and z + posbits |= ( ( ((__uint64)x) & 0x3FFFC00L) << 38) | + ( ( ((__uint64)z) & 0x3FFFC00L) << 22); +#endif + + lightCache_t cacheValue = cache[idx]; + + // If this cache entry doesn't refer to the same thing... + if( ( cacheValue & POSITION_MASK ) != posbits ) + { + /// and it has been written to... + if( cacheValue & LIGHTING_WRITEBACK ) + { + // Then we need to flush + int val = ( cacheValue >> LIGHTING_SHIFT ) & 15; + int xx = ( (cacheValue >> 6 ) & 0x3f0 ) | ( x & 15 ); +#ifdef _LARGE_WORLDS + xx |= ( (cacheValue >> 38) & 0x3FFFC00); + xx = ( xx << 6 ) >> 6; // sign extend +#else + xx = ( xx << 22 ) >> 22; // sign extend +#endif + int yy = ( (cacheValue >> 2 ) & 0x0f0 ) | ( y & 15 ); + int zz = ( (cacheValue << 4 ) & 0x3f0 ) | ( z & 15 ); +#ifdef _LARGE_WORLDS + zz |= ( (cacheValue >> 22) & 0x3FFFC00); + zz = ( zz << 6 ) >> 6; // sign extend +#else + zz = ( zz << 22 ) >> 22; // sign extend +#endif + setBrightness(layer, xx, yy, zz, val, true); + } + cacheValue = posbits; + } + + // Just written to it, so value is valid & requires writing back + cacheValue &= ~(15 << LIGHTING_SHIFT ); + cacheValue |= brightness << LIGHTING_SHIFT; + cacheValue |= ( LIGHTING_WRITEBACK | LIGHTING_VALID ); + + // cacheUse has a single bit for each x, y and z to say whether anything with that x, y or z has been written to + (*cacheUse) |= ( ( 1LL << ( x & 15 ) ) | ( 0x10000LL << ( y & 15 ) ) | ( 0x100000000LL << ( z & 15 ) ) ); + + cache[idx] = cacheValue; +} + +// Get a brightness value, going through the cache if enabled for this thread +inline int Level::getBrightnessCached(lightCache_t *cache, LightLayer::variety layer, int x, int y, int z) +{ + if( cache == NULL ) return getBrightness(layer, x, y, z); + if( y & 0xffffff00 ) return getBrightness(layer, x, y, z); // Fall back on original method for out-of-bounds y + + int idx = ( ( x & 15 ) << 8 ) | + ( ( y & 15 ) << 4 ) | + ( z & 15 ); + lightCache_t posbits = ( ( x & 0x3f0 ) << 6 ) | + ( ( y & 0x0f0 ) << 2 ) | + ( ( z & 0x3f0 ) >> 4 ); +#ifdef _LARGE_WORLDS + // Add in the higher bits for x and z + posbits |= ( ( ((__uint64)x) & 0x3FFFC00L) << 38) | + ( ( ((__uint64)z) & 0x3FFFC00L) << 22); +#endif + + lightCache_t cacheValue = cache[idx]; + + if( ( cacheValue & POSITION_MASK ) != posbits ) + { + // Position differs - need to evict this cache entry + if( cacheValue & LIGHTING_WRITEBACK ) + { + // Then we need to flush + int val = ( cacheValue >> LIGHTING_SHIFT ) & 15; + int xx = ( (cacheValue >> 6 ) & 0x3f0 ) | ( x & 15 ); +#ifdef _LARGE_WORLDS + xx |= ( (cacheValue >> 38) & 0x3FFFC00); + xx = ( xx << 6 ) >> 6; // sign extend +#else + xx = ( xx << 22 ) >> 22; // sign extend +#endif + int yy = ( (cacheValue >> 2 ) & 0x0f0 ) | ( y & 15 ); + int zz = ( (cacheValue << 4 ) & 0x3f0 ) | ( z & 15 ); +#ifdef _LARGE_WORLDS + zz |= ( (cacheValue >> 22) & 0x3FFFC00); + zz = ( zz << 6 ) >> 6; // sign extend +#else + zz = ( zz << 22 ) >> 22; // sign extend +#endif + setBrightness(layer, xx, yy, zz, val, true); + } + cacheValue = posbits | LIGHTING_VALID; + int val = getBrightness(layer, x, y, z); + cacheValue |= val << LIGHTING_SHIFT; + } + else + { + // The position matches - will incurr a read miss if the lighting value isn't valid + if( ( cacheValue & LIGHTING_VALID ) == 0 ) + { + int val = getBrightness(layer, x, y, z); + cacheValue |= val << LIGHTING_SHIFT; + cacheValue |= LIGHTING_VALID; + } + else + { + // All valid - just return value + return ( cacheValue >> LIGHTING_SHIFT ) & 15; + } + } + + cache[idx] = cacheValue; + return ( cacheValue >> LIGHTING_SHIFT ) & 15; +} + +// Get a block emission value, going through the cache if enabled for this thread +inline int Level::getEmissionCached(lightCache_t *cache, int ct, int x, int y, int z) +{ + if( cache == NULL ) return Tile::lightEmission[ct]; + + int idx = ( ( x & 15 ) << 8 ) | + ( ( y & 15 ) << 4 ) | + ( z & 15 ); + lightCache_t posbits = ( ( x & 0x3f0 ) << 6 ) | + ( ( y & 0x0f0 ) << 2 ) | + ( ( z & 0x3f0 ) >> 4 ); +#ifdef _LARGE_WORLDS + // Add in the higher bits for x and z + posbits |= ( ( ((__uint64)x) & 0x3FFFC00) << 38) | + ( ( ((__uint64)z) & 0x3FFFC00) << 22); +#endif + + lightCache_t cacheValue = cache[idx]; + + if( ( cacheValue & POSITION_MASK ) != posbits ) + { + // Position differs - need to evict this cache entry + if( cacheValue & LIGHTING_WRITEBACK ) + { + // Then we need to flush + int val = ( cacheValue >> LIGHTING_SHIFT ) & 15; + int xx = ( (cacheValue >> 6 ) & 0x3f0 ) | ( x & 15 ); +#ifdef _LARGE_WORLDS + xx |= ( (cacheValue >> 38) & 0x3FFFC00); + xx = ( xx << 6 ) >> 6; // sign extend +#else + xx = ( xx << 22 ) >> 22; // sign extend +#endif + int yy = ( (cacheValue >> 2 ) & 0x0f0 ) | ( y & 15 ); + int zz = ( (cacheValue << 4 ) & 0x3f0 ) | ( z & 15 ); +#ifdef _LARGE_WORLDS + zz |= ( (cacheValue >> 22) & 0x3FFFC00); + zz = ( zz << 6 ) >> 6; // sign extend +#else + zz = ( zz << 22 ) >> 22; // sign extend +#endif + setBrightness(LightLayer::Block, xx, yy, zz, val, true); + } + + // Update both emission & blocking values whilst we are here + cacheValue = posbits | EMISSION_VALID | BLOCKING_VALID; + int t = getTile(x,y,z); + cacheValue |= ( Tile::lightEmission[t] & 15 ) << EMISSION_SHIFT; + cacheValue |= ( Tile::lightBlock[t] & 15 ) << BLOCKING_SHIFT; + } + else + { + // The position matches - will incurr a read miss if the lighting value isn't valid + if( ( cacheValue & EMISSION_VALID ) == 0 ) + { + // Update both emission & blocking values whilst we are here + cacheValue |= EMISSION_VALID | BLOCKING_VALID; + int t = getTile(x,y,z); + cacheValue |= ( Tile::lightEmission[t] & 15 ) << EMISSION_SHIFT; + cacheValue |= ( Tile::lightBlock[t] & 15 ) << BLOCKING_SHIFT; + } + else + { + // All valid - just return value + return ( cacheValue >> EMISSION_SHIFT ) & 15; + } + } + cache[idx] = cacheValue; + return ( cacheValue >> EMISSION_SHIFT ) & 15; +} + +// Get a tile light blocking value, going through cache if enabled for this thread +inline int Level::getBlockingCached(lightCache_t *cache, LightLayer::variety layer, int *ct, int x, int y, int z) +{ + if( cache == NULL ) + { + int t = getTile(x,y,z); + if(ct) *ct = t; + return Tile::lightBlock[t]; + } + + int idx = ( ( x & 15 ) << 8 ) | + ( ( y & 15 ) << 4 ) | + ( z & 15 ); + lightCache_t posbits = ( ( x & 0x3f0 ) << 6 ) | + ( ( y & 0x0f0 ) << 2 ) | + ( ( z & 0x3f0 ) >> 4 ); +#ifdef _LARGE_WORLDS + // Add in the higher bits for x and z + posbits |= ( ( ((__uint64)x) & 0x3FFFC00L) << 38) | + ( ( ((__uint64)z) & 0x3FFFC00L) << 22); +#endif + + lightCache_t cacheValue = cache[idx]; + + if( ( cacheValue & POSITION_MASK ) != posbits ) + { + // Position differs - need to evict this cache entry + if( cacheValue & LIGHTING_WRITEBACK ) + { + // Then we need to flush + int val = ( cacheValue >> LIGHTING_SHIFT ) & 15; + int xx = ( (cacheValue >> 6 ) & 0x3f0 ) | ( x & 15 ); +#ifdef _LARGE_WORLDS + xx |= ( (cacheValue >> 38) & 0x3FFFC00); + xx = ( xx << 6 ) >> 6; // sign extend +#else + xx = ( xx << 22 ) >> 22; // sign extend +#endif + int yy = ( (cacheValue >> 2 ) & 0x0f0 ) | ( y & 15 ); + int zz = ( (cacheValue << 4 ) & 0x3f0 ) | ( z & 15 ); +#ifdef _LARGE_WORLDS + zz |= ( (cacheValue >> 22) & 0x3FFFC00); + zz = ( zz << 6 ) >> 6; // sign extend +#else + zz = ( zz << 22 ) >> 22; // sign extend +#endif + setBrightness(layer, xx, yy, zz, val, true); + } + + // Update both emission & blocking values whilst we are here + cacheValue = posbits | EMISSION_VALID | BLOCKING_VALID; + int t = getTile(x,y,z); + cacheValue |= ( Tile::lightEmission[t] & 15 ) << EMISSION_SHIFT; + cacheValue |= ( Tile::lightBlock[t] & 15 ) << BLOCKING_SHIFT; + } + else + { + // The position matches - will incurr a read miss if the lighting value isn't valid + if( ( cacheValue & EMISSION_VALID ) == 0 ) + { + // Update both emission & blocking values whilst we are here + cacheValue |= EMISSION_VALID | BLOCKING_VALID; + int t = getTile(x,y,z); + cacheValue |= ( Tile::lightEmission[t] & 15 ) << EMISSION_SHIFT; + cacheValue |= ( Tile::lightBlock[t] & 15 ) << BLOCKING_SHIFT; + } + else + { + // All valid - just return value + return ( cacheValue >> BLOCKING_SHIFT ) & 15; + } + } + + cache[idx] = cacheValue; + return ( cacheValue >> BLOCKING_SHIFT ) & 15; +} + +// Write back any dirty entries in the lighting cache. Also calls the setTilesDirty method on the region which has been updated during this lighting update, since +// this hasn't been updated (for client threads) for each individual lighting update as would have been the case with the non-cached lighting. There's two reasons for this +// (1) it's more efficient, since we aren't doing so many individual calls to the level listener to let the renderer know what has been updated +// (2) it lets the lighting actually complete before we get any visual representation of the update, otherwise we end up seeing some strange partial updates +void Level::flushCache(lightCache_t *cache, __uint64 cacheUse, LightLayer::variety layer) +{ + // cacheUse has a single bit for each x, y and z to say whether anything with that x, y or z has been written to + if( cacheUse == 0 ) return; + if( cache ) + { + lightCache_t *pcache = cache; + for( int x = 0; x < 16; x++ ) + { + if( ( cacheUse & ( 1LL << x ) ) == 0 ) + { + pcache += 16 * 16; + continue; + } + for( int y = 0; y < 16; y++ ) + { + if( ( cacheUse & ( 0x10000LL << y ) ) == 0 ) + { + pcache += 16; + continue; + } + for( int z = 0; z < 16; z++ ) + { + if( ( cacheUse & ( 0x100000000LL << z ) ) == 0 ) + { + pcache++; + continue; + } + lightCache_t cacheValue = *pcache++; + if( cacheValue & LIGHTING_WRITEBACK ) + { + int val = ( cacheValue >> LIGHTING_SHIFT ) & 15; + int xx = ( (cacheValue >> 6 ) & 0x3f0 ) | ( x & 15 ); +#ifdef _LARGE_WORLDS + xx |= ( (cacheValue >> 38) & 0x3FFFC00); + xx = ( xx << 6 ) >> 6; // sign extend +#else + xx = ( xx << 22 ) >> 22; // sign extend +#endif + int yy = ( (cacheValue >> 2 ) & 0x0f0 ) | ( y & 15 ); + int zz = ( (cacheValue << 4 ) & 0x3f0 ) | ( z & 15 ); +#ifdef _LARGE_WORLDS + zz |= ( (cacheValue >> 22) & 0x3FFFC00); + zz = ( zz << 6 ) >> 6; // sign extend +#else + zz = ( zz << 22 ) >> 22; // sign extend +#endif + setBrightness(layer, xx, yy, zz, val, true); + } + } + } + } + } + // For client side (which has the renderer attached) we haven't been updating with each individual update, but have been gathering them up. + // Let the renderer know now the region that has been updated. + if( isClientSide && cachewritten) + { + setTilesDirty(cacheminx, cacheminy, cacheminz,cachemaxx,cachemaxy,cachemaxz); + } +} + +// 4J - added following 2 functions to move instaBuild flag from being a class member, to TLS +bool Level::getInstaTick() +{ + return ((size_t)TlsGetValue(tlsIdx)) != 0; +} + +void Level::setInstaTick(bool enable) +{ + void *value = 0; + if( enable ) value = (void *)1; + TlsSetValue(tlsIdx,value); +} + +// 4J - added +bool Level::hasEntitiesToRemove() +{ + return !entitiesToRemove.empty(); +} + +void Level::_init() +{ + cloudColor = 0xffffff; + + skyDarken = 0; + + randValue = (new Random())->nextInt(); + + addend = 1013904223; + + oRainLevel = rainLevel = 0.0f; + + oThunderLevel = thunderLevel = 0.0f; + + lightningTime = 0; + + lightningBoltTime = 0; + + noNeighborUpdate = false; + + difficulty = 0; + + random = new Random(); + isNew = false; + + dimension = NULL; + + chunkSource = NULL; + + levelStorage = nullptr; + + levelData = NULL; + + isFindingSpawn = false; + + savedDataStorage = NULL; + + spawnEnemies = true; + + spawnFriendlies = true; + + delayUntilNextMoodSound = random->nextInt(20 * 60 * 10); + + isClientSide = false; + + InitializeCriticalSection(&m_entitiesCS); + InitializeCriticalSection(&m_tileEntityListCS); + + m_timeOfDayOverride = -1; + + updatingTileEntities = false; + + villageSiege = new VillageSiege(this); + + toCheckLevel = new int[ 32 * 32 * 32]; // 4J - brought forward from 1.8.2 + InitializeCriticalSectionAndSpinCount(&m_checkLightCS, 5120); // 4J - added for 1.8.2 lighting + + // 4J Added + m_bDisableAddNewTileEntities = false; + m_iHighestY=-1000; + m_unsavedChunkCount = 0; +} + +// 4J - brought forward from 1.8.2 +Biome *Level::getBiome(int x, int z) +{ + if (hasChunkAt(x, 0, z)) + { + LevelChunk *lc = getChunkAt(x, z); + if (lc != NULL) + { + // Water chunks at the edge of the world return NULL for their biome as they can't store it, so should fall back on the normal method below + Biome *biome = lc->getBiome(x & 0xf, z & 0xf, dimension->biomeSource); + if( biome ) return biome; + } + } + return dimension->biomeSource->getBiome(x, z); +} + +BiomeSource *Level::getBiomeSource() +{ + return dimension->biomeSource; +} + +Level::Level(shared_ptr levelStorage, const wstring& name, Dimension *dimension, LevelSettings *levelSettings, bool doCreateChunkSource) + : seaLevel(constSeaLevel) +{ + _init(); + this->levelStorage = levelStorage;//shared_ptr(levelStorage); + this->dimension = dimension; + this->levelData = new LevelData(levelSettings, name); + if( !this->levelData->useNewSeaLevel() ) seaLevel = Level::genDepth / 2; // 4J added - sea level is one unit lower since 1.8.2, maintain older height for old levels + this->savedDataStorage = new SavedDataStorage(levelStorage.get()); + + shared_ptr savedVillages = dynamic_pointer_cast(savedDataStorage->get(typeid(Villages), Villages::VILLAGE_FILE_ID)); + if (savedVillages == NULL) + { + villages = shared_ptr(new Villages(this)); + savedDataStorage->set(Villages::VILLAGE_FILE_ID, villages); + } + else + { + villages = savedVillages; + villages->setLevel(this); + } + + dimension->init(this); + chunkSource = NULL; // 4J - added flag so chunk source can be called from derived class instead + + updateSkyBrightness(); + prepareWeather(); +} + + +Level::Level(Level *level, Dimension *dimension) + :seaLevel( constSeaLevel ) +{ + _init(); + this->levelStorage = level->levelStorage; + this->levelData = new LevelData(level->levelData); + if( !this->levelData->useNewSeaLevel() ) seaLevel = Level::genDepth / 2; // 4J added - sea level is one unit lower since 1.8.2, maintain older height for old levels + this->savedDataStorage = new SavedDataStorage( levelStorage.get() ); + + shared_ptr savedVillages = dynamic_pointer_cast(savedDataStorage->get(typeid(Villages), Villages::VILLAGE_FILE_ID)); + if (savedVillages == NULL) + { + villages = shared_ptr(new Villages(this)); + savedDataStorage->set(Villages::VILLAGE_FILE_ID, villages); + } + else + { + villages = savedVillages; + villages->setLevel(this); + } + + this->dimension = dimension; + dimension->init(this); + chunkSource = NULL; + updateSkyBrightness(); + prepareWeather(); +} + + +Level::Level(shared_ptrlevelStorage, const wstring& levelName, LevelSettings *levelSettings) + : seaLevel( constSeaLevel ) +{ + _init(levelStorage, levelName, levelSettings, NULL, true); +} + + +Level::Level(shared_ptrlevelStorage, const wstring& levelName, LevelSettings *levelSettings, Dimension *fixedDimension, bool doCreateChunkSource) + : seaLevel( constSeaLevel ) +{ + _init( levelStorage, levelName, levelSettings, fixedDimension, doCreateChunkSource ); +} + +void Level::_init(shared_ptrlevelStorage, const wstring& levelName, LevelSettings *levelSettings, Dimension *fixedDimension, bool doCreateChunkSource) +{ + _init(); + this->levelStorage = levelStorage;//shared_ptr(levelStorage); + this->savedDataStorage = new SavedDataStorage(levelStorage.get()); + + shared_ptr savedVillages = dynamic_pointer_cast(savedDataStorage->get(typeid(Villages), Villages::VILLAGE_FILE_ID)); + if (savedVillages == NULL) + { + villages = shared_ptr(new Villages(this)); + savedDataStorage->set(Villages::VILLAGE_FILE_ID, villages); + } + else + { + villages = savedVillages; + villages->setLevel(this); + } + + levelData = levelStorage->prepareLevel(); + isNew = levelData == NULL; + + if (fixedDimension != NULL) + { + dimension = fixedDimension; + } + // 4J Remove TU9 as getDimensions was never accurate. This path was never used anyway as we always set fixedDimension + //else if (levelData != NULL && levelData->getDimension() != 0) + //{ + // dimension = Dimension::getNew(levelData->getDimension()); + //} + else + { + dimension = Dimension::getNew(0); + } + + if (levelData == NULL) + { + levelData = new LevelData(levelSettings, levelName); + } + else + { + levelData->setLevelName(levelName); + } + if( !this->levelData->useNewSeaLevel() ) seaLevel = Level::genDepth / 2; // 4J added - sea level is one unit lower since 1.8.2, maintain older height for old levels + + ((Dimension *) dimension)->init( this ); + + chunkSource = doCreateChunkSource ? createChunkSource() : NULL; // 4J - added flag so chunk source can be called from derived class instead + + // 4J Stu- Moved to derived classes + //if (!levelData->isInitialized()) + //{ + // initializeLevel(levelSettings); + // levelData->setInitialized(true); + //} + + updateSkyBrightness(); + prepareWeather(); + +} + +Level::~Level() +{ + delete random; + delete dimension; + delete chunkSource; + delete levelData; + delete toCheckLevel; + + if( !isClientSide ) + { + NotGateTile::removeLevelReferences(this); // 4J added + } + + DeleteCriticalSection(&m_checkLightCS); + + // 4J-PB - savedDataStorage is shared between overworld and nether levels in the server, so it will already have been deleted on the first level delete + if(savedDataStorage!=NULL) delete savedDataStorage; + + DeleteCriticalSection(&m_entitiesCS); + DeleteCriticalSection(&m_tileEntityListCS); + + // 4J Stu - At least one of the listeners is something we cannot delete, the LevelRenderer + /* + for(int i = 0; i < listeners.size(); i++) + delete listeners[i]; + */ +} + +void Level::initializeLevel(LevelSettings *settings) +{ + levelData->setInitialized(true); +} + +void Level::validateSpawn() +{ + setSpawnPos(8, 64, 8); +} + +int Level::getTopTile(int x, int z) +{ + // 4J added - was breaking spawning as not finding ground in superflat worlds + if( levelData->getGenerator() == LevelType::lvl_flat ) + { + return Tile::grass_Id; + } + + int y = seaLevel; + while (!isEmptyTile(x, y + 1, z)) + { + y++; + } + return getTile(x, y, z); +} +int Level::getTile(int x, int y, int z) +{ + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + return 0; + } + if (y < minBuildHeight) return 0; + if (y >= maxBuildHeight) return 0; + return getChunk(x >> 4, z >> 4)->getTile(x & 15, y, z & 15); +} + +int Level::getTileLightBlock(int x, int y, int z) +{ + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + return 0; + } + if (y < minBuildHeight) return 0; + if (y >= maxBuildHeight) return 0; + return getChunk(x >> 4, z >> 4)->getTileLightBlock(x & 15, y, z & 15); +} + +bool Level::isEmptyTile(int x, int y, int z) +{ + return getTile(x, y, z) == 0; +} + +bool Level::isEntityTile(int x, int y, int z) +{ + int t = getTile(x, y, z); + if (Tile::tiles[t] != NULL && Tile::tiles[t]->isEntityTile()) + { + return true; + } + return false; +} + +int Level::getTileRenderShape(int x, int y, int z) +{ + int t = getTile(x, y, z); + if (Tile::tiles[t] != NULL) + { + return Tile::tiles[t]->getRenderShape(); + } + return Tile::SHAPE_INVISIBLE; +} + +bool Level::hasChunkAt(int x, int y, int z) +{ + if (y < minBuildHeight || y >= maxBuildHeight) return false; + return hasChunk(x >> 4, z >> 4); +} + +// 4J added +bool Level::reallyHasChunkAt(int x, int y, int z) +{ + if (y < minBuildHeight || y >= maxBuildHeight) return false; + return reallyHasChunk(x >> 4, z >> 4); +} + +bool Level::hasChunksAt(int x, int y, int z, int r) +{ + return hasChunksAt(x - r, y - r, z - r, x + r, y + r, z + r); +} + +// 4J added +bool Level::reallyHasChunksAt(int x, int y, int z, int r) +{ + return reallyHasChunksAt(x - r, y - r, z - r, x + r, y + r, z + r); +} + + +bool Level::hasChunksAt(int x0, int y0, int z0, int x1, int y1, int z1) +{ + if (y1 < minBuildHeight || y0 >= maxBuildHeight) return false; + + x0 >>= 4; + z0 >>= 4; + x1 >>= 4; + z1 >>= 4; + + for (int x = x0; x <= x1; x++) + for (int z = z0; z <= z1; z++) + if (!hasChunk(x, z)) return false; + + return true; +} + +// 4J added +bool Level::reallyHasChunksAt(int x0, int y0, int z0, int x1, int y1, int z1) +{ + x0 >>= 4; + z0 >>= 4; + x1 >>= 4; + z1 >>= 4; + + for (int x = x0; x <= x1; x++) + for (int z = z0; z <= z1; z++) + if (!reallyHasChunk(x, z)) return false; + + return true; +} + +bool Level::hasChunk(int x, int z) +{ + return this->chunkSource->hasChunk(x, z); +} + +// 4J added +bool Level::reallyHasChunk(int x, int z) +{ + return this->chunkSource->reallyHasChunk(x, z); +} + + +LevelChunk *Level::getChunkAt(int x, int z) +{ + return getChunk(x >> 4, z >> 4); +} + + +LevelChunk *Level::getChunk(int x, int z) +{ + return this->chunkSource->getChunk(x, z); +} + + +bool Level::setTileAndDataNoUpdate(int x, int y, int z, int tile, int data) +{ + return setTileAndDataNoUpdate(x, y, z, tile, data, true); +} + +bool Level::setTileAndDataNoUpdate(int x, int y, int z, int tile, int data, bool informClients) +{ + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + return false; + } + if (y < 0) return false; + if (y >= maxBuildHeight) return false; + LevelChunk *c = getChunk(x >> 4, z >> 4); + // 4J - changes for lighting brought forward from 1.8.2 + bool result; +#ifndef _CONTENT_PACKAGE + int old = c->getTile(x & 15, y, z & 15); + int olddata = c->getData( x & 15, y, z & 15); +#endif + result = c->setTileAndData(x & 15, y, z & 15, tile, data); +#ifndef _CONTENT_PACKAGE + PIXBeginNamedEvent(0,"Checking light %d %d %d",x,y,z); + PIXBeginNamedEvent(0,"was %d, %d now %d, %d",old,olddata,tile,data); +#endif + this->checkLight(x, y, z); + PIXEndNamedEvent(); + PIXEndNamedEvent(); + if (informClients && result && (isClientSide || c->seenByPlayer)) sendTileUpdated(x, y, z); + return result; +} + + +bool Level::setTileNoUpdate(int x, int y, int z, int tile) +{ + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + return false; + } + if (y < 0) return false; + if (y >= maxBuildHeight) return false; + LevelChunk *c = getChunk(x >> 4, z >> 4); + // 4J - changes for lighting brought forward from 1.8.2 + bool result = c->setTile(x & 15, y, z & 15, tile); + this->checkLight(x, y, z); + if (result && (isClientSide || c->seenByPlayer)) sendTileUpdated(x, y, z); + return result; +} + +bool Level::setTileNoUpdateNoLightCheck(int x, int y, int z, int tile) +{ + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + return false; + } + if (y < 0) return false; + if (y >= maxBuildHeight) return false; + LevelChunk *c = getChunk(x >> 4, z >> 4); + // 4J - changes for lighting brought forward from 1.8.2 + bool result = c->setTile(x & 15, y, z & 15, tile); + return result; +} + + +Material *Level::getMaterial(int x, int y, int z) +{ + int t = getTile(x, y, z); + if (t == 0) return Material::air; + return Tile::tiles[t]->material; +} + + +int Level::getData(int x, int y, int z) +{ + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + return 0; + } + if (y < 0) return 0; + if (y >= maxBuildHeight) return 0; + LevelChunk *c = getChunk(x >> 4, z >> 4); + x &= 15; + z &= 15; + return c->getData(x, y, z); +} + + +void Level::setData(int x, int y, int z, int data, bool forceUpdate/*=false*/) // 4J added forceUpdate +{ + if (setDataNoUpdate(x, y, z, data) || forceUpdate) + { + tileUpdated(x, y, z, getTile(x, y, z)); + } +} + +bool Level::setDataNoUpdate(int x, int y, int z, int data) +{ + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + return false; + } + if (y < 0) return false; + if (y >= maxBuildHeight) return false; + LevelChunk *c = getChunk(x >> 4, z >> 4); + int cx = x & 15; + int cz = z & 15; + // 4J - have changed _sendTileData to encode a bitfield of which bits are important to be sent. This will be zero where the original flag was false, and non-zero where the original + // flag was true - hence recreating the original flag as sendTileData here. For nearly all tiles this will be 15 for the case where this used to be true (ie all bits are important) so + // there should be absolutely to change in behaviour. However, for leaf tiles, bits have been masked so we don't bother doing sendTileUpdated if a non-visual thing has changed in the data + unsigned char importantMask = Tile::_sendTileData[c->getTile(cx, y, cz) & Tile::TILE_NUM_MASK]; + bool sendTileData = importantMask != 0; + + bool maskedBitsChanged; + bool result = c->setData(cx, y, cz, data, importantMask, &maskedBitsChanged); + if (result && (isClientSide || (c->seenByPlayer && sendTileData && maskedBitsChanged))) sendTileUpdated(x, y, z); + return result; +} + + +bool Level::setTile(int x, int y, int z, int tile) +{ + if (setTileNoUpdate(x, y, z, tile)) + { + tileUpdated(x, y, z, tile); + return true; + } + return false; +} + + +bool Level::setTileAndData(int x, int y, int z, int tile, int data) +{ + if (setTileAndDataNoUpdate(x, y, z, tile, data)) + { + tileUpdated(x, y, z, tile); + return true; + } + return false; +} + +void Level::sendTileUpdated(int x, int y, int z) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + (*it)->tileChanged(x, y, z); + } +} + +void Level::tileUpdated(int x, int y, int z, int tile) +{ + this->updateNeighborsAt(x, y, z, tile); +} + + +void Level::lightColumnChanged(int x, int z, int y0, int y1) +{ + PIXBeginNamedEvent(0,"LightColumnChanged (%d,%d) %d to %d",x,z,y0,y1); + if (y0 > y1) + { + int tmp = y1; + y1 = y0; + y0 = tmp; + } + + if (!dimension->hasCeiling) + { + PIXBeginNamedEvent(0,"Checking lights"); + for (int y = y0; y <= y1; y++) + { + PIXBeginNamedEvent(0,"Checking light %d", y); + checkLight(LightLayer::Sky, x, y, z); + PIXEndNamedEvent(); + } + PIXEndNamedEvent(); + } + PIXBeginNamedEvent(0,"Setting tiles dirty"); + setTilesDirty(x, y0, z, x, y1, z); + PIXEndNamedEvent(); + PIXEndNamedEvent(); +} + + +void Level::setTileDirty(int x, int y, int z) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + (*it)->setTilesDirty(x, y, z, x, y, z, this); + } +} + + +void Level::setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + (*it)->setTilesDirty(x0, y0, z0, x1, y1, z1, this); + } +} + + +void Level::swap(int x1, int y1, int z1, int x2, int y2, int z2) +{ + int t1 = getTile(x1, y1, z1); + int d1 = getData(x1, y1, z1); + int t2 = getTile(x2, y2, z2); + int d2 = getData(x2, y2, z2); + + setTileAndDataNoUpdate(x1, y1, z1, t2, d2); + setTileAndDataNoUpdate(x2, y2, z2, t1, d1); + + updateNeighborsAt(x1, y1, z1, t2); + updateNeighborsAt(x2, y2, z2, t1); +} + + +void Level::updateNeighborsAt(int x, int y, int z, int tile) +{ + neighborChanged(x - 1, y, z, tile); + neighborChanged(x + 1, y, z, tile); + neighborChanged(x, y - 1, z, tile); + neighborChanged(x, y + 1, z, tile); + neighborChanged(x, y, z - 1, tile); + neighborChanged(x, y, z + 1, tile); +} + + +void Level::neighborChanged(int x, int y, int z, int type) +{ + if (noNeighborUpdate || isClientSide) return; + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile != NULL) tile->neighborChanged(this, x, y, z, type); +} + + + +bool Level::canSeeSky(int x, int y, int z) +{ + return getChunk(x >> 4, z >> 4)->isSkyLit(x & 15, y, z & 15); +} + + +int Level::getDaytimeRawBrightness(int x, int y, int z) +{ + if (y < 0) return 0; + if (y >= maxBuildHeight) y = maxBuildHeight - 1; + return getChunk(x >> 4, z >> 4)->getRawBrightness(x & 15, y, z & 15, 0); +} + + +int Level::getRawBrightness(int x, int y, int z) +{ + return getRawBrightness(x, y, z, true); +} + + +int Level::getRawBrightness(int x, int y, int z, bool propagate) +{ + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + return MAX_BRIGHTNESS; + } + + if (propagate) + { + int id = getTile(x, y, z); + switch(id) + { + case Tile::stoneSlabHalf_Id: + case Tile::woodSlabHalf_Id: + case Tile::farmland_Id: + case Tile::stairs_stone_Id: + case Tile::stairs_wood_Id: + { + int br = getRawBrightness(x, y + 1, z, false); + int br1 = getRawBrightness(x + 1, y, z, false); + int br2 = getRawBrightness(x - 1, y, z, false); + int br3 = getRawBrightness(x, y, z + 1, false); + int br4 = getRawBrightness(x, y, z - 1, false); + if (br1 > br) br = br1; + if (br2 > br) br = br2; + if (br3 > br) br = br3; + if (br4 > br) br = br4; + return br; + } + break; + } + } + + if (y < 0) return 0; + if (y >= maxBuildHeight) y = maxBuildHeight - 1; + + LevelChunk *c = getChunk(x >> 4, z >> 4); + x &= 15; + z &= 15; + return c->getRawBrightness(x, y, z, skyDarken); +} + + +bool Level::isSkyLit(int x, int y, int z) +{ + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + return false; + } + if (dimension->hasCeiling) return false; + + if (y < 0) return false; + if (y >= maxBuildHeight) return true; + if (!hasChunk(x >> 4, z >> 4)) return false; + + LevelChunk *c = getChunk(x >> 4, z >> 4); + x &= 15; + z &= 15; + return c->isSkyLit(x, y, z); +} + + +int Level::getHeightmap(int x, int z) +{ + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + return 0; + } + if (!hasChunk(x >> 4, z >> 4)) return 0; + + LevelChunk *c = getChunk(x >> 4, z >> 4); + return c->getHeightmap(x & 15, z & 15); +} + + +void Level::updateLightIfOtherThan(LightLayer::variety layer, int x, int y, int z, int expected) +{ + if (dimension->hasCeiling && layer == LightLayer::Sky) return; + + if (!hasChunkAt(x, y, z)) return; + + if (layer == LightLayer::Sky) + { + if (isSkyLit(x, y, z)) expected = 15; + } + else if (layer == LightLayer::Block) + { + int t = getTile(x, y, z); + if (Tile::lightEmission[t] > expected) expected = Tile::lightEmission[t]; + } + + if (getBrightness(layer, x, y, z) != expected) + { + setBrightness(layer, x, y, z, expected); + } +} + +// 4J - update brought forward from 1.8.2 +int Level::getBrightnessPropagate(LightLayer::variety layer, int x, int y, int z, int tileId) +{ + if (dimension->hasCeiling && layer == LightLayer::Sky) return 0; + + if (y < 0) y = 0; + if (y >= maxBuildHeight && layer == LightLayer::Sky) + { + // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we + // were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast + // it to an int + return (int)layer; + } + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we + // were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast + // it to an int + return (int)layer; + } + int xc = x >> 4; + int zc = z >> 4; + if (!hasChunk(xc, zc)) return (int)layer; + + { + int id = tileId > -1 ? tileId : getTile(x,y,z); + if (Tile::propagate[id]) + { + int br = getBrightness(layer, x, y + 1, z); + int br1 = getBrightness(layer, x + 1, y, z); + int br2 = getBrightness(layer, x - 1, y, z); + int br3 = getBrightness(layer, x, y, z + 1); + int br4 = getBrightness(layer, x, y, z - 1); + if (br1 > br) br = br1; + if (br2 > br) br = br2; + if (br3 > br) br = br3; + if (br4 > br) br = br4; + return br; + } + } + + LevelChunk *c = getChunk(xc, zc); + return c->getBrightness(layer, x & 15, y, z & 15); +} + +int Level::getBrightness(LightLayer::variety layer, int x, int y, int z) +{ + // 4J - optimised. Not doing checks on x/z that are no longer necessary, and directly checking the cache within + // the ServerChunkCache/MultiplayerChunkCache rather than going through wrappers & virtual functions. + int xc = x >> 4; + int zc = z >> 4; + + int ix = xc + (chunkSourceXZSize/2); + int iz = zc + (chunkSourceXZSize/2); + + if( ( ix < 0 ) || ( ix >= chunkSourceXZSize ) ) return 0; + if( ( iz < 0 ) || ( iz >= chunkSourceXZSize ) ) return 0; + int idx = ix * chunkSourceXZSize + iz; + LevelChunk *c = chunkSourceCache[idx]; + + if( c == NULL ) return (int)layer; + + if (y < 0) y = 0; + if (y >= maxBuildHeight) y = maxBuildHeight - 1; + + return c->getBrightness(layer, x & 15, y, z & 15); +} + +// 4J added as optimisation - if all the neighbouring brightesses are going to be in the one chunk, just get +// the level chunk once +void Level::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety layer, int x, int y, int z) +{ + if( ( ( ( x & 15 ) == 0 ) || ( ( x & 15 ) == 15 ) ) || + ( ( ( z & 15 ) == 0 ) || ( ( z & 15 ) == 15 ) ) || + ( ( y <= 0 ) || ( y >= 127 ) ) ) + { + // We're spanning more than one chunk, just fall back on original java method here + brightnesses[0] = getBrightness(layer, x - 1, y, z); + brightnesses[1] = getBrightness(layer, x + 1, y, z); + brightnesses[2] = getBrightness(layer, x, y - 1, z); + brightnesses[3] = getBrightness(layer, x, y + 1, z); + brightnesses[4] = getBrightness(layer, x, y, z - 1); + brightnesses[5] = getBrightness(layer, x, y, z + 1); + } + else + { + // All in one chunk - just get the chunk once, and do a single call to get the results + int xc = x >> 4; + int zc = z >> 4; + + int ix = xc + (chunkSourceXZSize/2); + int iz = zc + (chunkSourceXZSize/2); + + // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we + // were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast + // it to an int + if( ( ( ix < 0 ) || ( ix >= chunkSourceXZSize ) ) || + ( ( iz < 0 ) || ( iz >= chunkSourceXZSize ) ) ) + { + for( int i = 0; i < 6; i++ ) + { + brightnesses[i] = (int)layer; + } + return; + } + + int idx = ix * chunkSourceXZSize + iz; + LevelChunk *c = chunkSourceCache[idx]; + + // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we + // were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast + // it to an int + if( c == NULL ) + { + for( int i = 0; i < 6; i++ ) + { + brightnesses[i] = (int)layer; + } + return; + } + + // Single call to the levelchunk too to avoid overhead of virtual fn calls + c->getNeighbourBrightnesses(brightnesses, layer, x & 15, y, z & 15); + } +} + +void Level::setBrightness(LightLayer::variety layer, int x, int y, int z, int brightness, bool noUpdateOnClient/*=false*/) // 4J added noUpdateOnClient +{ + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + return; + } + if (y < 0) return; + if (y >= maxBuildHeight) return; + if (!hasChunk(x >> 4, z >> 4)) return; + LevelChunk *c = getChunk(x >> 4, z >> 4); + + c->setBrightness(layer, x & 15, y, z & 15, brightness); + + // 4J added + if( isClientSide && noUpdateOnClient ) + { + if( cachewritten ) + { + if( x < cacheminx ) cacheminx = x; + if( x > cachemaxx ) cachemaxx = x; + if( y < cacheminy ) cacheminy = y; + if( y > cachemaxy ) cachemaxy = y; + if( z < cacheminz ) cacheminz = z; + if( z > cachemaxz ) cachemaxz = z; + } + else + { + cachewritten = true; + cacheminx = x; + cachemaxx = x; + cacheminy = y; + cachemaxy = y; + cacheminz = z; + cachemaxz = z; + } + } + else + { + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + (*it)->tileLightChanged(x, y, z); + } + } +} + +void Level::setTileBrightnessChanged(int x, int y, int z) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + (*it)->tileLightChanged(x, y, z); + } +} + +int Level::getLightColor(int x, int y, int z, int emitt, int tileId/*=-1*/) +{ + int s = getBrightnessPropagate(LightLayer::Sky, x, y, z, tileId); + int b = getBrightnessPropagate(LightLayer::Block, x, y, z, tileId); + if (b < emitt) b = emitt; + return s << 20 | b << 4; +} + +float Level::getBrightness(int x, int y, int z, int emitt) +{ + int n = getRawBrightness(x, y, z); + if (n < emitt) n = emitt; + return dimension->brightnessRamp[n]; +} + + +float Level::getBrightness(int x, int y, int z) +{ + return dimension->brightnessRamp[getRawBrightness(x, y, z)]; +} + + +bool Level::isDay() +{ + return this->skyDarken < 4; +} + + +HitResult *Level::clip(Vec3 *a, Vec3 *b) +{ + return clip(a, b, false, false); +} + + +HitResult *Level::clip(Vec3 *a, Vec3 *b, bool liquid) +{ + return clip(a, b, liquid, false); +} + + +HitResult *Level::clip(Vec3 *a, Vec3 *b, bool liquid, bool solidOnly) +{ + if (Double::isNaN(a->x) || Double::isNaN(a->y) || Double::isNaN(a->z)) return NULL; + if (Double::isNaN(b->x) || Double::isNaN(b->y) || Double::isNaN(b->z)) return NULL; + + int xTile1 = Mth::floor(b->x); + int yTile1 = Mth::floor(b->y); + int zTile1 = Mth::floor(b->z); + + int xTile0 = Mth::floor(a->x); + int yTile0 = Mth::floor(a->y); + int zTile0 = Mth::floor(a->z); + + { + int t = getTile(xTile0, yTile0, zTile0); + int data = getData(xTile0, yTile0, zTile0); + Tile *tile = Tile::tiles[t]; + if (solidOnly && tile != NULL && tile->getAABB(this, xTile0, yTile0, zTile0) == NULL) + { + // No collision + + } + else if (t > 0 && tile->mayPick(data, liquid)) + { + HitResult *r = tile->clip(this, xTile0, yTile0, zTile0, a, b); + if (r != NULL) return r; + } + } + + int maxIterations = 200; + while (maxIterations-- >= 0) + { + if (Double::isNaN(a->x) || Double::isNaN(a->y) || Double::isNaN(a->z)) return NULL; + if (xTile0 == xTile1 && yTile0 == yTile1 && zTile0 == zTile1) return NULL; + + bool xClipped = true; + bool yClipped = true; + bool zClipped = true; + + double xClip = 999; + double yClip = 999; + double zClip = 999; + + if (xTile1 > xTile0) xClip = xTile0 + 1.000; + else if (xTile1 < xTile0) xClip = xTile0 + 0.000; + else xClipped = false; + + if (yTile1 > yTile0) yClip = yTile0 + 1.000; + else if (yTile1 < yTile0) yClip = yTile0 + 0.000; + else yClipped = false; + + if (zTile1 > zTile0) zClip = zTile0 + 1.000; + else if (zTile1 < zTile0) zClip = zTile0 + 0.000; + else zClipped = false; + + double xDist = 999; + double yDist = 999; + double zDist = 999; + + double xd = b->x - a->x; + double yd = b->y - a->y; + double zd = b->z - a->z; + + if (xClipped) xDist = (xClip - a->x) / xd; + if (yClipped) yDist = (yClip - a->y) / yd; + if (zClipped) zDist = (zClip - a->z) / zd; + + int face = 0; + if (xDist < yDist && xDist < zDist) + { + if (xTile1 > xTile0) face = 4; + else face = 5; + + a->x = xClip; + a->y += yd * xDist; + a->z += zd * xDist; + } + else if (yDist < zDist) + { + if (yTile1 > yTile0) face = 0; + else face = 1; + + a->x += xd * yDist; + a->y = yClip; + a->z += zd * yDist; + } + else + { + if (zTile1 > zTile0) face = 2; + else face = 3; + + a->x += xd * zDist; + a->y += yd * zDist; + a->z = zClip; + } + + Vec3 *tPos = Vec3::newTemp(a->x, a->y, a->z); + xTile0 = (int) (tPos->x = floor(a->x)); + if (face == 5) + { + xTile0--; + tPos->x++; + } + yTile0 = (int) (tPos->y = floor(a->y)); + if (face == 1) + { + yTile0--; + tPos->y++; + } + zTile0 = (int) (tPos->z = floor(a->z)); + if (face == 3) + { + zTile0--; + tPos->z++; + } + + int t = getTile(xTile0, yTile0, zTile0); + int data = getData(xTile0, yTile0, zTile0); + Tile *tile = Tile::tiles[t]; + if (solidOnly && tile != NULL && tile->getAABB(this, xTile0, yTile0, zTile0) == NULL) + { + // No collision + + } + else if (t > 0 && tile->mayPick(data, liquid)) + { + HitResult *r = tile->clip(this, xTile0, yTile0, zTile0, a, b); + if (r != NULL) return r; + } + } + return NULL; +} + + +void Level::playSound(shared_ptr entity, int iSound, float volume, float pitch) +{ + if(entity == NULL) return; + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + // 4J-PB - if the entity is a local player, don't play the sound + if(entity->GetType() == eTYPE_SERVERPLAYER) + { + //app.DebugPrintf("ENTITY is serverplayer\n"); + + (*it)->playSound(entity,iSound, entity->x, entity->y - entity->heightOffset, entity->z, volume, pitch); + } + else + { + (*it)->playSound(iSound, entity->x, entity->y - entity->heightOffset, entity->z, volume, pitch); + } + } +} + + +//void Level::playSound(double x, double y, double z, const wstring& name, float volume, float pitch) +void Level::playSound(double x, double y, double z, int iSound, float volume, float pitch, float fClipSoundDist) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + (*it)->playSound(iSound, x, y, z, volume, pitch, fClipSoundDist); + } +} + +void Level::playLocalSound(double x, double y, double z, int iSound, float volume, float pitch, float fClipSoundDist) +{ +} + +void Level::playStreamingMusic(const wstring& name, int x, int y, int z) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + (*it)->playStreamingMusic(name, x, y, z); + } +} + + +void Level::playMusic(double x, double y, double z, const wstring& string, float volume) +{ +} + +// 4J removed - +/* +void Level::addParticle(const wstring& id, double x, double y, double z, double xd, double yd, double zd) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + (*it)->addParticle(id, x, y, z, xd, yd, zd); +} +*/ + +// 4J-PB added +void Level::addParticle(ePARTICLE_TYPE id, double x, double y, double z, double xd, double yd, double zd) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + (*it)->addParticle(id, x, y, z, xd, yd, zd); +} + +bool Level::addGlobalEntity(shared_ptr e) +{ + globalEntities.push_back(e); + return true; +} + +#pragma optimize( "", off ) + +bool Level::addEntity(shared_ptr e) +{ + int xc = Mth::floor(e->x / 16); + int zc = Mth::floor(e->z / 16); + + if(e == NULL) + { + return false; + } + + bool forced = false; + if (dynamic_pointer_cast( e ) != NULL) + { + forced = true; + } + + if (forced || hasChunk(xc, zc)) + { + if (dynamic_pointer_cast( e ) != NULL) + { + shared_ptr player = dynamic_pointer_cast(e); + + // 4J Stu - Added so we don't continually add the player to the players list while they are dead + if( find( players.begin(), players.end(), e ) == players.end() ) + { + players.push_back(player); + } + + updateSleepingPlayerList(); + } + MemSect(42); + getChunk(xc, zc)->addEntity(e); + MemSect(0); + EnterCriticalSection(&m_entitiesCS); + MemSect(43); + entities.push_back(e); + MemSect(0); + LeaveCriticalSection(&m_entitiesCS); + MemSect(44); + entityAdded(e); + MemSect(0); + return true; + } + return false; +} + +#pragma optimize( "", on ) + +void Level::entityAdded(shared_ptr e) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + (*it)->entityAdded(e); + } +} + + +void Level::entityRemoved(shared_ptr e) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + (*it)->entityRemoved(e); + } +} + +// 4J added +void Level::playerRemoved(shared_ptr e) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + (*it)->playerRemoved(e); + } +} + +void Level::removeEntity(shared_ptr e) +{ + if (e->rider.lock() != NULL) + { + e->rider.lock()->ride(nullptr); + } + if (e->riding != NULL) + { + e->ride(nullptr); + } + e->remove(); + if (dynamic_pointer_cast( e ) != NULL) + { + vector >::iterator it = players.begin(); + vector >::iterator itEnd = players.end(); + while( it != itEnd && *it != dynamic_pointer_cast(e) ) + it++; + + if( it != itEnd ) + { + players.erase( it ); + } + + updateSleepingPlayerList(); + playerRemoved(e); // 4J added - this will let the entity tracker know that we have actually removed the player from the level's player list + } +} + + +void Level::removeEntityImmediately(shared_ptr e) +{ + e->remove(); + + if (dynamic_pointer_cast( e ) != NULL) + { + vector >::iterator it = players.begin(); + vector >::iterator itEnd = players.end(); + while( it != itEnd && *it != dynamic_pointer_cast(e) ) + it++; + + if( it != itEnd ) + { + players.erase( it ); + } + + updateSleepingPlayerList(); + playerRemoved(e); // 4J added - this will let the entity tracker know that we have actually removed the player from the level's player list + } + + int xc = e->xChunk; + int zc = e->zChunk; + if (e->inChunk && hasChunk(xc, zc)) + { + getChunk(xc, zc)->removeEntity(e); + } + + EnterCriticalSection(&m_entitiesCS); + vector >::iterator it = entities.begin(); + vector >::iterator endIt = entities.end(); + while( it != endIt && *it != e) + it++; + + if( it != endIt ) + { + entities.erase( it ); + } + LeaveCriticalSection(&m_entitiesCS); + entityRemoved(e); +} + + +void Level::addListener(LevelListener *listener) +{ + listeners.push_back(listener); +} + + +void Level::removeListener(LevelListener *listener) +{ + vector::iterator it = listeners.begin(); + vector::iterator itEnd = listeners.end(); + while( it != itEnd && *it != listener ) + it++; + + if( it != itEnd ) + listeners.erase( it ); +} + + +// 4J - added noEntities and blockAtEdge parameter +AABBList *Level::getCubes(shared_ptr source, AABB *box, bool noEntities, bool blockAtEdge) +{ + boxes.clear(); + int x0 = Mth::floor(box->x0); + int x1 = Mth::floor(box->x1 + 1); + int y0 = Mth::floor(box->y0); + int y1 = Mth::floor(box->y1 + 1); + int z0 = Mth::floor(box->z0); + int z1 = Mth::floor(box->z1 + 1); + + int maxxz = ( dimension->getXZSize() * 16 ) / 2; + int minxz = -maxxz; + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + // 4J - If we are outside the map, return solid AABBs (rock is a bit of an arbitrary choice here, just need a correct AABB) + if( blockAtEdge && ( ( x < minxz ) || ( x >= maxxz ) || ( z < minxz ) || ( z >= maxxz ) ) ) + { + for (int y = y0 - 1; y < y1; y++) + { + Tile::rock->addAABBs(this, x, y, z, box, &boxes, source); + } + } + else + { + if (hasChunkAt(x, 64, z)) + { + for (int y = y0 - 1; y < y1; y++) + { + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile != NULL) + { + tile->addAABBs(this, x, y, z, box, &boxes, source); + } + } + } + } + } + // 4J - also stop player falling out of the bottom of the map if blockAtEdge is true. Again, rock is an arbitrary choice here + // 4J Stu - Don't stop entities falling into the void while in The End (it has no bedrock) + if( blockAtEdge && ( ( y0 - 1 ) < 0 ) && dimension->id != 1 ) + { + for (int y = y0 - 1; y < 0; y++) + { + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + Tile::rock->addAABBs(this, x, y, z, box, &boxes, source ); + } + } + } + // 4J - final bounds check - limit vertical movement so we can't move above maxMovementHeight + if( blockAtEdge && ( y1 > maxMovementHeight ) ) + { + for (int y = maxMovementHeight; y < y1; y++) + { + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + Tile::rock->addAABBs(this, x, y, z, box, &boxes, source ); + } + } + } + // 4J - now add in collision for any blocks which have actually been removed, but haven't had their render data updated to reflect this yet. This is to stop the player + // being able to move the view position inside a tile which is (visually) still there, and see out of the world. This is particularly a problem when moving upwards in + // creative mode as the player can get very close to the edge of tiles whilst looking upwards and can therefore very quickly move inside one. + Minecraft::GetInstance()->levelRenderer->destroyedTileManager->addAABBs( this, box, &boxes); + + // 4J - added + if( noEntities ) return &boxes; + + double r = 0.25; + vector > *ee = getEntities(source, box->grow(r, r, r)); + vector >::iterator itEnd = ee->end(); + for (AUTO_VAR(it, ee->begin()); it != itEnd; it++) + { + AABB *collideBox = (*it)->getCollideBox(); + if (collideBox != NULL && collideBox->intersects(box)) + { + boxes.push_back(collideBox); + } + + collideBox = source->getCollideAgainstBox(*it); + if (collideBox != NULL && collideBox->intersects(box)) + { + boxes.push_back(collideBox); + } + } + + return &boxes; +} + +// 4J Stu - Brought forward from 12w36 to fix #46282 - TU5: Gameplay: Exiting the minecart in a tight corridor damages the player +AABBList *Level::getTileCubes(AABB *box, bool blockAtEdge) +{ + return getCubes(nullptr, box, true, blockAtEdge); + //boxes.clear(); + //int x0 = Mth::floor(box->x0); + //int x1 = Mth::floor(box->x1 + 1); + //int y0 = Mth::floor(box->y0); + //int y1 = Mth::floor(box->y1 + 1); + //int z0 = Mth::floor(box->z0); + //int z1 = Mth::floor(box->z1 + 1); + + //for (int x = x0; x < x1; x++) + //{ + // for (int z = z0; z < z1; z++) + // { + // if (hasChunkAt(x, 64, z)) + // { + // for (int y = y0 - 1; y < y1; y++) + // { + // Tile *tile = Tile::tiles[getTile(x, y, z)]; + + // if (tile != NULL) + // { + // tile->addAABBs(this, x, y, z, box, &boxes); + // } + // } + // } + // } + //} + + //return boxes; +} + +//4J - change brought forward from 1.8.2 +int Level::getOldSkyDarken(float a) +{ + float td = getTimeOfDay(a); + + float br = 1 - (Mth::cos(td * PI * 2) * 2 + 0.5f); + if (br < 0.0f) br = 0.0f; + if (br > 1.0f) br = 1.0f; + + br = 1 - br; + + br *= 1 - (getRainLevel(a) * 5 / 16.0f); + br *= 1 - (getThunderLevel(a) * 5 / 16.0f); + br = 1 - br; + return ((int) (br * 11)); +} + +//4J - change brought forward from 1.8.2 +float Level::getSkyDarken(float a) +{ + float td = getTimeOfDay(a); + + float br = 1 - (Mth::cos(td * PI * 2) * 2 + 0.2f); + if (br < 0.0f) br = 0.0f; + if (br > 1.0f) br = 1.0f; + + br = 1.0f - br; + + br *= 1.0f - (getRainLevel(a) * 5.0f / 16.0f); + br *= 1.0f - (getThunderLevel(a) * 5.0f / 16.0f); + // return ((int) (br * 13)); + + return br * 0.8f + 0.2f; +} + + + +Vec3 *Level::getSkyColor(shared_ptr source, float a) +{ + float td = getTimeOfDay(a); + + float br = Mth::cos(td * PI * 2) * 2 + 0.5f; + if (br < 0.0f) br = 0.0f; + if (br > 1.0f) br = 1.0f; + + int xx = Mth::floor(source->x); + int zz = Mth::floor(source->z); + Biome *biome = getBiome(xx, zz); + float temp = biome->getTemperature(); + int skyColor = biome->getSkyColor(temp); + + float r = ((skyColor >> 16) & 0xff) / 255.0f; + float g = ((skyColor >> 8) & 0xff) / 255.0f; + float b = ((skyColor) & 0xff) / 255.0f; + r *= br; + g *= br; + b *= br; + + float rainLevel = getRainLevel(a); + if (rainLevel > 0) + { + float mid = (r * 0.30f + g * 0.59f + b * 0.11f) * 0.6f; + + float ba = 1 - rainLevel * 0.75f; + r = r * ba + mid * (1 - ba); + g = g * ba + mid * (1 - ba); + b = b * ba + mid * (1 - ba); + } + float thunderLevel = getThunderLevel(a); + if (thunderLevel > 0) + { + float mid = (r * 0.30f + g * 0.59f + b * 0.11f) * 0.2f; + + float ba = 1 - thunderLevel * 0.75f; + r = r * ba + mid * (1 - ba); + g = g * ba + mid * (1 - ba); + b = b * ba + mid * (1 - ba); + } + + if (lightningBoltTime > 0) + { + float f = (lightningBoltTime - a); + if (f > 1) f = 1; + f = f * 0.45f; + r = r * (1 - f) + 0.8f * f; + g = g * (1 - f) + 0.8f * f; + b = b * (1 - f) + 1 * f; + } + + return Vec3::newTemp(r, g, b); +} + + +float Level::getTimeOfDay(float a) +{ + /* + * 4J-PB removed line below - notch committed 1.6.6 with the incorrect + * getTimeOfDay and changed it before releasing (without + * re-committing)... that should be the only difference // jeb + */ + /* if (this != NULL) return 0.5f; */ + + // 4J Added if so we can override timeOfDay without changing the time that affects ticking of things + if( m_timeOfDayOverride >= 0 ) + { + return dimension->getTimeOfDay(m_timeOfDayOverride, a); + } + else + { + return dimension->getTimeOfDay(levelData->getTime(), a);; + } +} + +int Level::getMoonPhase(float a) +{ + return dimension->getMoonPhase(levelData->getTime(), a); +} + +float Level::getSunAngle(float a) +{ + float td = getTimeOfDay(a); + return td * PI * 2; +} + + +Vec3 *Level::getCloudColor(float a) +{ + float td = getTimeOfDay(a); + + float br = Mth::cos(td * PI * 2) * 2.0f + 0.5f; + if (br < 0.0f) br = 0.0f; + if (br > 1.0f) br = 1.0f; + + int baseCloudColour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_In_Cloud_Base_Colour ); + + float r = ((baseCloudColour >> 16) & 0xff) / 255.0f; + float g = ((baseCloudColour >> 8) & 0xff) / 255.0f; + float b = ((baseCloudColour) & 0xff) / 255.0f; + + float rainLevel = getRainLevel(a); + if (rainLevel > 0) + { + float mid = (r * 0.30f + g * 0.59f + b * 0.11f) * 0.6f; + + float ba = 1 - rainLevel * 0.95f; + r = r * ba + mid * (1 - ba); + g = g * ba + mid * (1 - ba); + b = b * ba + mid * (1 - ba); + } + + r *= br * 0.90f + 0.10f; + g *= br * 0.90f + 0.10f; + b *= br * 0.85f + 0.15f; + + float thunderLevel = getThunderLevel(a); + if (thunderLevel > 0) + { + float mid = (r * 0.30f + g * 0.59f + b * 0.11f) * 0.2f; + + float ba = 1 - thunderLevel * 0.95f; + r = r * ba + mid * (1 - ba); + g = g * ba + mid * (1 - ba); + b = b * ba + mid * (1 - ba); + } + + return Vec3::newTemp(r, g, b); +} + + +Vec3 *Level::getFogColor(float a) +{ + float td = getTimeOfDay(a); + return dimension->getFogColor(td, a); +} + + +int Level::getTopRainBlock(int x, int z) +{ + // 4J - optimisation brought forward from 1.8.2 - used to do full calculation here but result is now cached in LevelChunk + return getChunkAt(x, z)->getTopRainBlock(x & 15, z & 15); +} + +// 4J added +bool Level::biomeHasRain(int x, int z) +{ + return getChunkAt(x, z)->biomeHasRain(x & 15, z & 15); +} + +// 4J added +bool Level::biomeHasSnow(int x, int z) +{ + return getChunkAt(x, z)->biomeHasSnow(x & 15, z & 15); +} + +int Level::getTopSolidBlock(int x, int z) +{ + LevelChunk *levelChunk = getChunkAt(x, z); + + int y = levelChunk->getHighestSectionPosition() + 15; + + x &= 15; + z &= 15; + + while (y > 0) + { + int t = levelChunk->getTile(x, y, z); + if (t == 0 || !(Tile::tiles[t]->material->blocksMotion()) || Tile::tiles[t]->material == Material::leaves) + { + y--; + } + else + { + return y + 1; + } + } + return -1; +} + + +int Level::getLightDepth(int x, int z) +{ + return getChunkAt(x, z)->getHeightmap(x & 15, z & 15); +} + + +float Level::getStarBrightness(float a) +{ + float td = getTimeOfDay(a); + + float br = 1 - (Mth::cos(td * PI * 2) * 2 + 0.25f); + if (br < 0.0f) br = 0.0f; + if (br > 1.0f) br = 1.0f; + + return br * br * 0.5f; +} + + +void Level::addToTickNextTick(int x, int y, int z, int tileId, int tickDelay) +{ +} + +void Level::forceAddTileTick(int x, int y, int z, int tileId, int tickDelay) +{ +} + +void Level::tickEntities() +{ + //for (int i = 0; i < globalEntities.size(); i++) + vector >::iterator itGE = globalEntities.begin(); + while( itGE != globalEntities.end() ) + { + shared_ptr e = *itGE;//globalEntities.at(i); + e->tick(); + if (e->removed) + { + //globalEntities.remove(i--); + itGE = globalEntities.erase( itGE ); + } + else + { + itGE++; + } + } + + //entities.removeAll(entitiesToRemove); + EnterCriticalSection(&m_entitiesCS); + + for( AUTO_VAR(it, entities.begin()); it != entities.end(); ) + { + bool found = false; + for( AUTO_VAR(it2, entitiesToRemove.begin()); it2 != entitiesToRemove.end(); it2++ ) + { + if( (*it) == (*it2) ) + { + found = true; + break; + } + } + if( found ) + { + it = entities.erase(it); + } + else + { + it++; + } + } + LeaveCriticalSection(&m_entitiesCS); + + AUTO_VAR(itETREnd, entitiesToRemove.end()); + for (AUTO_VAR(it, entitiesToRemove.begin()); it != itETREnd; it++) + { + shared_ptr e = *it;//entitiesToRemove.at(j); + int xc = e->xChunk; + int zc = e->zChunk; + if (e->inChunk && hasChunk(xc, zc)) + { + getChunk(xc, zc)->removeEntity(e); + } + } + + itETREnd = entitiesToRemove.end(); + for (AUTO_VAR(it, entitiesToRemove.begin()); it != itETREnd; it++) + { + entityRemoved(*it); + } + // + entitiesToRemove.clear(); + + //for (int i = 0; i < entities.size(); i++) + + /* 4J Jev, using an iterator causes problems here as + * the vector is modified from inside this loop. + */ + EnterCriticalSection(&m_entitiesCS); + + for (unsigned int i = 0; i < entities.size(); ) + { + shared_ptr e = entities.at(i); + + if (e->riding != NULL) + { + if (e->riding->removed || e->riding->rider.lock() != e) + { + e->riding->rider = weak_ptr(); + e->riding = nullptr; + } + else + { + i++; + continue; + } + } + + if (!e->removed) + { +#ifndef _FINAL_BUILD + if(!( app.DebugSettingsOn() && app.GetMobsDontTickEnabled() && (dynamic_pointer_cast(e) != NULL) && (dynamic_pointer_cast(e) == NULL))) +#endif + { + tick(e); + } + } + + if (e->removed) + { + int xc = e->xChunk; + int zc = e->zChunk; + if (e->inChunk && hasChunk(xc, zc)) + { + getChunk(xc, zc)->removeEntity(e); + } + //entities.remove(i--); + //itE = entities.erase( itE ); + + // 4J Find the entity again before deleting, as things might have moved in the entity array eg + // from the explosion created by tnt + AUTO_VAR(it, find(entities.begin(), entities.end(), e)); + if( it != entities.end() ) + { + entities.erase(it); + } + + entityRemoved(e); + } + else + { + i++; + } + } + LeaveCriticalSection(&m_entitiesCS); + + EnterCriticalSection(&m_tileEntityListCS); + + updatingTileEntities = true; + for (AUTO_VAR(it, tileEntityList.begin()); it != tileEntityList.end();) + { + shared_ptr te = *it;//tilevector >.at(i); + if( !te->isRemoved() && te->hasLevel() ) + { + if (hasChunkAt(te->x, te->y, te->z)) + { +#ifdef _LARGE_WORLDS + LevelChunk *lc = getChunk(te->x >> 4, te->z >> 4); + if(!isClientSide || !lc->isUnloaded()) +#endif + { + te->tick(); + } + } + } + + if( te->isRemoved() ) + { + it = tileEntityList.erase(it); + if (hasChunk(te->x >> 4, te->z >> 4)) + { + LevelChunk *lc = getChunk(te->x >> 4, te->z >> 4); + if (lc != NULL) lc->removeTileEntity(te->x & 15, te->y, te->z & 15); + } + } + else + { + it++; + } + } + updatingTileEntities = false; + +// 4J-PB - Stuart - check this is correct here + + if (!tileEntitiesToUnload.empty()) + { + //tileEntityList.removeAll(tileEntitiesToUnload); + + for( AUTO_VAR(it, tileEntityList.begin()); it != tileEntityList.end(); ) + { + bool found = false; + for( AUTO_VAR(it2, tileEntitiesToUnload.begin()); it2 != tileEntitiesToUnload.end(); it2++ ) + { + if( (*it) == (*it2) ) + { + found = true; + break; + } + } + if( found ) + { + if(isClientSide) + { + __debugbreak(); + } + it = tileEntityList.erase(it); + } + else + { + it++; + } + } + tileEntitiesToUnload.clear(); + } + + if( !pendingTileEntities.empty() ) + { + for( AUTO_VAR(it, pendingTileEntities.begin()); it != pendingTileEntities.end(); it++ ) + { + shared_ptr e = *it; + if( !e->isRemoved() ) + { + if( find(tileEntityList.begin(),tileEntityList.end(),e) == tileEntityList.end() ) + { + tileEntityList.push_back(e); + } + if (hasChunk(e->x >> 4, e->z >> 4)) + { + LevelChunk *lc = getChunk(e->x >> 4, e->z >> 4); + if (lc != NULL) lc->setTileEntity(e->x & 15, e->y, e->z & 15, e); + } + + sendTileUpdated(e->x, e->y, e->z); + } + } + pendingTileEntities.clear(); + } + LeaveCriticalSection(&m_tileEntityListCS); +} + +void Level::addAllPendingTileEntities(vector< shared_ptr >& entities) +{ + EnterCriticalSection(&m_tileEntityListCS); + if( updatingTileEntities ) + { + for( AUTO_VAR(it, entities.begin()); it != entities.end(); it++ ) + { + pendingTileEntities.push_back(*it); + } + } + else + { + for( AUTO_VAR(it, entities.begin()); it != entities.end(); it++ ) + { + tileEntityList.push_back(*it); + } + } + LeaveCriticalSection(&m_tileEntityListCS); +} + +void Level::tick(shared_ptr e) +{ + tick(e, true); +} + + +void Level::tick(shared_ptr e, bool actual) +{ + int xc = Mth::floor(e->x); + int zc = Mth::floor(e->z); + int r = 32; +#ifdef __PSVITA__ + // AP - make sure the dragon ticks all the time, even when there aren't any chunks. + if (actual && e->GetType() != eTYPE_ENDERDRAGON && !hasChunksAt(xc - r, 0, zc - r, xc + r, 0, zc + r)) +#else + if (actual && !hasChunksAt(xc - r, 0, zc - r, xc + r, 0, zc + r)) +#endif + { + return; + } + + e->xOld = e->x; + e->yOld = e->y; + e->zOld = e->z; + e->yRotO = e->yRot; + e->xRotO = e->xRot; + +#ifdef __PSVITA__ + // AP - make sure the dragon ticks all the time, even when there aren't any chunks. + if (actual && (e->GetType() == eTYPE_ENDERDRAGON || e->inChunk) ) +#else + if (actual && e->inChunk ) +#endif + { + if (e->riding != NULL) + { + e->rideTick(); + } + else + { + e->tick(); + } + } + + // SANTITY!! + if (Double::isNaN(e->x) || Double::isInfinite(e->x)) e->x = e->xOld; + if (Double::isNaN(e->y) || Double::isInfinite(e->y)) e->y = e->yOld; + if (Double::isNaN(e->z) || Double::isInfinite(e->z)) e->z = e->zOld; + if (Double::isNaN(e->xRot) || Double::isInfinite(e->xRot)) e->xRot = e->xRotO; + if (Double::isNaN(e->yRot) || Double::isInfinite(e->yRot)) e->yRot = e->yRotO; + + int xcn = Mth::floor(e->x / 16); + int ycn = Mth::floor(e->y / 16); + int zcn = Mth::floor(e->z / 16); + + + + if (!e->inChunk || (e->xChunk != xcn || e->yChunk != ycn || e->zChunk != zcn)) + { + if (e->inChunk && hasChunk(e->xChunk, e->zChunk)) + { + getChunk(e->xChunk, e->zChunk)->removeEntity(e, e->yChunk); + } + + if (hasChunk(xcn, zcn)) + { + + e->inChunk = true; + MemSect(39); + getChunk(xcn, zcn)->addEntity(e); + MemSect(0); + } + else + { + e->inChunk = false; + // e.remove(); + } + } + + if (actual && e->inChunk) + { + if (e->rider.lock() != NULL) + { + if (e->rider.lock()->removed || e->rider.lock()->riding != e) + { + e->rider.lock()->riding = nullptr; + e->rider = weak_ptr(); + } + else + { + tick(e->rider.lock()); + } + } + } +} + + +bool Level::isUnobstructed(AABB *aabb) +{ + return isUnobstructed(aabb, nullptr); +} + +bool Level::isUnobstructed(AABB *aabb, shared_ptr ignore) +{ + vector > *ents = getEntities(nullptr, aabb); + AUTO_VAR(itEnd, ents->end()); + for (AUTO_VAR(it, ents->begin()); it != itEnd; it++) + { + shared_ptr e = *it; + if (!e->removed && e->blocksBuilding && e != ignore) return false; + } + return true; +} + + +bool Level::containsAnyBlocks(AABB *box) +{ + int x0 = Mth::floor(box->x0); + int x1 = Mth::floor(box->x1 + 1); + int y0 = Mth::floor(box->y0); + int y1 = Mth::floor(box->y1 + 1); + int z0 = Mth::floor(box->z0); + int z1 = Mth::floor(box->z1 + 1); + + if (box->x0 < 0) x0--; + if (box->y0 < 0) y0--; + if (box->z0 < 0) z0--; + + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + for (int z = z0; z < z1; z++) + { + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile != NULL) + { + return true; + } + } + return false; +} + + +bool Level::containsAnyLiquid(AABB *box) +{ + int x0 = Mth::floor(box->x0); + int x1 = Mth::floor(box->x1 + 1); + int y0 = Mth::floor(box->y0); + int y1 = Mth::floor(box->y1 + 1); + int z0 = Mth::floor(box->z0); + int z1 = Mth::floor(box->z1 + 1); + + if (box->x0 < 0) x0--; + if (box->y0 < 0) y0--; + if (box->z0 < 0) z0--; + + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + for (int z = z0; z < z1; z++) + { + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile != NULL && tile->material->isLiquid()) + { + return true; + } + } + return false; +} + +// 4J - added this to be used during mob spawning, and it returns true if there's any liquid in the bounding box, or might be because +// we don't have a loaded chunk that we'd need to determine whether it really did. The overall aim is to not load or create any chunk +// we haven't already got, and be cautious about placing the mob's. +bool Level::containsAnyLiquid_NoLoad(AABB *box) +{ + int x0 = Mth::floor(box->x0); + int x1 = Mth::floor(box->x1 + 1); + int y0 = Mth::floor(box->y0); + int y1 = Mth::floor(box->y1 + 1); + int z0 = Mth::floor(box->z0); + int z1 = Mth::floor(box->z1 + 1); + + if (box->x0 < 0) x0--; + if (box->y0 < 0) y0--; + if (box->z0 < 0) z0--; + + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + for (int z = z0; z < z1; z++) + { + if( !hasChunkAt(x,y,z) ) return true; // If we don't have it, it might be liquid... + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile != NULL && tile->material->isLiquid()) + { + return true; + } + } + return false; +} + + +bool Level::containsFireTile(AABB *box) +{ + int x0 = Mth::floor(box->x0); + int x1 = Mth::floor(box->x1 + 1); + int y0 = Mth::floor(box->y0); + int y1 = Mth::floor(box->y1 + 1); + int z0 = Mth::floor(box->z0); + int z1 = Mth::floor(box->z1 + 1); + + if (hasChunksAt(x0, y0, z0, x1, y1, z1)) + { + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + for (int z = z0; z < z1; z++) + { + int t = getTile(x, y, z); + + if (t == Tile::fire_Id || t == Tile::lava_Id || t == Tile::calmLava_Id) return true; + } + } + return false; +} + + +bool Level::checkAndHandleWater(AABB *box, Material *material, shared_ptr e) +{ + int x0 = Mth::floor(box->x0); + int x1 = Mth::floor(box->x1 + 1); + + int y0 = Mth::floor(box->y0); + int y1 = Mth::floor(box->y1 + 1); + + int z0 = Mth::floor(box->z0); + int z1 = Mth::floor(box->z1 + 1); + + if (!hasChunksAt(x0, y0, z0, x1, y1, z1)) + { + return false; + } + + bool ok = false; + Vec3 *current = Vec3::newTemp(0, 0, 0); + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + for (int z = z0; z < z1; z++) + { + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile != NULL && tile->material == material) + { + double yt0 = y + 1 - LiquidTile::getHeight(getData(x, y, z)); + if (y1 >= yt0) + { + ok = true; + tile->handleEntityInside(this, x, y, z, e, current); + } + } + } + if (current->length() > 0) + { + current = current->normalize(); + double pow = 0.014; + e->xd += current->x * pow; + e->yd += current->y * pow; + e->zd += current->z * pow; + } + return ok; +} + + +bool Level::containsMaterial(AABB *box, Material *material) +{ + int x0 = Mth::floor(box->x0); + int x1 = Mth::floor(box->x1 + 1); + int y0 = Mth::floor(box->y0); + int y1 = Mth::floor(box->y1 + 1); + int z0 = Mth::floor(box->z0); + int z1 = Mth::floor(box->z1 + 1); + + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + for (int z = z0; z < z1; z++) + { + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile != NULL && tile->material == material) + { + return true; + } + } + return false; +} + + +bool Level::containsLiquid(AABB *box, Material *material) +{ + int x0 = Mth::floor(box->x0); + int x1 = Mth::floor(box->x1 + 1); + int y0 = Mth::floor(box->y0); + int y1 = Mth::floor(box->y1 + 1); + int z0 = Mth::floor(box->z0); + int z1 = Mth::floor(box->z1 + 1); + + for (int x = x0; x < x1; x++) + for (int y = y0; y < y1; y++) + for (int z = z0; z < z1; z++) + { + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile != NULL && tile->material == material) + { + int data = getData(x, y, z); + double yh1 = y + 1; + if (data < 8) + { + yh1 = y + 1 - data / 8.0; + } + if (yh1 >= box->y0) + { + return true; + } + } + } + return false; +} + + +shared_ptr Level::explode(shared_ptr source, double x, double y, double z, float r, bool destroyBlocks) +{ + return explode(source, x, y, z, r, false, destroyBlocks); +} + + +shared_ptr Level::explode(shared_ptr source, double x, double y, double z, float r, bool fire, bool destroyBlocks) +{ + shared_ptr explosion = shared_ptr( new Explosion(this, source, x, y, z, r) ); + explosion->fire = fire; + explosion->destroyBlocks = destroyBlocks; + explosion->explode(); + explosion->finalizeExplosion(true); + return explosion; +} + + +float Level::getSeenPercent(Vec3 *center, AABB *bb) +{ + double xs = 1.0 / ((bb->x1 - bb->x0) * 2 + 1); + double ys = 1.0 / ((bb->y1 - bb->y0) * 2 + 1); + double zs = 1.0 / ((bb->z1 - bb->z0) * 2 + 1); + int hits = 0; + int count = 0; + for (double xx = 0; xx <= 1; xx += xs) // 4J Stu - xx, yy and zz were floats, made them doubles to remove warnings + for (double yy = 0; yy <= 1; yy += ys) + for (double zz = 0; zz <= 1; zz += zs) + { + double x = bb->x0 + (bb->x1 - bb->x0) * xx; + double y = bb->y0 + (bb->y1 - bb->y0) * yy; + double z = bb->z0 + (bb->z1 - bb->z0) * zz; + HitResult *res = clip(Vec3::newTemp(x, y, z), center); + if ( res == NULL) hits++; + delete res; + count++; + } + + return hits / (float) count; +} + + +bool Level::extinguishFire(shared_ptr player, int x, int y, int z, int face) +{ + if (face == 0) y--; + if (face == 1) y++; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + + if (getTile(x, y, z) == Tile::fire_Id) + { + levelEvent(player, LevelEvent::SOUND_FIZZ, x, y, z, 0); + setTile(x, y, z, 0); + return true; + } + return false; +} + +/* +shared_ptr Level::findSubclassOf(Entity::Class *entityClass) +{ + return shared_ptr(); +} +*/ + + +wstring Level::gatherStats() +{ + wchar_t buf[64]; + EnterCriticalSection(&m_entitiesCS); + swprintf(buf,64,L"All:%d",this->entities.size()); + LeaveCriticalSection(&m_entitiesCS); + return wstring(buf); +} + + +wstring Level::gatherChunkSourceStats() +{ + return chunkSource->gatherStats(); +} + + +shared_ptr Level::getTileEntity(int x, int y, int z) +{ + if (y >= Level::maxBuildHeight) + { + return nullptr; + } + LevelChunk *lc = getChunk(x >> 4, z >> 4); + if (lc != NULL) return lc->getTileEntity(x & 15, y, z & 15); + + if (lc != NULL) + { + shared_ptr tileEntity = lc->getTileEntity(x & 15, y, z & 15); + + if (tileEntity == NULL) + { + EnterCriticalSection(&m_tileEntityListCS); + for( AUTO_VAR(it, pendingTileEntities.begin()); it != pendingTileEntities.end(); it++ ) + { + shared_ptr e = *it; + + if (!e->isRemoved() && e->x == x && e->y == y && e->z == z) + { + tileEntity = e; + break; + } + } + LeaveCriticalSection(&m_tileEntityListCS); + } + return tileEntity; + } + + return nullptr; +} + + +void Level::setTileEntity(int x, int y, int z, shared_ptr tileEntity) +{ + if (tileEntity != NULL && !tileEntity->isRemoved()) + { + EnterCriticalSection(&m_tileEntityListCS); + if (updatingTileEntities) + { + tileEntity->x = x; + tileEntity->y = y; + tileEntity->z = z; + pendingTileEntities.push_back(tileEntity); + } + else + { + tileEntityList.push_back(tileEntity); + + LevelChunk *lc = getChunk(x >> 4, z >> 4); + if (lc != NULL) lc->setTileEntity(x & 15, y, z & 15, tileEntity); + } + LeaveCriticalSection(&m_tileEntityListCS); + } + + +} + + +void Level::removeTileEntity(int x, int y, int z) +{ + EnterCriticalSection(&m_tileEntityListCS); + shared_ptr te = getTileEntity(x, y, z); + if (te != NULL && updatingTileEntities) + { + te->setRemoved(); + AUTO_VAR(it, find(pendingTileEntities.begin(), pendingTileEntities.end(), te )); + if( it != pendingTileEntities.end() ) + { + pendingTileEntities.erase(it); + } + } + else + { + if (te != NULL) + { + AUTO_VAR(it, find(pendingTileEntities.begin(), pendingTileEntities.end(), te )); + if( it != pendingTileEntities.end() ) + { + pendingTileEntities.erase(it); + } + AUTO_VAR(it2, find(tileEntityList.begin(), tileEntityList.end(), te)); + if( it2 != tileEntityList.end() ) + { + tileEntityList.erase(it2); + } + } + LevelChunk *lc = getChunk(x >> 4, z >> 4); + if (lc != NULL) lc->removeTileEntity(x & 15, y, z & 15); + } + LeaveCriticalSection(&m_tileEntityListCS); +} + +void Level::markForRemoval(shared_ptr entity) +{ + tileEntitiesToUnload.push_back(entity); +} + +bool Level::isSolidRenderTile(int x, int y, int z) +{ + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile == NULL) return false; + + // 4J - addition here to make rendering big blocks of leaves more efficient. Normally leaves never consider themselves as solid, so + // blocks of leaves will have all sides of each block completely visible. Changing to consider as solid if this block is surrounded by + // other leaves (or solid things). This is paired with another change in Tile::getTexture which makes such solid tiles actually visibly solid (these + // textures exist already for non-fancy graphics). Note: this tile-specific code is here rather than making some new virtual method in the tiles, + // for the sake of efficiency - I don't imagine we'll be doing much more of this sort of thing + + if( tile->id == Tile::leaves_Id ) + { + int axo[6] = { 1,-1, 0, 0, 0, 0}; + int ayo[6] = { 0, 0, 1,-1, 0, 0}; + int azo[6] = { 0, 0, 0, 0, 1,-1}; + for( int i = 0; i < 6; i++ ) + { + int t = getTile(x + axo[i], y + ayo[i] , z + azo[i]); + if( ( t != Tile::leaves_Id ) && ( ( Tile::tiles[t] == NULL ) || !Tile::tiles[t]->isSolidRender() ) ) + { + return false; + } + } + + return true; + } + return tile->isSolidRender(!isClientSide); +} + + +bool Level::isSolidBlockingTile(int x, int y, int z) +{ + return Tile::isSolidBlockingTile(getTile(x, y, z)); +} + +/** + * This method does the same as isSolidBlockingTile, except it will not + * check the tile if the coordinates is in an unloaded or empty chunk. This + * is to help vs the problem of "popping" torches in SMP. + */ + +bool Level::isSolidBlockingTileInLoadedChunk(int x, int y, int z, bool valueIfNotLoaded) +{ + if (x < -MAX_LEVEL_SIZE || z < -MAX_LEVEL_SIZE || x >= MAX_LEVEL_SIZE || z >= MAX_LEVEL_SIZE) + { + return valueIfNotLoaded; + } + LevelChunk *chunk = chunkSource->getChunk(x >> 4, z >> 4); + if (chunk == NULL || chunk->isEmpty()) + { + return valueIfNotLoaded; + } + + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile == NULL) return false; + return tile->material->isSolidBlocking() && tile->isCubeShaped(); +} + +// 4J - brought forward from 1.3.2 +bool Level::isTopSolidBlocking(int x, int y, int z) +{ + // Temporary workaround until tahgs per-face solidity is finished + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile == NULL) return false; + + if (tile->material->isSolidBlocking() && tile->isCubeShaped()) return true; + if (dynamic_cast(tile) != NULL) + { + return (getData(x, y, z) & StairTile::UPSIDEDOWN_BIT) == StairTile::UPSIDEDOWN_BIT; + } + if (dynamic_cast(tile) != NULL) + { + return (getData(x, y, z) & HalfSlabTile::TOP_SLOT_BIT) == HalfSlabTile::TOP_SLOT_BIT; + } + if (dynamic_cast(tile) != NULL) return (getData(x, y, z) & TopSnowTile::HEIGHT_MASK) == TopSnowTile::MAX_HEIGHT + 1; + return false; +} + +void Level::updateSkyBrightness() +{ + int newDark = this->getOldSkyDarken(1); + if (newDark != skyDarken) + { + skyDarken = newDark; + } +} + +void Level::setSpawnSettings(bool spawnEnemies, bool spawnFriendlies) +{ + this->spawnEnemies = spawnEnemies; + this->spawnFriendlies = spawnFriendlies; +} + +void Level::tick() +{ + PIXBeginNamedEvent(0,"Weather tick"); + tickWeather(); + PIXEndNamedEvent(); +} + +void Level::prepareWeather() +{ + if (levelData->isRaining()) + { + this->rainLevel = 1; + if (levelData->isThundering()) + { + this->thunderLevel = 1; + } + } +} + + +void Level::tickWeather() +{ + if (dimension->hasCeiling) return; + +#ifndef _FINAL_BUILD + // debug setting added to disable weather + if(app.DebugSettingsOn()) + { + if(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<setThundering(false); + levelData->setThunderTime(random->nextInt(TICKS_PER_DAY * 7) + TICKS_PER_DAY / 2); + levelData->setRaining(false); + levelData->setRainTime(random->nextInt(TICKS_PER_DAY * 7) + TICKS_PER_DAY / 2); + } + } +#endif + + if (lightningTime > 0) + { + lightningTime--; + } + + int thunderTime = levelData->getThunderTime(); + if (thunderTime <= 0) + { + if (levelData->isThundering()) + { + levelData->setThunderTime(random->nextInt(20 * 60 * 10) + 20 * 60 * 3); + } + else + { + levelData->setThunderTime(random->nextInt(TICKS_PER_DAY * 7) + TICKS_PER_DAY / 2); + } + } + else + { + thunderTime--; + levelData->setThunderTime(thunderTime); + if (thunderTime <= 0) + { + levelData->setThundering(!levelData->isThundering()); + } + } + + int rainTime = levelData->getRainTime(); + if (rainTime <= 0) + { + if (levelData->isRaining()) + { + levelData->setRainTime(random->nextInt(TICKS_PER_DAY / 2) + TICKS_PER_DAY / 2); + } + else + { + levelData->setRainTime(random->nextInt(TICKS_PER_DAY * 7) + TICKS_PER_DAY / 2); + } + } + else + { + rainTime--; + levelData->setRainTime(rainTime); + if (rainTime <= 0) + { + levelData->setRaining(!levelData->isRaining()); + } +/* if( !levelData->isRaining() ) + { + levelData->setRaining(true); + }*/ + } + + oRainLevel = rainLevel; + if (levelData->isRaining()) + { + rainLevel += 0.01; + } + else + { + rainLevel -= 0.01; + } + if (rainLevel < 0) rainLevel = 0; + if (rainLevel > 1) rainLevel = 1; + + oThunderLevel = thunderLevel; + if (levelData->isThundering()) + { + thunderLevel += 0.01; + } + else + { + thunderLevel -= 0.01; + } + if (thunderLevel < 0) thunderLevel = 0; + if (thunderLevel > 1) thunderLevel = 1; +} + +void Level::toggleDownfall() +{ + // this will trick the tickWeather method to toggle rain next tick + levelData->setRainTime(1); +} + +void Level::buildAndPrepareChunksToPoll() +{ +#if 0 + AUTO_VAR(itEnd, players.end()); + for (AUTO_VAR(it, players.begin()); it != itEnd; it++) + { + shared_ptr player = *it; + int xx = Mth::floor(player->x / 16); + int zz = Mth::floor(player->z / 16); + + int r = CHUNK_POLL_RANGE; + for (int x = -r; x <= r; x++) + for (int z = -r; z <= r; z++) + { + chunksToPoll.insert(ChunkPos(x + xx, z + zz)); + } + } +#else + // 4J - rewritten to add chunks interleaved by player, and to add them from the centre outwards. We're going to be + // potentially adding less creatures than the original so that our count stays consistent with number of players added, so + // we want to make sure as best we can that the ones we do add are near the active players + int playerCount = (int)players.size(); + int *xx = new int[playerCount]; + int *zz = new int[playerCount]; + for (int i = 0; i < playerCount; i++) + { + shared_ptr player = players[i]; + xx[i] = Mth::floor(player->x / 16); + zz[i] = Mth::floor(player->z / 16); + chunksToPoll.insert(ChunkPos(xx[i], zz[i] )); + } + + for( int r = 1; r <= 9; r++ ) + { + for( int l = 0; l < ( r * 2 ) ; l++ ) + { + for( int i = 0; i < playerCount; i++ ) + { + chunksToPoll.insert(ChunkPos( ( xx[i] - r ) + l , ( zz[i] - r ) ) ); + chunksToPoll.insert(ChunkPos( ( xx[i] + r ) , ( zz[i] - r ) + l ) ); + chunksToPoll.insert(ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r ) ) ); + chunksToPoll.insert(ChunkPos( ( xx[i] - r ) , ( zz[i] + r ) - l ) ); + } + } + } + delete [] xx; + delete [] zz; +#endif + + if (delayUntilNextMoodSound > 0) delayUntilNextMoodSound--; + + // 4J Stu - Added 1.2.3, but not sure if we want to do it + //util.Timer.push("playerCheckLight"); + //// randomly check areas around the players + //if (!players.isEmpty()) { + // int select = random.nextInt(players.size()); + // Player player = players.get(select); + // int px = Mth.floor(player.x) + random.nextInt(11) - 5; + // int py = Mth.floor(player.y) + random.nextInt(11) - 5; + // int pz = Mth.floor(player.z) + random.nextInt(11) - 5; + // checkLight(px, py, pz); + //} + //util.Timer.pop(); +} + +void Level::tickClientSideTiles(int xo, int zo, LevelChunk *lc) +{ + //lc->tick(); // 4J - brought this lighting update forward from 1.8.2 + + if (delayUntilNextMoodSound == 0) + { + randValue = randValue * 3 + addend; + int val = (randValue >> 2); + int x = (val & 15); + int z = ((val >> 8) & 15); + int y = ((val >> 16) & genDepthMinusOne); + + int id = lc->getTile(x, y, z); + x += xo; + z += zo; + if (id == 0 && this->getDaytimeRawBrightness(x, y, z) <= random->nextInt(8) && getBrightness(LightLayer::Sky, x, y, z) <= 0) + { + shared_ptr player = getNearestPlayer(x + 0.5, y + 0.5, z + 0.5, 8); + if (player != NULL && player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) > 2 * 2) + { + // 4J-PB - Fixed issue with cave audio event having 2 sounds at 192k +#ifdef _XBOX + this->playSound(x + 0.5, y + 0.5, z + 0.5,eSoundType_AMBIENT_CAVE_CAVE2, 0.7f, 0.8f + random->nextFloat() * 0.2f); +#else + this->playSound(x + 0.5, y + 0.5, z + 0.5,eSoundType_AMBIENT_CAVE_CAVE, 0.7f, 0.8f + random->nextFloat() * 0.2f); +#endif + delayUntilNextMoodSound = random->nextInt(20 * 60 * 10) + 20 * 60 * 5; + } + } + } + + // 4J Stu - Added 1.2.3, but do we need it? + //lc->checkNextLight(); +} + +void Level::tickTiles() +{ + buildAndPrepareChunksToPoll(); +} + +bool Level::shouldFreezeIgnoreNeighbors(int x, int y, int z) +{ + return shouldFreeze(x, y, z, false); +} + +bool Level::shouldFreeze(int x, int y, int z) +{ + return shouldFreeze(x, y, z, true); +} + +bool Level::shouldFreeze(int x, int y, int z, bool checkNeighbors) +{ + Biome *biome = getBiome(x, z); + float temp = biome->getTemperature(); + if (temp > 0.15f) return false; + + if (y >= 0 && y < maxBuildHeight && getBrightness(LightLayer::Block, x, y, z) < 10) + { + int current = getTile(x, y, z); + if ((current == Tile::calmWater_Id || current == Tile::water_Id) && getData(x, y, z) == 0) + { + if (!checkNeighbors) return true; + + bool surroundedByWater = true; + if (surroundedByWater && getMaterial(x - 1, y, z) != Material::water) surroundedByWater = false; + if (surroundedByWater && getMaterial(x + 1, y, z) != Material::water) surroundedByWater = false; + if (surroundedByWater && getMaterial(x, y, z - 1) != Material::water) surroundedByWater = false; + if (surroundedByWater && getMaterial(x, y, z + 1) != Material::water) surroundedByWater = false; + if (!surroundedByWater) return true; + } + } + return false; +} + +bool Level::shouldSnow(int x, int y, int z) +{ + Biome *biome = getBiome(x, z); + float temp = biome->getTemperature(); + if (temp > 0.15f) return false; + + + if (y >= 0 && y < maxBuildHeight && getBrightness(LightLayer::Block, x, y, z) < 10) + { + int below = getTile(x, y - 1, z); + int current = getTile(x, y, z); + if (current == 0) + { + if (Tile::topSnow->mayPlace(this, x, y, z) && (below != 0 && below != Tile::ice_Id && Tile::tiles[below]->material->blocksMotion())) + { + return true; + } + } + } + + return false; +} + + +void Level::checkLight(int x, int y, int z, bool force, bool rootOnlyEmissive) // 4J added force, rootOnlyEmissive parameters +{ + if (!dimension->hasCeiling) checkLight(LightLayer::Sky, x, y, z, force, false); + checkLight(LightLayer::Block, x, y, z, force, rootOnlyEmissive); +} +int Level::getExpectedSkyColor(lightCache_t *cache, int oc, int x, int y , int z, int ct, int block) +{ + int expected = 0; + + if( block == 255 ) return 0; // 4J added as optimisation + + if (canSeeSky(x, y, z)) + { + expected = 15; + } + else + { + if (block == 0) block = 1; + + // 4J - changed this to attempt to get all 6 brightnesses of neighbours in a single call, as an optimisation + int b[6]; + b[0] = getBrightnessCached(cache, LightLayer::Sky, x - 1, y, z); + b[1] = getBrightnessCached(cache, LightLayer::Sky, x + 1, y, z); + b[2] = getBrightnessCached(cache, LightLayer::Sky, x, y - 1, z); + b[3] = getBrightnessCached(cache, LightLayer::Sky, x, y + 1, z); + b[4] = getBrightnessCached(cache, LightLayer::Sky, x, y, z - 1); + b[5] = getBrightnessCached(cache, LightLayer::Sky, x, y, z + 1); + for( int i = 0; i < 6; i++ ) + { + if( ( b[i] - block ) > expected ) expected = b[i] - block; + } + } + + return expected; +} + +int Level::getExpectedBlockColor(lightCache_t *cache, int oc, int x, int y, int z, int ct, int block, bool propagatedOnly) +{ + int expected = propagatedOnly ? 0 : getEmissionCached(cache, ct, x, y, z); + + if( block >= 15 ) return expected; // 4J added as optimisation + + // 4J - changed this to attempt to get all 6 brightnesses of neighbours in a single call, as an optimisation + int b[6]; + b[0] = getBrightnessCached(cache, LightLayer::Block, x - 1, y, z); + b[1] = getBrightnessCached(cache, LightLayer::Block, x + 1, y, z); + b[2] = getBrightnessCached(cache, LightLayer::Block, x, y - 1, z); + b[3] = getBrightnessCached(cache, LightLayer::Block, x, y + 1, z); + b[4] = getBrightnessCached(cache, LightLayer::Block, x, y, z - 1); + b[5] = getBrightnessCached(cache, LightLayer::Block, x, y, z + 1); + for( int i = 0; i < 6; i++ ) + { + if( ( b[i] - block ) > expected ) expected = b[i] - block; + } + + return expected; +} + +inline int GetIndex(int x, int y, int z) +{ + return ( ( x & 15 ) << 8 ) | ( ( y & 15 ) << 4 ) | ( z & 15 ); +} + +// 4J - Made changes here so that lighting goes through a cache, if enabled for this thread +void Level::checkLight(LightLayer::variety layer, int xc, int yc, int zc, bool force, bool rootOnlyEmissive) +{ + lightCache_t *cache = (lightCache_t *)TlsGetValue(tlsIdxLightCache); + __uint64 cacheUse = 0; + + if( force ) + { + // 4J - special mode added so we can do lava lighting updates without having all neighbouring chunks loaded in + if (!hasChunksAt(xc, yc, zc, 0)) return; + } + else + { + // 4J - this is normal java behaviour + if (!hasChunksAt(xc, yc, zc, 17)) return; + } + +#if 0 + ///////////////////////////////////////////////////////////////////////////////////////////// + // Get the frequency of the timer + LARGE_INTEGER qwTicksPerSec, qwTime, qwNewTime, qwDeltaTime1, qwDeltaTime2; + float fElapsedTime1 = 0.0f; + float fElapsedTime2 = 0.0f; + QueryPerformanceFrequency( &qwTicksPerSec ); + float fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart; + + QueryPerformanceCounter( &qwTime ); + ///////////////////////////////////////////////////////////////////////////////////////////// +#endif + + EnterCriticalSection(&m_checkLightCS); + +#ifdef __PSVITA__ + // AP - only clear the one array element required to check if something has changed + cachewritten = false; + if( cache != NULL ) + { + int idx; + if( !(yc & 0xffffff00) ) + { + idx = GetIndex(xc, yc, zc); + cache[idx] = 0; + idx = GetIndex(xc - 1, yc, zc); + cache[idx] = 0; + idx = GetIndex(xc + 1, yc, zc); + cache[idx] = 0; + idx = GetIndex(xc, yc, zc - 1); + cache[idx] = 0; + idx = GetIndex(xc, yc, zc + 1); + cache[idx] = 0; + } + if( !((yc-1) & 0xffffff00) ) + { + idx = GetIndex(xc, yc - 1, zc); + cache[idx] = 0; + } + if( !((yc+1) & 0xffffff00) ) + { + idx = GetIndex(xc, yc + 1, zc); + cache[idx] = 0; + } + } +#else + initCache(cache); +#endif + + // If we're in cached mode, then use memory allocated after the cached data itself for the toCheck array, in an attempt to make both that & the other cached data sit on the CPU L2 cache better. + + int *toCheck; + if( cache == NULL ) + { + toCheck = toCheckLevel; + } + else + { + toCheck = (int *)(cache + (16*16*16)); + } + + int tcp = 0; + int tcc = 0; + //int darktcc = 0; + + + // 4J - added + int minXZ = - (dimension->getXZSize() * 16 ) / 2; + int maxXZ = (dimension->getXZSize() * 16 ) / 2 - 1; + if( ( xc > maxXZ ) || ( xc < minXZ ) || ( zc > maxXZ ) || ( zc < minXZ ) ) + { + LeaveCriticalSection(&m_checkLightCS); + return; + } + + // Lock 128K of cache (containing all the lighting cache + first 112K of toCheck array) on L2 to try and stop any cached data getting knocked out of L2 by other non-cached reads (or vice-versa) +// if( cache ) XLockL2(XLOCKL2_INDEX_TITLE, cache, 128 * 1024, XLOCKL2_LOCK_SIZE_1_WAY, 0 ); + + { + int cc = getBrightnessCached(cache, layer, xc, yc, zc); + int ex = 0; + { + int ct = 0; + int block = getBlockingCached(cache, layer, &ct, xc, yc, zc); + if (block == 0) block = 1; + + int expected = 0; + if (layer == LightLayer::Sky) + { + expected = getExpectedSkyColor(cache, cc, xc, yc, zc, ct, block); + } + else + { + expected = getExpectedBlockColor(cache, cc, xc, yc, zc, ct, block, false); + } + + ex = expected; + + } + +#ifdef __PSVITA__ + // AP - we only need to memset the entire array if we discover something has changed + if( ex != cc && cache ) + { + lightCache_t old[7]; + if( !(yc & 0xffffff00) ) + { + old[0] = cache[GetIndex(xc, yc, zc)]; + old[1] = cache[GetIndex(xc - 1, yc, zc)]; + old[2] = cache[GetIndex(xc + 1, yc, zc)]; + old[5] = cache[GetIndex(xc, yc, zc - 1)]; + old[6] = cache[GetIndex(xc, yc, zc + 1)]; + } + if( !((yc-1) & 0xffffff00) ) + { + old[3] = cache[GetIndex(xc, yc - 1, zc)]; + } + if( !((yc+1) & 0xffffff00) ) + { + old[4] = cache[GetIndex(xc, yc + 1, zc)]; + } + + XMemSet128(cache,0,16*16*16*sizeof(lightCache_t)); + + if( !(yc & 0xffffff00) ) + { + cache[GetIndex(xc, yc, zc)] = old[0]; + cache[GetIndex(xc - 1, yc, zc)] = old[1]; + cache[GetIndex(xc + 1, yc, zc)] = old[2]; + cache[GetIndex(xc, yc, zc - 1)] = old[5]; + cache[GetIndex(xc, yc, zc + 1)] = old[6]; + } + if( !((yc-1) & 0xffffff00) ) + { + cache[GetIndex(xc, yc - 1, zc)] = old[3]; + } + if( !((yc+1) & 0xffffff00) ) + { + cache[GetIndex(xc, yc + 1, zc)] = old[4]; + } + } +#endif + + if (ex > cc) + { + toCheck[tcc++] = ((32)) + ((32) << 6) + ((32) << 12); + } + else if (ex < cc) + { + // 4J - added tcn. This is the code that is run when checkLight has been called for a light source that has got darker / turned off. + // In the original version, after zeroing tiles brightnesses that are deemed to come from this light source, all the zeroed tiles are then passed to the next + // stage of the function to potentially have their brightnesses put back up again. We shouldn't need to consider All these tiles as starting points for this process, now just + // considering the edge tiles (defined as a tile where we have a neighbour that is brightner than can be explained by the original light source we are turning off) + int tcn = 0; + if (layer == LightLayer::Block || true) + { + toCheck[tcc++] = ((32)) + ((32) << 6) + ((32) << 12) + (cc << 18); + while (tcp < tcc) + { + int p = toCheck[tcp++]; + int x = ((p) & 63) - 32 + xc; + int y = ((p >> 6) & 63) - 32 + yc; + int z = ((p >> 12) & 63) - 32 + zc; + int cexp = ((p >> 18) & 15); + int o = getBrightnessCached(cache, layer, x, y, z); + if (o == cexp) + { + setBrightnessCached(cache, &cacheUse, layer, x, y, z, 0); + // cexp--; // 4J - removed, change from 1.2.3 + if (cexp > 0) + { + int xd = x - xc; + int yd = y - yc; + int zd = z - zc; + if (xd < 0) xd = -xd; + if (yd < 0) yd = -yd; + if (zd < 0) zd = -zd; + if (xd + yd + zd < 17) + { + bool edge = false; + for (int j = 0; j < 6; j++) + { + int flip = j % 2 * 2 - 1; + + int xx = x + ((j / 2) % 3 / 2) * flip; + int yy = y + ((j / 2 + 1) % 3 / 2) * flip; + int zz = z + ((j / 2 + 2) % 3 / 2) * flip; + + // 4J - added - don't let this lighting creep out of the normal fixed world and into the infinite water chunks beyond + if( ( xx > maxXZ ) || ( xx < minXZ ) || ( zz > maxXZ ) || ( zz < minXZ ) ) continue; + if( ( yy < 0 ) || ( yy >= maxBuildHeight ) ) continue; + + o = getBrightnessCached(cache, layer, xx, yy, zz); + // 4J - some changes here brought forward from 1.2.3 + int block = getBlockingCached(cache, layer, NULL, xx, yy, zz); + if (block == 0) block = 1; + if ((o == cexp - block) && (tcc < (32 * 32 * 32))) // 4J - 32 * 32 * 32 was toCheck.length + { + toCheck[tcc++] = (((xx - xc) + 32)) + (((yy - yc) + 32) << 6) + (((zz - zc) + 32) << 12) + ((cexp - block) << 18); + } + else + { + // 4J - added - keep track of which tiles form the edge of the region we are zeroing + if( o > ( cexp - block ) ) + { + edge = true; + } + } + } + // 4J - added - keep track of which tiles form the edge of the region we are zeroing - can store over the original elements in the array because tcn must be <= tcp + if( edge == true ) + { + toCheck[tcn++] = p; + } + } + } + + } + } + } + tcp = 0; +// darktcc = tcc; /////////////////////////////////////////////////// + tcc = tcn; // 4J added - we've moved all the edge tiles to the start of the array, so only need to process these now. The original processes all tcc tiles again in the next section + } + } + + while (tcp < tcc) + { + int p = toCheck[tcp++]; + int x = ((p) & 63) - 32 + xc; + int y = ((p >> 6) & 63) - 32 + yc; + int z = ((p >> 12) & 63) - 32 + zc; + + // If force is set, then this is being used to in a special mode to try and light lava tiles as chunks are being loaded in. In this case, we + // don't want a lighting update to drag in any neighbouring chunks that aren't loaded yet. + if( force ) + { + if( !hasChunkAt(x,y,z) ) + { + continue; + } + } + + int c = getBrightnessCached(cache, layer, x, y, z); + int ct = 0; + int block = getBlockingCached(cache, layer, &ct, x, y, z); + if (block == 0) block = 1; + + int expected = 0; + if (layer == LightLayer::Sky) + { + expected = getExpectedSkyColor(cache, c, x, y, z, ct, block); + } + else + { + // If rootOnlyEmissive flag is set, then only consider the starting tile to be possibly emissive. + bool propagatedOnly = false; + if( rootOnlyEmissive ) + { + propagatedOnly = ( x != xc ) || ( y != yc ) || ( z != zc ); + } + expected = getExpectedBlockColor(cache, c, x, y, z, ct, block, propagatedOnly); + } + + if (expected != c) + { + setBrightnessCached(cache, &cacheUse, layer, x, y, z, expected); + + if (expected > c) + { + int xd = x - xc; + int yd = y - yc; + int zd = z - zc; + if (xd < 0) xd = -xd; + if (yd < 0) yd = -yd; + if (zd < 0) zd = -zd; + if (xd + yd + zd < 17 && tcc < (32 * 32 * 32) - 6) // 4J - 32 * 32 * 32 was toCheck.length + { + // 4J - added extra checks here to stop lighting updates moving out of the actual fixed world and into the infinite water chunks + if( ( x - 1 ) >= minXZ ) { if (getBrightnessCached(cache, layer, x - 1, y, z) < expected) toCheck[tcc++] = (((x - 1 - xc) + 32)) + (((y - yc) + 32) << 6) + (((z - zc) + 32) << 12); } + if( ( x + 1 ) <= maxXZ ) { if (getBrightnessCached(cache, layer, x + 1, y, z) < expected) toCheck[tcc++] = (((x + 1 - xc) + 32)) + (((y - yc) + 32) << 6) + (((z - zc) + 32) << 12); } + if( ( y - 1 ) >= 0 ) { if (getBrightnessCached(cache, layer, x, y - 1, z) < expected) toCheck[tcc++] = (((x - xc) + 32)) + (((y - 1 - yc) + 32) << 6) + (((z - zc) + 32) << 12); } + if( ( y + 1 ) < maxBuildHeight ) { if (getBrightnessCached(cache, layer, x, y + 1, z) < expected) toCheck[tcc++] = (((x - xc) + 32)) + (((y + 1 - yc) + 32) << 6) + (((z - zc) + 32) << 12); } + if( ( z - 1 ) >= minXZ ) { if (getBrightnessCached(cache, layer, x, y, z - 1) < expected) toCheck[tcc++] = (((x - xc) + 32)) + (((y - yc) + 32) << 6) + (((z - 1 - zc) + 32) << 12); } + if( ( z + 1 ) <= maxXZ ) { if (getBrightnessCached(cache, layer, x, y, z + 1) < expected) toCheck[tcc++] = (((x - xc) + 32)) + (((y - yc) + 32) << 6) + (((z + 1 - zc) + 32) << 12); } + } + } + } + } +// if( cache ) XUnlockL2(XLOCKL2_INDEX_TITLE); +#if 0 + QueryPerformanceCounter( &qwNewTime ); + qwDeltaTime1.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart; + qwTime = qwNewTime; +#endif + + flushCache(cache, cacheUse, layer); +#if 0 + ///////////////////////////////////////////////////////////////// + if( cache ) + { + QueryPerformanceCounter( &qwNewTime ); + qwDeltaTime2.QuadPart = qwNewTime.QuadPart - qwTime.QuadPart; + fElapsedTime1 = fSecsPerTick * ((FLOAT)(qwDeltaTime1.QuadPart)); + fElapsedTime2 = fSecsPerTick * ((FLOAT)(qwDeltaTime2.QuadPart)); + if( ( darktcc > 0 ) | ( tcc > 0 ) ) + { + printf("%d %d %d %f + %f = %f\n", darktcc, tcc, darktcc + tcc, fElapsedTime1 * 1000.0f, fElapsedTime2 * 1000.0f, ( fElapsedTime1 + fElapsedTime2 ) * 1000.0f); + } + } + ///////////////////////////////////////////////////////////////// +#endif + LeaveCriticalSection(&m_checkLightCS); + +} + + +bool Level::tickPendingTicks(bool force) +{ + return false; +} + +vector *Level::fetchTicksInChunk(LevelChunk *chunk, bool remove) +{ + return NULL; +} + + +vector > *Level::getEntities(shared_ptr except, AABB *bb) +{ + MemSect(40); + es.clear(); + int xc0 = Mth::floor((bb->x0 - 2) / 16); + int xc1 = Mth::floor((bb->x1 + 2) / 16); + int zc0 = Mth::floor((bb->z0 - 2) / 16); + int zc1 = Mth::floor((bb->z1 + 2) / 16); + +#ifdef __PSVITA__ +#ifdef _ENTITIES_RW_SECTION + // AP - RW critical sections are expensive so enter it here so we only have to call it once instead of X times + EnterCriticalRWSection(&LevelChunk::m_csEntities, false); +#else + EnterCriticalSection(&LevelChunk::m_csEntities); +#endif +#endif + + for (int xc = xc0; xc <= xc1; xc++) + for (int zc = zc0; zc <= zc1; zc++) + { + if (hasChunk(xc, zc)) + { + getChunk(xc, zc)->getEntities(except, bb, es); + } + } + MemSect(0); + +#ifdef __PSVITA__ +#ifdef _ENTITIES_RW_SECTION + LeaveCriticalRWSection(&LevelChunk::m_csEntities, false); +#else + LeaveCriticalSection(&LevelChunk::m_csEntities); +#endif +#endif + + return &es; +} + + +vector > *Level::getEntitiesOfClass(const type_info& baseClass, AABB *bb) +{ + int xc0 = Mth::floor((bb->x0 - 2) / 16); + int xc1 = Mth::floor((bb->x1 + 2) / 16); + int zc0 = Mth::floor((bb->z0 - 2) / 16); + int zc1 = Mth::floor((bb->z1 + 2) / 16); + vector > *es = new vector >(); + +#ifdef __PSVITA__ +#ifdef _ENTITIES_RW_SECTION + // AP - RW critical sections are expensive so enter it here so we only have to call it once instead of X times + EnterCriticalRWSection(&LevelChunk::m_csEntities, false); +#else + EnterCriticalSection(&LevelChunk::m_csEntities); +#endif +#endif + + for (int xc = xc0; xc <= xc1; xc++) + for (int zc = zc0; zc <= zc1; zc++) + { + if (hasChunk(xc, zc)) + { + getChunk(xc, zc)->getEntitiesOfClass(baseClass, bb, *es); + } + } + +#ifdef __PSVITA__ +#ifdef _ENTITIES_RW_SECTION + LeaveCriticalRWSection(&LevelChunk::m_csEntities, false); +#else + LeaveCriticalSection(&LevelChunk::m_csEntities); +#endif +#endif + + return es; +} + +shared_ptr Level::getClosestEntityOfClass(const type_info& baseClass, AABB *bb, shared_ptr source) +{ + vector > *entities = getEntitiesOfClass(baseClass, bb); + shared_ptr closest = nullptr; + double closestDistSqr = Double::MAX_VALUE; + //for (Entity entity : entities) + for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) + { + shared_ptr entity = *it; + if (entity == source) continue; + double distSqr = source->distanceToSqr(entity); + if (distSqr > closestDistSqr) continue; + closest = entity; + closestDistSqr = distSqr; + } + delete entities; + return closest; +} + +vector > Level::getAllEntities() +{ + EnterCriticalSection(&m_entitiesCS); + vector > retVec = entities; + LeaveCriticalSection(&m_entitiesCS); + return retVec; +} + + +void Level::tileEntityChanged(int x, int y, int z, shared_ptr te) +{ + if (this->hasChunkAt(x, y, z)) + { + getChunkAt(x, z)->markUnsaved(); + } +} + +#if 0 +unsigned int Level::countInstanceOf(BaseObject::Class *clas) +{ + unsigned int count = 0; + EnterCriticalSection(&m_entitiesCS); + AUTO_VAR(itEnd, entities.end()); + for (AUTO_VAR(it, entities.begin()); it != itEnd; it++) + { + shared_ptr e = *it;//entities.at(i); + if (clas->isAssignableFrom(e->getClass())) count++; + } + LeaveCriticalSection(&m_entitiesCS); + + return count; +} +#endif + +// 4J - added - more limited (but faster) version of above, used to count water animals, animals, monsters for the mob spawner +// singleType flag should be true if we are just trying to match eINSTANCEOF exactly, and false if it is a eINSTANCEOF from a group (eTYPE_WATERANIMAL, eTYPE_ANIMAL, eTYPE_MONSTER) +unsigned int Level::countInstanceOf(eINSTANCEOF clas, bool singleType, unsigned int *protectedCount/* = NULL*/, unsigned int *couldWanderCount/* = NULL*/) +{ + unsigned int count = 0; + if( protectedCount ) *protectedCount = 0; + if( couldWanderCount ) *couldWanderCount = 0; + EnterCriticalSection(&m_entitiesCS); + AUTO_VAR(itEnd, entities.end()); + for (AUTO_VAR(it, entities.begin()); it != itEnd; it++) + { + shared_ptr e = *it;//entities.at(i); + if( singleType ) + { + if (e->GetType() == clas) + { + if ( protectedCount && e->isDespawnProtected() ) + { + (*protectedCount)++; + } + + if ( couldWanderCount && e->couldWander() ) + { + (*couldWanderCount)++; + } + + count++; + } + } + else + { + if (e->GetType() & clas) count++; + } + } + LeaveCriticalSection(&m_entitiesCS); + + return count; +} + +unsigned int Level::countInstanceOfInRange(eINSTANCEOF clas, bool singleType, int range, int x, int y, int z) +{ + unsigned int count = 0; + EnterCriticalSection(&m_entitiesCS); + AUTO_VAR(itEnd, entities.end()); + for (AUTO_VAR(it, entities.begin()); it != itEnd; it++) + { + shared_ptr e = *it;//entities.at(i); + + float sd = e->distanceTo(x,y,z); + if (sd * sd > range * range) + { + continue; + } + + if( singleType ) + { + if (e->GetType() == clas) + { + count++; + } + } + else + { + if (e->GetType() & clas) + { + count++; + } + } + } + LeaveCriticalSection(&m_entitiesCS); + + return count; +} + +void Level::addEntities(vector > *list) +{ + //entities.addAll(list); + EnterCriticalSection(&m_entitiesCS); + entities.insert(entities.end(), list->begin(), list->end()); + AUTO_VAR(itEnd, list->end()); + bool deleteDragons = false; + for (AUTO_VAR(it, list->begin()); it != itEnd; it++) + { + entityAdded(*it); + + // 4J Stu - Special change to remove duplicate enderdragons that a previous bug might have produced + if( (*it)->GetType() == eTYPE_ENDERDRAGON) + { + deleteDragons = true; + } + } + + if(deleteDragons) + { + deleteDragons = false; + for(AUTO_VAR(it, entities.begin()); it != entities.end(); ++it) + { + // 4J Stu - Special change to remove duplicate enderdragons that a previous bug might have produced + if( (*it)->GetType() == eTYPE_ENDERDRAGON) + { + if(deleteDragons) + { + (*it)->remove(); + } + else + { + deleteDragons = true; + } + } + } + } + LeaveCriticalSection(&m_entitiesCS); +} + + +void Level::removeEntities(vector > *list) +{ + //entitiesToRemove.addAll(list); + entitiesToRemove.insert(entitiesToRemove.end(), list->begin(), list->end()); +} + +bool Level::mayPlace(int tileId, int x, int y, int z, bool ignoreEntities, int face, shared_ptr ignoreEntity) +{ + int targetType = getTile(x, y, z); + Tile *targetTile = Tile::tiles[targetType]; + + Tile *tile = Tile::tiles[tileId]; + + AABB *aabb = tile->getAABB(this, x, y, z); + if (ignoreEntities) aabb = NULL; + if (aabb != NULL && !isUnobstructed(aabb, ignoreEntity)) return false; + if (targetTile != NULL && + (targetTile == Tile::water || targetTile == Tile::calmWater || targetTile == Tile::lava || + targetTile == Tile::calmLava || targetTile == Tile::fire || targetTile->material->isReplaceable())) targetTile = NULL; + if (targetTile != NULL && targetTile->material == Material::decoration && tile == Tile::anvil) return true; + if (tileId > 0 && targetTile == NULL) + { + if (tile->mayPlace(this, x, y, z, face)) + { + return true; + } + } + return false; +} + + +int Level::getSeaLevel() +{ + return seaLevel; +} + + +Path *Level::findPath(shared_ptr from, shared_ptr to, float maxDist, bool canPassDoors, bool canOpenDoors, bool avoidWater, bool canFloat) +{ + int x = Mth::floor(from->x); + int y = Mth::floor(from->y + 1); + int z = Mth::floor(from->z); + + int r = (int) (maxDist + 16); + int x1 = x - r; + int y1 = y - r; + int z1 = z - r; + int x2 = x + r; + int y2 = y + r; + int z2 = z + r; + Region region = Region(this, x1, y1, z1, x2, y2, z2); + Path *path = (PathFinder(®ion, canPassDoors, canOpenDoors, avoidWater, canFloat)).findPath(from.get(), to.get(), maxDist); + return path; +} + + +Path *Level::findPath(shared_ptr from, int xBest, int yBest, int zBest, float maxDist, bool canPassDoors, bool canOpenDoors, bool avoidWater, bool canFloat) +{ + int x = Mth::floor(from->x); + int y = Mth::floor(from->y); + int z = Mth::floor(from->z); + + int r = (int) (maxDist + 8); + int x1 = x - r; + int y1 = y - r; + int z1 = z - r; + int x2 = x + r; + int y2 = y + r; + int z2 = z + r; + Region region = Region(this, x1, y1, z1, x2, y2, z2); + Path *path = (PathFinder(®ion, canPassDoors, canOpenDoors, avoidWater, canFloat)).findPath(from.get(), xBest, yBest, zBest, maxDist); + return path; +} + + +bool Level::getDirectSignal(int x, int y, int z, int dir) +{ + int t = getTile(x, y, z); + if (t == 0) return false; + return Tile::tiles[t]->getDirectSignal(this, x, y, z, dir); +} + + +bool Level::hasDirectSignal(int x, int y, int z) +{ + if (getDirectSignal(x, y - 1, z, 0)) return true; + if (getDirectSignal(x, y + 1, z, 1)) return true; + if (getDirectSignal(x, y, z - 1, 2)) return true; + if (getDirectSignal(x, y, z + 1, 3)) return true; + if (getDirectSignal(x - 1, y, z, 4)) return true; + if (getDirectSignal(x + 1, y, z, 5)) return true; + return false; +} + + +bool Level::getSignal(int x, int y, int z, int dir) +{ + if (isSolidBlockingTile(x, y, z)) + { + return hasDirectSignal(x, y, z); + } + int t = getTile(x, y, z); + if (t == 0) return false; + return Tile::tiles[t]->getSignal(this, x, y, z, dir); +} + + +bool Level::hasNeighborSignal(int x, int y, int z) +{ + if (getSignal(x, y - 1, z, 0)) return true; + if (getSignal(x, y + 1, z, 1)) return true; + if (getSignal(x, y, z - 1, 2)) return true; + if (getSignal(x, y, z + 1, 3)) return true; + if (getSignal(x - 1, y, z, 4)) return true; + if (getSignal(x + 1, y, z, 5)) return true; + return false; +} + +// 4J Stu - Added maxYDist param +shared_ptr Level::getNearestPlayer(shared_ptr source, double maxDist, double maxYDist /*= -1*/) +{ + return getNearestPlayer(source->x, source->y, source->z, maxDist, maxYDist); +} + +// 4J Stu - Added maxYDist param +shared_ptr Level::getNearestPlayer(double x, double y, double z, double maxDist, double maxYDist /*= -1*/) +{ + MemSect(21); + double best = -1; + shared_ptr result = nullptr; + AUTO_VAR(itEnd, players.end()); + for (AUTO_VAR(it, players.begin()); it != itEnd; it++) + { + shared_ptr p = *it;//players.at(i); + double dist = p->distanceToSqr(x, y, z); + + // Allow specifying shorter distances in the vertical + if(maxYDist > 0 && abs(p->y - y) > maxYDist) continue; + + // 4J Stu - Added check that this player is still alive + if ((maxDist < 0 || dist < maxDist * maxDist) && (best == -1 || dist < best) && p->isAlive() ) + { + best = dist; + result = p; + } + } + MemSect(0); + return result; +} + +shared_ptr Level::getNearestPlayer(double x, double z, double maxDist) +{ + double best = -1; + shared_ptr result = nullptr; + AUTO_VAR(itEnd, players.end()); + for (AUTO_VAR(it, players.begin()); it != itEnd; it++) + { + shared_ptr p = *it; + double dist = p->distanceToSqr(x, p->y, z); + if ((maxDist < 0 || dist < maxDist * maxDist) && (best == -1 || dist < best)) + { + best = dist; + result = p; + } + } + return result; +} + +shared_ptr Level::getNearestAttackablePlayer(shared_ptr source, double maxDist) +{ + return getNearestAttackablePlayer(source->x, source->y, source->z, maxDist); +} + +shared_ptr Level::getNearestAttackablePlayer(double x, double y, double z, double maxDist) +{ + double best = -1; + + shared_ptr result = nullptr; + AUTO_VAR(itEnd, players.end()); + for (AUTO_VAR(it, players.begin()); it != itEnd; it++) + { + shared_ptr p = *it; + + if (p->abilities.invulnerable) + { + continue; + } + + double dist = p->distanceToSqr(x, y, z); + double visibleDist = maxDist; + + // decrease the max attackable distance if the target player + // is sneaking or invisible + if (p->isSneaking()) + { + visibleDist *= .8f; + } + if (p->isInvisible()) + { + float coverPercentage = p->getArmorCoverPercentage(); + if (coverPercentage < .1f) + { + coverPercentage = .1f; + } + visibleDist *= (.7f * coverPercentage); + } + + // 4J Stu - Added check that this player is still alive and privilege check + if ((visibleDist < 0 || dist < visibleDist * visibleDist) && (best == -1 || dist < best) && p->isAlive() && !p->hasInvisiblePrivilege()) + { + best = dist; + result = p; + } + } + return result; +} + +shared_ptr Level::getPlayerByName(const wstring& name) +{ + AUTO_VAR(itEnd, players.end()); + for (AUTO_VAR(it, players.begin()); it != itEnd; it++) + { + if (name.compare( (*it)->name) == 0) + { + return *it; //players.at(i); + } + } + return shared_ptr(); +} + +shared_ptr Level::getPlayerByUUID(const wstring& name) +{ + AUTO_VAR(itEnd, players.end()); + for (AUTO_VAR(it, players.begin()); it != itEnd; it++) + { + if (name.compare( (*it)->getUUID() ) == 0) + { + return *it; //players.at(i); + } + } + return shared_ptr(); +} + +// 4J Stu - Removed in 1.2.3 ? +byteArray Level::getBlocksAndData(int x, int y, int z, int xs, int ys, int zs, bool includeLighting/* = true*/) +{ + byteArray result( xs * ys * zs * 5 / 2 ); + int xc0 = x >> 4; + int zc0 = z >> 4; + int xc1 = (x + xs - 1) >> 4; + int zc1 = (z + zs - 1) >> 4; + + int p = 0; + + int y0 = y; + int y1 = y + ys; + if (y0 < 0) y0 = 0; + if (y1 > Level::maxBuildHeight) y1 = Level::maxBuildHeight; + for (int xc = xc0; xc <= xc1; xc++) + { + int x0 = x - xc * 16; + int x1 = x + xs - xc * 16; + if (x0 < 0) x0 = 0; + if (x1 > 16) x1 = 16; + for (int zc = zc0; zc <= zc1; zc++) + { + int z0 = z - zc * 16; + int z1 = z + zs - zc * 16; + if (z0 < 0) z0 = 0; + if (z1 > 16) z1 = 16; + p = getChunk(xc, zc)->getBlocksAndData(&result, x0, y0, z0, x1, y1, z1, p, includeLighting); + } + } + + return result; +} + +// 4J Stu - Removed in 1.2.3 ? +void Level::setBlocksAndData(int x, int y, int z, int xs, int ys, int zs, byteArray data, bool includeLighting/* = true*/) +{ + int xc0 = x >> 4; + int zc0 = z >> 4; + int xc1 = (x + xs - 1) >> 4; + int zc1 = (z + zs - 1) >> 4; + + int p = 0; + + int y0 = y; + int y1 = y + ys; + if (y0 < 0) y0 = 0; + if (y1 > Level::maxBuildHeight) y1 = Level::maxBuildHeight; + for (int xc = xc0; xc <= xc1; xc++) + { + int x0 = x - xc * 16; + int x1 = x + xs - xc * 16; + if (x0 < 0) x0 = 0; + if (x1 > 16) x1 = 16; + for (int zc = zc0; zc <= zc1; zc++) + { + int z0 = z - zc * 16; + int z1 = z + zs - zc * 16; + if (z0 < 0) z0 = 0; + if (z1 > 16) z1 = 16; + LevelChunk *lc = getChunk(xc, zc); + // 4J Stu - Unshare before we make any changes incase the server is already another step ahead of us + // Fix for #7904 - Gameplay: Players can dupe torches by throwing them repeatedly into water. + // This is quite expensive so only actually do it if we are hosting, online, and the update will actually + // change something + bool forceUnshare = false; + if( g_NetworkManager.IsHost() && isClientSide ) + { + forceUnshare = lc->testSetBlocksAndData(data, x0, y0, z0, x1, y1, z1, p); + } + if( forceUnshare ) + { + int size = (x1 - x0 ) * ( y1 - y0 ) * ( z1 - z0 ); + PIXBeginNamedEvent(0,"Chunk data unsharing %d\n", size); + lc->stopSharingTilesAndData(); + PIXEndNamedEvent(); + } + if(p < data.length) p = lc->setBlocksAndData(data, x0, y0, z0, x1, y1, z1, p, includeLighting); + setTilesDirty(xc * 16 + x0, y0, zc * 16 + z0, xc * 16 + x1, y1, zc * 16 + z1); + + PIXBeginNamedEvent(0,"Chunk data sharing\n"); + if( g_NetworkManager.IsHost() && isClientSide ) + { + lc->startSharingTilesAndData(); + } + PIXEndNamedEvent(); + } + } +} + + +void Level::disconnect(bool sendDisconnect /*= true*/) +{ +} + + +void Level::checkSession() +{ + levelStorage->checkSession(); +} + + +void Level::setTime(__int64 time) +{ + // 4J : WESTY : Added to track game time played by players for other awards. + if (time != 0) // Ignore setting time to 0, done at level start and during tutorial. + { + // Determine step in time and ensure it is reasonable ( we only have an int to store the player stat). + __int64 timeDiff = time - levelData->getTime(); + + // debug setting added to keep it at day time +#ifndef _FINAL_BUILD + if(app.DebugSettingsOn()) + { + if(app.GetGameSettingsDebugMask(ProfileManager.GetPrimaryPad())&(1L<getTime(); + } + } +#endif + + if (timeDiff < 0) + { + timeDiff = 0; + } + else if (timeDiff > 100) + { + // Time differences of more than ~5 seconds are generally not real time passing so ignore (moving dimensions does this) + app.DebugPrintf("Level::setTime: Massive time difference, ignoring for time passed stat (%lli)\n", timeDiff); + timeDiff = 0; + } + + // Apply stat to each player. + if ( timeDiff > 0 && levelData->getTime() != -1 ) + { + AUTO_VAR(itEnd, players.end()); + for (vector >::iterator it = players.begin(); it != itEnd; it++) + { + (*it)->awardStat( GenericStats::timePlayed(), GenericStats::param_time(timeDiff) ); + } + } + } + + this->levelData->setTime(time); +} + +void Level::setOverrideTimeOfDay(__int64 time) +{ + m_timeOfDayOverride = time; +} + +__int64 Level::getSeed() +{ + return levelData->getSeed(); +} + + +__int64 Level::getTime() +{ + return levelData->getTime(); +} + + +Pos *Level::getSharedSpawnPos() +{ + return new Pos(levelData->getXSpawn(), levelData->getYSpawn(), levelData->getZSpawn()); +} + +void Level::setSpawnPos(int x, int y, int z) +{ + levelData->setSpawn(x, y, z); +} + +void Level::setSpawnPos(Pos *spawnPos) +{ + setSpawnPos(spawnPos->x, spawnPos->y, spawnPos->z); +} + + +void Level::ensureAdded(shared_ptr entity) +{ + int xc = Mth::floor(entity->x / 16); + int zc = Mth::floor(entity->z / 16); + int r = 2; + for (int x = xc - r; x <= xc + r; x++) + { + for (int z = zc - r; z <= zc + r; z++) + { + this->getChunk(x, z); + } + } + + //if (!entities.contains(entity)) + EnterCriticalSection(&m_entitiesCS); + if( find(entities.begin(), entities.end(), entity) == entities.end() ) + { + entities.push_back(entity); + } + LeaveCriticalSection(&m_entitiesCS); +} + + +bool Level::mayInteract(shared_ptr player, int xt, int yt, int zt, int content) +{ + return true; +} + + +void Level::broadcastEntityEvent(shared_ptr e, byte event) +{ +} + +ChunkSource *Level::getChunkSource() +{ + return chunkSource; +} + + +void Level::tileEvent(int x, int y, int z, int tile, int b0, int b1) +{ + if (tile > 0) Tile::tiles[tile]->triggerEvent(this, x, y, z, b0, b1); +} + + +LevelStorage *Level::getLevelStorage() +{ + return levelStorage.get(); +} + + +LevelData *Level::getLevelData() +{ + return levelData; +} + + +void Level::updateSleepingPlayerList() +{ +} + +float Level::getThunderLevel(float a) +{ + return (oThunderLevel + (thunderLevel - oThunderLevel) * a) * getRainLevel(a); +} + + +float Level::getRainLevel(float a) +{ + return oRainLevel + (rainLevel - oRainLevel) * a; +} + + +void Level::setRainLevel(float rainLevel) +{ + this->oRainLevel = rainLevel; + this->rainLevel = rainLevel; +} + + +bool Level::isThundering() +{ + return getThunderLevel(1) > 0.9; +} + + +bool Level::isRaining() +{ + return getRainLevel(1) > 0.2; +} + + +bool Level::isRainingAt(int x, int y, int z) +{ + if (!isRaining()) return false; + if (!canSeeSky(x, y, z)) return false; + if (getTopRainBlock(x, z) > y) return false; + +// 4J - changed to use new method of getting biomedata that caches results of rain & snow + if (biomeHasSnow(x, z)) return false; + return biomeHasRain(x, z); +} + +bool Level::isHumidAt(int x, int y, int z) +{ + Biome *biome = getBiome(x, z); + return biome->isHumid(); +} + + +void Level::setSavedData(const wstring& id, shared_ptr data) +{ + savedDataStorage->set(id, data); +} + + +shared_ptr Level::getSavedData(const type_info& clazz, const wstring& id) +{ + return savedDataStorage->get(clazz, id); +} + + +int Level::getFreeAuxValueFor(const wstring& id) +{ + return savedDataStorage->getFreeAuxValueFor(id); +} + +// 4J Added +int Level::getAuxValueForMap(PlayerUID xuid, int dimension, int centreXC, int centreZC, int scale) +{ + return savedDataStorage->getAuxValueForMap(xuid, dimension, centreXC, centreZC, scale); +} + +// void Level::globalLevelEvent(int type, int sourceX, int sourceY, int sourceZ, int data) +// { +// auto itEnd = listeners.end(); +// for (auto it = listeners.begin(); it != itEnd; it++) +// { +// (*it)->globalLevelEvent(type, sourceX, sourceY, sourceZ, data); +// } +// } + +void Level::levelEvent(int type, int x, int y, int z, int data) +{ + levelEvent(nullptr, type, x, y, z, data); +} + + +void Level::levelEvent(shared_ptr source, int type, int x, int y, int z, int data) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + (*it)->levelEvent(source, type, x, y, z, data); + } +} + +int Level::getMaxBuildHeight() +{ + return maxBuildHeight; +} + +int Level::getHeight() +{ + return dimension->hasCeiling ? genDepth : maxBuildHeight; +} + +Random *Level::getRandomFor(int x, int z, int blend) +{ + __int64 seed = (x * 341873128712l + z * 132897987541l) + getLevelData()->getSeed() + blend; + random->setSeed(seed); + return random; +} + +bool Level::updateLights() +{ + return false; +} + +TilePos *Level::findNearestMapFeature(const wstring& featureName, int x, int y, int z) +{ + return getChunkSource()->findNearestMapFeature(this, featureName, x, y, z); +} + +bool Level::isAllEmpty() +{ + return false; +} + +double Level::getHorizonHeight() +{ + if (levelData->getGenerator() == LevelType::lvl_flat) + { + return 0.0; + } + return 63.0; +} + +void Level::destroyTileProgress(int id, int x, int y, int z, int progress) +{ + AUTO_VAR(itEnd, listeners.end()); + for (AUTO_VAR(it, listeners.begin()); it != itEnd; it++) + { + (*it)->destroyTileProgress(id, x, y, z, progress); + } +} + +bool Level::useNewSeaLevel() +{ + return levelData->useNewSeaLevel(); +} + +bool Level::getHasBeenInCreative() +{ + return levelData->getHasBeenInCreative(); +} + +bool Level::isGenerateMapFeatures() +{ + return levelData->isGenerateMapFeatures(); +} + +int Level::getSaveVersion() +{ + return getLevelStorage()->getSaveFile()->getSaveVersion(); +} + +int Level::getOriginalSaveVersion() +{ + return getLevelStorage()->getSaveFile()->getOriginalSaveVersion(); +} + +// 4J - determine if a chunk has been done the post-post-processing stage. This happens when *its* neighbours have each been post-processed, and does some final lighting that can +// only really be done when the post-processing has placed all possible tiles into this chunk. +bool Level::isChunkPostPostProcessed(int x, int z) +{ + if( !hasChunk(x, z) ) return false; // This will occur for non-loaded chunks, not for edge chunks + + LevelChunk *lc = getChunk(x, z); + if( lc->isEmpty() ) return true; // Since we've already eliminated non-loaded chunks, this should only occur for edge chunks. Consider those as fully processed + + return (( lc->terrainPopulated & LevelChunk::sTerrainPostPostProcessed ) == LevelChunk::sTerrainPostPostProcessed); +} + +// 4J added - returns true if a chunk is fully, fully finalised - in that it can be sent to another machine. This is the case when all 8 neighbours of this chunk +// have not only been post-processed, but also had the post-post-processing done that they themselves can only do once Their 8 neighbours have been post-processed. +bool Level::isChunkFinalised(int x, int z) +{ + for( int xo = -1; xo <= 1; xo++ ) + for( int zo = -1; zo <= 1; zo++ ) + { + if( !isChunkPostPostProcessed(x + xo, z + zo) ) return false; + } + + return true; +} + +int Level::getUnsavedChunkCount() +{ + return m_unsavedChunkCount; +} + +void Level::incrementUnsavedChunkCount() +{ + ++m_unsavedChunkCount; +} + +void Level::decrementUnsavedChunkCount() +{ + --m_unsavedChunkCount; +} + +bool Level::canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType) +{ + int count = 0; + int max = 0; + if(spawnType == eSpawnType_Egg) + { + switch(type) + { + case eTYPE_VILLAGER: + count = countInstanceOf( eTYPE_VILLAGER, true); + max = MobCategory::MAX_XBOX_VILLAGERS_WITH_SPAWN_EGG; + break; + case eTYPE_CHICKEN: + count = countInstanceOf( eTYPE_CHICKEN, true); + max = MobCategory::MAX_XBOX_CHICKENS_WITH_SPAWN_EGG; + break; + case eTYPE_WOLF: + count = countInstanceOf( eTYPE_WOLF, true); + max = MobCategory::MAX_XBOX_WOLVES_WITH_SPAWN_EGG; + break; + case eTYPE_MUSHROOMCOW: + count = countInstanceOf( eTYPE_MUSHROOMCOW, true); + max = MobCategory::MAX_XBOX_MUSHROOMCOWS_WITH_SPAWN_EGG; + break; + case eTYPE_SQUID: + count = countInstanceOf( eTYPE_SQUID, true); + max = MobCategory::MAX_XBOX_SQUIDS_WITH_SPAWN_EGG; + break; + case eTYPE_SNOWMAN: + count = countInstanceOf( eTYPE_SNOWMAN, true); + max = MobCategory::MAX_XBOX_SNOWMEN; + break; + case eTYPE_VILLAGERGOLEM: + count = countInstanceOf( eTYPE_VILLAGERGOLEM, true); + max = MobCategory::MAX_XBOX_IRONGOLEM; + break; + default: + if((type & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) == eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) + { + count = countInstanceOf( eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false); + max = MobCategory::MAX_XBOX_ANIMALS_WITH_SPAWN_EGG; + } + else if( (type & eTYPE_MONSTER) == eTYPE_MONSTER) + { + count = countInstanceOf( eTYPE_MONSTER, false); + max = MobCategory::MAX_XBOX_MONSTERS_WITH_SPAWN_EGG; + } + }; + } + else if(spawnType == eSpawnType_Breed) + { + switch(type) + { + case eTYPE_VILLAGER: + count = countInstanceOf( eTYPE_VILLAGER, true); + max = MobCategory::MAX_VILLAGERS_WITH_BREEDING; + break; + case eTYPE_CHICKEN: + count = countInstanceOf( eTYPE_CHICKEN, true); + max = MobCategory::MAX_XBOX_CHICKENS_WITH_BREEDING; + break; + case eTYPE_WOLF: + count = countInstanceOf( eTYPE_WOLF, true); + max = MobCategory::MAX_XBOX_WOLVES_WITH_BREEDING; + break; + case eTYPE_MUSHROOMCOW: + count = countInstanceOf( eTYPE_MUSHROOMCOW, true); + max = MobCategory::MAX_XBOX_MUSHROOMCOWS_WITH_BREEDING; + break; + default: + if((type & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) == eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) + { + count = countInstanceOf( eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false); + max = MobCategory::MAX_XBOX_ANIMALS_WITH_BREEDING; + } + else if( (type & eTYPE_MONSTER) == eTYPE_MONSTER) + { + + } + break; + } + } + return count < max; +} \ No newline at end of file diff --git a/Minecraft.World/Level.h b/Minecraft.World/Level.h new file mode 100644 index 00000000..361a40e8 --- /dev/null +++ b/Minecraft.World/Level.h @@ -0,0 +1,537 @@ +#pragma once +using namespace std; +#include "LevelSource.h" +#include "LightLayer.h" +#include "ChunkPos.h" +#include "TickNextTickData.h" +#include "SavedData.h" +#include "Definitions.h" +#include "ParticleTypes.h" +#include "biome.h" +#include "C4JThread.h" + +#ifdef __PSVITA__ +#include "..\Minecraft.Client\PSVita\PSVitaExtras\CustomSet.h" +#endif + +// 4J Stu - This value should be big enough that we don't get any crashes causes by memory overwrites, +// however it does seem way too large for what is actually needed. Needs further investigation +#define LEVEL_CHUNKS_TO_UPDATE_MAX (19*19*8) + +class Vec3; +class ChunkSource; +class LevelListener; +class Explosion; +class Dimension; +class Material; +class TileEntity; +class AABB; +class Entity; +class SavedData; +class Pos; +class Player; +class LevelData; +class ProgressListener; +class Random; +class LevelStorage; +class SavedDataStorage; +class HitResult; +class Path; +class LevelSettings; +class Biome; +class Villages; +class VillageSiege; + +class Level : public LevelSource +{ +public: + static const int MAX_TICK_TILES_PER_TICK = 1000; + + // 4J Added + static const int MAX_GRASS_TICKS = 100; + static const int MAX_LAVA_TICKS = 100; + + +public: + static const int MAX_XBOX_BOATS = 40; // Max number of boats + static const int MAX_CONSOLE_MINECARTS = 40; + static const int MAX_DISPENSABLE_FIREBALLS = 200; + static const int MAX_DISPENSABLE_PROJECTILES = 300; + + static const int MAX_LEVEL_SIZE = 30000000; + static const int maxMovementHeight = 512; // 4J added + + static const int minBuildHeight = 0; // 4J - brought forward from 1.2.3 + static const int maxBuildHeight = 256; // 4J - brought forward from 1.2.3 + static const int genDepthBits = 7; + static const int genDepthBitsPlusFour = genDepthBits + 4; + static const int genDepth = 1 << genDepthBits; + static const int genDepthMinusOne = genDepth - 1; + static const int constSeaLevel = genDepth / 2 - 1; + + static const int CHUNK_TILE_COUNT = maxBuildHeight * 16 * 16; + static const int HALF_CHUNK_TILE_COUNT = CHUNK_TILE_COUNT/2; + static const int COMPRESSED_CHUNK_SECTION_HEIGHT = 128; + static const int COMPRESSED_CHUNK_SECTION_TILES = COMPRESSED_CHUNK_SECTION_HEIGHT * 16 * 16; // 4J Stu - Fixed size + + int seaLevel; + + // 4J - added, making instaTick flag use TLS so we can set it in the chunk rebuilding thread without upsetting the main game thread + static DWORD tlsIdx; + static DWORD tlsIdxLightCache; + static void enableLightingCache(); + static void destroyLightingCache(); + static bool getCacheTestEnabled(); + static bool getInstaTick(); + static void setInstaTick(bool enable); +// bool instaTick; // 4J - removed + + static const int MAX_BRIGHTNESS = 15; + static const int TICKS_PER_DAY = 20 * 60 * 20; // ORG:20*60*20 + +public: + CRITICAL_SECTION m_entitiesCS; // 4J added + + vector > entities; + +protected: + vector > entitiesToRemove; +public: + bool hasEntitiesToRemove(); // 4J added + bool m_bDisableAddNewTileEntities; // 4J Added + CRITICAL_SECTION m_tileEntityListCS; // 4J added + vector > tileEntityList; +private: + vector > pendingTileEntities; + vector > tileEntitiesToUnload; + bool updatingTileEntities; +public: + vector > players; + vector > globalEntities; + +private: + int cloudColor; + +public: + int skyDarken; + +protected: + int randValue; + +public: + int addend; + +protected: + float oRainLevel, rainLevel; + float oThunderLevel, thunderLevel; + int lightningTime; + +public: + int lightningBoltTime; + bool noNeighborUpdate; + int difficulty; + Random *random; + bool isNew; + Dimension *dimension; + +protected: + vector listeners; + +public: + ChunkSource *chunkSource; // 4J - changed to public +protected: + // This is the only shared_ptr ref to levelStorage - we need to keep this as long as at least one Level references it, + // to be able to cope with moving from dimension to dimension where the Level(Level *level, Dimension *dimension) ctor is used + shared_ptr levelStorage; + + LevelData *levelData; + +public: + bool isFindingSpawn; + SavedDataStorage *savedDataStorage; + shared_ptr villages; + VillageSiege *villageSiege; + +public: + Biome *getBiome(int x, int z); // 4J - brought forward from 1.2.3 + virtual BiomeSource *getBiomeSource(); + +private: + + // 4J Stu - Added these ctors to handle init of member variables + void _init(); + void _init(shared_ptrlevelStorage, const wstring& levelName, LevelSettings *levelSettings, Dimension *fixedDimension, bool doCreateChunkSource = true); + +public: + Level(shared_ptrlevelStorage, const wstring& name, Dimension *dimension, LevelSettings *levelSettings, bool doCreateChunkSource = true); + Level(Level *level, Dimension *dimension); + Level(shared_ptrlevelStorage, const wstring& levelName, LevelSettings *levelSettings); + Level(shared_ptrlevelStorage, const wstring& levelName, LevelSettings *levelSettings, Dimension *fixedDimension, bool doCreateChunkSource = true); + + virtual ~Level(); + +protected: + virtual ChunkSource *createChunkSource() = 0; + + virtual void initializeLevel(LevelSettings *settings); + +public: + virtual bool AllPlayersAreSleeping() { return false; } // 4J Added + + virtual void validateSpawn(); + int getTopTile(int x, int z); + +public: + virtual int getTile(int x, int y, int z); + virtual int getTileLightBlock(int x, int y, int z); + bool isEmptyTile(int x, int y, int z); + virtual bool isEntityTile(int x, int y, int z); + int getTileRenderShape(int x, int y, int z); + bool hasChunkAt(int x, int y, int z); + bool hasChunksAt(int x, int y, int z, int r); + bool hasChunksAt(int x0, int y0, int z0, int x1, int y1, int z1); + bool reallyHasChunkAt(int x, int y, int z); // 4J added + bool reallyHasChunksAt(int x, int y, int z, int r); // 4J added + bool reallyHasChunksAt(int x0, int y0, int z0, int x1, int y1, int z1); // 4J added + +public: + bool hasChunk(int x, int z); + bool reallyHasChunk(int x, int z ); // 4J added + +public: + LevelChunk *getChunkAt(int x, int z); + LevelChunk *getChunk(int x, int z); + virtual bool setTileAndDataNoUpdate(int x, int y, int z, int tile, int data); + virtual bool setTileAndDataNoUpdate(int x, int y, int z, int tile, int data, bool informClients); + virtual bool setTileNoUpdate(int x, int y, int z, int tile); + bool setTileNoUpdateNoLightCheck(int x, int y, int z, int tile); // 4J added + Material *getMaterial(int x, int y, int z); + virtual int getData(int x, int y, int z); + void setData(int x, int y, int z, int data, bool forceUpdate=false); // 4J added forceUpdate + virtual bool setDataNoUpdate(int x, int y, int z, int data); + bool setTile(int x, int y, int z, int tile); + bool setTileAndData(int x, int y, int z, int tile, int data); + void sendTileUpdated(int x, int y, int z); + +public: + virtual void tileUpdated(int x, int y, int z, int tile); + void lightColumnChanged(int x, int z, int y0, int y1); + void setTileDirty(int x, int y, int z); + void setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1); + void swap(int x1, int y1, int z1, int x2, int y2, int z2); + void updateNeighborsAt(int x, int y, int z, int tile); + +private: + void neighborChanged(int x, int y, int z, int type); + +public: + bool canSeeSky(int x, int y, int z); + int getDaytimeRawBrightness(int x, int y, int z); + int getRawBrightness(int x, int y, int z); + int getRawBrightness(int x, int y, int z, bool propagate); + bool isSkyLit(int x, int y, int z); + int getHeightmap(int x, int z); + void updateLightIfOtherThan(LightLayer::variety layer, int x, int y, int z, int expected); + int getBrightnessPropagate(LightLayer::variety layer, int x, int y, int z, int tileId); // 4J added tileId + void getNeighbourBrightnesses(int *brightnesses, LightLayer::variety layer, int x, int y, int z); // 4J added + int getBrightness(LightLayer::variety layer, int x, int y, int z); + void setBrightness(LightLayer::variety layer, int x, int y, int z, int brightness, bool noUpdateOnClient=false); // 4J added noUpdateOnClient + void setBrightnessNoUpdateOnClient(LightLayer::variety layer, int x, int y, int z, int brightness); // 4J added + +#ifdef _LARGE_WORLDS + typedef __uint64 lightCache_t; +#else + typedef unsigned int lightCache_t; +#endif + inline void setBrightnessCached(lightCache_t *cache, __uint64 *cacheUse, LightLayer::variety layer, int x, int y, int z, int brightness); + inline int getBrightnessCached(lightCache_t *cache, LightLayer::variety layer, int x, int y, int z); + inline int getEmissionCached(lightCache_t *cache, int ct, int x, int y, int z); + inline int getBlockingCached(lightCache_t *cache, LightLayer::variety layer, int *ct, int x, int y, int z); + void initCache(lightCache_t *cache); + void flushCache(lightCache_t *cache, __uint64 cacheUse, LightLayer::variety layer); + + bool cachewritten; + static const int LIGHTING_SHIFT = 24; + static const int BLOCKING_SHIFT = 20; + static const int EMISSION_SHIFT = 16; +#ifdef _LARGE_WORLDS + static const __int64 LIGHTING_WRITEBACK = 0x80000000LL; + static const __int64 EMISSION_VALID = 0x40000000LL; + static const __int64 BLOCKING_VALID = 0x20000000LL; + static const __int64 LIGHTING_VALID = 0x10000000LL; + static const lightCache_t POSITION_MASK = 0xffffffff0000ffffLL; +#else + static const int LIGHTING_WRITEBACK = 0x80000000; + static const int EMISSION_VALID = 0x40000000; + static const int BLOCKING_VALID = 0x20000000; + static const int LIGHTING_VALID = 0x10000000; + static const lightCache_t POSITION_MASK = 0x0000ffff; +#endif + + int cacheminx, cachemaxx, cacheminy, cachemaxy, cacheminz, cachemaxz; + void setTileBrightnessChanged(int x, int y, int z); + virtual int getLightColor(int x, int y, int z, int emitt, int tileId = -1); // 4J - brought forward from 1.8.2 + virtual float getBrightness(int x, int y, int z, int emitt); + virtual float getBrightness(int x, int y, int z); + bool isDay(); + HitResult *clip(Vec3 *a, Vec3 *b); + HitResult *clip(Vec3 *a, Vec3 *b, bool liquid); + HitResult *clip(Vec3 *a, Vec3 *b, bool liquid, bool solidOnly); + + virtual void playSound(shared_ptr entity, int iSound, float volume, float pitch); + virtual void playSound(double x, double y, double z, int iSound, float volume, float pitch, float fClipSoundDist=16.0f); + + virtual void playLocalSound(double x, double y, double z, int iSound, float volume, float pitch, float fClipSoundDist=16.0f); + + void playStreamingMusic(const wstring& name, int x, int y, int z); + void playMusic(double x, double y, double z, const wstring& string, float volume); + // 4J removed - void addParticle(const wstring& id, double x, double y, double z, double xd, double yd, double zd); + void addParticle(ePARTICLE_TYPE id, double x, double y, double z, double xd, double yd, double zd); // 4J added + virtual bool addGlobalEntity(shared_ptr e); + virtual bool addEntity(shared_ptr e); + +protected: + virtual void entityAdded(shared_ptr e); + virtual void entityRemoved(shared_ptr e); + virtual void playerRemoved(shared_ptr e); // 4J added + +public: + virtual void removeEntity(shared_ptr e); + void removeEntityImmediately(shared_ptr e); + void addListener(LevelListener *listener); + void removeListener(LevelListener *listener); + +private: + AABBList boxes; + +public: + AABBList *getCubes(shared_ptr source, AABB *box, bool noEntities=false, bool blockAtEdge=false); // 4J - added noEntities & blockAtEdge parameters + AABBList *getTileCubes(AABB *box, bool blockAtEdge); // 4J Stu - Brought forward from 12w36 to fix #46282 - TU5: Gameplay: Exiting the minecart in a tight corridor damages the player + int getOldSkyDarken(float a); // 4J - change brought forward from 1.8.2 + float getSkyDarken(float a); // 4J - change brought forward from 1.8.2 + Vec3 *getSkyColor(shared_ptr source, float a); + float getTimeOfDay(float a); + int getMoonPhase(float a); + float getSunAngle(float a); + Vec3 *getCloudColor(float a); + Vec3 *getFogColor(float a); + int getTopRainBlock(int x, int z); + int getTopSolidBlock(int x, int z); + bool biomeHasRain(int x, int z); // 4J added + bool biomeHasSnow(int x, int z); // 4J added + int getLightDepth(int x, int z); + float getStarBrightness(float a); + virtual void addToTickNextTick(int x, int y, int z, int tileId, int tickDelay); + virtual void forceAddTileTick(int x, int y, int z, int tileId, int tickDelay); + virtual void tickEntities(); + void addAllPendingTileEntities(vector< shared_ptr >& entities); + void tick(shared_ptr e); + virtual void tick(shared_ptr e, bool actual); + bool isUnobstructed(AABB *aabb); + bool isUnobstructed(AABB *aabb, shared_ptr ignore); + bool containsAnyBlocks(AABB *box); + bool containsAnyLiquid(AABB *box); + bool containsAnyLiquid_NoLoad(AABB *box); // 4J added + bool containsFireTile(AABB *box); + bool checkAndHandleWater(AABB *box, Material *material, shared_ptr e); + bool containsMaterial(AABB *box, Material *material); + bool containsLiquid(AABB *box, Material *material); + // 4J Stu - destroyBlocks param brought forward as part of fix for tnt cannons + shared_ptr explode(shared_ptr source, double x, double y, double z, float r, bool destroyBlocks); + virtual shared_ptr explode(shared_ptr source, double x, double y, double z, float r, bool fire, bool destroyBlocks); + float getSeenPercent(Vec3 *center, AABB *bb); + bool extinguishFire(shared_ptr player, int x, int y, int z, int face); + wstring gatherStats(); + wstring gatherChunkSourceStats(); + virtual shared_ptr getTileEntity(int x, int y, int z); + void setTileEntity(int x, int y, int z, shared_ptr tileEntity); + void removeTileEntity(int x, int y, int z); + void markForRemoval(shared_ptr entity); + virtual bool isSolidRenderTile(int x, int y, int z); + virtual bool isSolidBlockingTile(int x, int y, int z); + bool isSolidBlockingTileInLoadedChunk(int x, int y, int z, bool valueIfNotLoaded); + virtual bool isTopSolidBlocking(int x, int y, int z); // 4J - brought forward from 1.3.2 + +protected: + bool spawnEnemies; + bool spawnFriendlies; + +public: + // int xxo, yyo, zzo; + + void updateSkyBrightness(); + void setSpawnSettings(bool spawnEnemies, bool spawnFriendlies); + virtual void tick(); + +private: + void prepareWeather(); + +protected: + virtual void tickWeather(); + +private: + void stopWeather(); + +public: + void toggleDownfall(); + +protected: +#ifdef __PSVITA__ + // AP - See CustomSet.h for an explanation of this + CustomSet chunksToPoll; +#else + unordered_set chunksToPoll; +#endif + +private: + int delayUntilNextMoodSound; + static const int CHUNK_POLL_RANGE = 9; + static const int CHUNK_TILE_TICK_COUNT = 80; + static const int CHUNK_SECTION_TILE_TICK_COUNT = (CHUNK_TILE_TICK_COUNT / 8) + 1; + +protected: + virtual void buildAndPrepareChunksToPoll(); + virtual void tickClientSideTiles(int xo, int zo, LevelChunk *lc); + virtual void tickTiles(); + + // 4J - snow & ice checks brought forward from 1.2.3 +public: + bool shouldFreezeIgnoreNeighbors(int x, int y, int z); + bool shouldFreeze(int x, int y, int z); + bool shouldFreeze(int x, int y, int z, bool checkNeighbors); + bool shouldSnow(int x, int y, int z); + void checkLight(int x, int y, int z, bool force = false, bool rootOnlyEmissive = false); // 4J added force, rootOnlySource parameters +private: + int *toCheckLevel; + int getExpectedSkyColor(lightCache_t *cache, int oc, int x, int y , int z, int ct, int block); + int getExpectedBlockColor(lightCache_t *cache, int oc, int x, int y, int z, int ct, int block, bool propagatedOnly); // 4J added parameter +public: + void checkLight(LightLayer::variety layer, int xc, int yc, int zc, bool force = false, bool rootOnlyEmissive = false); // 4J added force, rootOnlySource parameters + +public: + virtual bool tickPendingTicks(bool force); + virtual vector *fetchTicksInChunk(LevelChunk *chunk, bool remove); + +private: + vector > es; + +public: + bool isClientSide; + + vector > *getEntities(shared_ptr except, AABB *bb); + vector > *getEntitiesOfClass(const type_info& baseClass, AABB *bb); + shared_ptr getClosestEntityOfClass(const type_info& baseClass, AABB *bb, shared_ptr source); + vector > getAllEntities(); + void tileEntityChanged(int x, int y, int z, shared_ptr te); +// unsigned int countInstanceOf(BaseObject::Class *clas); + unsigned int countInstanceOf(eINSTANCEOF clas, bool singleType, unsigned int *protectedCount = NULL, unsigned int *couldWanderCount = NULL); // 4J added + unsigned int countInstanceOfInRange(eINSTANCEOF clas, bool singleType, int range, int x, int y, int z); // 4J Added + void addEntities(vector > *list); + virtual void removeEntities(vector > *list); + bool mayPlace(int tileId, int x, int y, int z, bool ignoreEntities, int face, shared_ptr ignoreEntity); + int getSeaLevel(); + Path *findPath(shared_ptr from, shared_ptr to, float maxDist, bool canPassDoors, bool canOpenDoors, bool avoidWater, bool canFloat); + Path *findPath(shared_ptr from, int xBest, int yBest, int zBest, float maxDist, bool canPassDoors, bool canOpenDoors, bool avoidWater, bool canFloat); + bool getDirectSignal(int x, int y, int z, int dir); + bool hasDirectSignal(int x, int y, int z); + bool getSignal(int x, int y, int z, int dir); + bool hasNeighborSignal(int x, int y, int z); + // 4J Added maxYDist param + shared_ptr getNearestPlayer(shared_ptr source, double maxDist, double maxYDist = -1); + shared_ptr getNearestPlayer(double x, double y, double z, double maxDist, double maxYDist = -1); + shared_ptr getNearestPlayer(double x, double z, double maxDist); + shared_ptr getNearestAttackablePlayer(shared_ptr source, double maxDist); + shared_ptr getNearestAttackablePlayer(double x, double y, double z, double maxDist); + + shared_ptr getPlayerByName(const wstring& name); + shared_ptr getPlayerByUUID(const wstring& name); // 4J Added + byteArray getBlocksAndData(int x, int y, int z, int xs, int ys, int zs, bool includeLighting = true); + void setBlocksAndData(int x, int y, int z, int xs, int ys, int zs, byteArray data, bool includeLighting = true); + virtual void disconnect(bool sendDisconnect = true); + void checkSession(); + void setTime(__int64 time); + void setOverrideTimeOfDay(__int64 time); // 4J Added so we can override timeOfDay without changing tick time + __int64 getSeed(); + __int64 getTime(); + Pos *getSharedSpawnPos(); + void setSpawnPos(int x, int y, int z); + void setSpawnPos(Pos *spawnPos); + void ensureAdded(shared_ptr entity); + virtual bool mayInteract(shared_ptr player, int xt, int yt, int zt, int content); + virtual void broadcastEntityEvent(shared_ptr e, byte event); + ChunkSource *getChunkSource(); + virtual void tileEvent(int x, int y, int z, int tile, int b0, int b1); + LevelStorage *getLevelStorage(); + LevelData *getLevelData(); + virtual void updateSleepingPlayerList(); + bool useNewSeaLevel(); // 4J added + bool getHasBeenInCreative(); // 4J Added + bool isGenerateMapFeatures(); // 4J Added + int getSaveVersion(); + int getOriginalSaveVersion(); + float getThunderLevel(float a); + float getRainLevel(float a); + void setRainLevel(float rainLevel); + bool isThundering(); + bool isRaining(); + bool isRainingAt(int x, int y, int z); + bool isHumidAt(int x, int y, int z); + void setSavedData(const wstring& id, shared_ptr data); + shared_ptr getSavedData(const type_info& clazz, const wstring& id); + int getFreeAuxValueFor(const wstring& id); + void levelEvent(int type, int x, int y, int z, int data); + void levelEvent(shared_ptr source, int type, int x, int y, int z, int data); + int getMaxBuildHeight(); + int getHeight(); + Random *getRandomFor(int x, int z, int blend); + bool updateLights(); + virtual bool isAllEmpty(); + double getHorizonHeight() ; + void destroyTileProgress(int id, int x, int y, int z, int progress); + TilePos *findNearestMapFeature(const wstring& featureName, int x, int y, int z); + + // 4J Added + int getAuxValueForMap(PlayerUID xuid, int dimension, int centreXC, int centreZC, int scale); + + // 4J added + + + __int64 m_timeOfDayOverride; + + // 4J - optimisation - keep direct reference of underlying cache here + LevelChunk **chunkSourceCache; + int chunkSourceXZSize; + + // 4J - added for implementation of finite limit to number of item entities, tnt and falling block entities +public: + virtual bool newPrimedTntAllowed() { return true; } + virtual bool newFallingTileAllowed() { return true; } + + // 4J - added for new lighting from 1.8.2 + CRITICAL_SECTION m_checkLightCS; + +private: + int m_iHighestY; // 4J-PB - for the end portal in The End +public: + int GetHighestY() { return m_iHighestY;} + void SetHighestY(int iVal) { m_iHighestY=iVal;} + + bool isChunkFinalised(int x, int z); // 4J added + bool isChunkPostPostProcessed(int x, int z); // 4J added + +private: + int m_unsavedChunkCount; + +public: + int getUnsavedChunkCount(); + void incrementUnsavedChunkCount(); // 4J Added + void decrementUnsavedChunkCount(); // 4J Added + + enum ESPAWN_TYPE + { + eSpawnType_Egg, + eSpawnType_Breed, + }; + + bool canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType); +}; diff --git a/Minecraft.World/LevelChunk.cpp b/Minecraft.World/LevelChunk.cpp new file mode 100644 index 00000000..823c7c4c --- /dev/null +++ b/Minecraft.World/LevelChunk.cpp @@ -0,0 +1,2463 @@ +#include "stdafx.h" +#include "System.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.entity.monster.h" +#include "DataLayer.h" +#include "SparseLightStorage.h" +#include "BlockReplacements.h" +#include "LevelChunk.h" +#include "..\Minecraft.Client\MinecraftServer.h" +#include "..\Minecraft.Client\ServerLevel.h" +#include "..\Minecraft.Client\ServerChunkCache.h" +#include "..\Minecraft.Client\GameRenderer.h" + +#ifdef __PS3__ +#include "C4JSpursJob.h" +#endif //__PS3__ + + +#ifdef SHARING_ENABLED +CRITICAL_SECTION LevelChunk::m_csSharing; +#endif +#ifdef _ENTITIES_RW_SECTION + // AP - use a RW critical section so we can have multiple threads reading the same data to avoid a clash + CRITICAL_RW_SECTION LevelChunk::m_csEntities; +#else + CRITICAL_SECTION LevelChunk::m_csEntities; +#endif +CRITICAL_SECTION LevelChunk::m_csTileEntities; +bool LevelChunk::touchedSky = false; + +void LevelChunk::staticCtor() +{ +#ifdef SHARING_ENABLED + InitializeCriticalSection(&m_csSharing); +#endif +#ifdef _ENTITIES_RW_SECTION + InitializeCriticalRWSection(&m_csEntities); +#else + InitializeCriticalSection(&m_csEntities); +#endif + InitializeCriticalSection(&m_csTileEntities); +} + +void LevelChunk::init(Level *level, int x, int z) +{ + biomes = byteArray(16 * 16); + for(int i = 0; i < 16 * 16; i++ ) + { + biomes[i] = 0xff; + } +#ifdef _ENTITIES_RW_SECTION + EnterCriticalRWSection(&m_csEntities, true); +#else + EnterCriticalSection(&m_csEntities); +#endif + entityBlocks = new vector > *[ENTITY_BLOCKS_LENGTH]; +#ifdef _ENTITIES_RW_SECTION + LeaveCriticalRWSection(&m_csEntities, true); +#else + LeaveCriticalSection(&m_csEntities); +#endif + + terrainPopulated = 0; + m_unsaved = false; + lastSaveHadEntities = false; + lastSaveTime = 0; + dontSave = false; + loaded = false; + minHeight = 0; + hasGapsToCheck = false; + seenByPlayer = true; // 4J Stu - Always true + + // 4J Stu - Not using this + checkLightPosition = 0; //LIGHT_CHECK_MAX_POS; + + this->level = level; + this->x = x; + this->z = z; + MemSect(1); + heightmap = byteArray(16 * 16); +#ifdef _ENTITIES_RW_SECTION + EnterCriticalRWSection(&m_csEntities, true); +#else + EnterCriticalSection(&m_csEntities); +#endif + for (int i = 0; i < ENTITY_BLOCKS_LENGTH; i++) + { + entityBlocks[i] = new vector >(); + } +#ifdef _ENTITIES_RW_SECTION + LeaveCriticalRWSection(&m_csEntities, true); +#else + LeaveCriticalSection(&m_csEntities); +#endif + + MemSect(0); + + // Optimisation brought forward from 1.8.2, change from int to unsigned char & this special value changed from -999 to 255 + for(int i = 0; i < 16 * 16; i++ ) + { + rainHeights[i] = 255; + } + // 4J - lighting change brought forward from 1.8.2, introduced an array of bools called gapsToRecheck, which are now a single bit in array of nybble flags in this version + for(int i = 0; i < 8 * 16; i++ ) + { + columnFlags[i] = 0; + } + + // 4J added - to flag if any emissive tile has been added to this chunk (will be cleared when lighting has been successfully completed for this chunk). Defaulting to true + // as emissive things can be made and passed in the the initialisation block array. + emissiveAdded = true; + +#ifdef _LARGE_WORLDS + m_bUnloaded = false; // 4J Added + m_unloadedEntitiesTag = NULL; +#endif +} + +// This ctor is used for loading a save into +LevelChunk::LevelChunk(Level *level, int x, int z) : ENTITY_BLOCKS_LENGTH( Level::maxBuildHeight/16 ) +{ + init(level, x, z); + lowerBlocks = new CompressedTileStorage(); + lowerData = NULL; + lowerSkyLight = NULL; + lowerBlockLight = NULL; + serverTerrainPopulated = NULL; + + if(Level::maxBuildHeight > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + // Create all these as empty, as we may not be loading any data into them + upperBlocks = new CompressedTileStorage(true); + upperData = new SparseDataStorage(true); + upperSkyLight = new SparseLightStorage(true, true); + upperBlockLight = new SparseLightStorage(false, true); + } + else + { + upperBlocks = NULL; + upperData = NULL; + upperSkyLight = NULL; + upperBlockLight = NULL; + } + +#ifdef SHARING_ENABLED + sharingTilesAndData = false; +#endif +} + +// 4J - note that since we now compress the block storage, the parameter blocks is used as a source of data, but doesn't get used As the source data so needs +// to be deleted after calling this ctor. +LevelChunk::LevelChunk(Level *level, byteArray blocks, int x, int z) : ENTITY_BLOCKS_LENGTH( Level::maxBuildHeight/16 ) +{ + init(level, x, z); + + // We'll be creating this as "empty" when this ctor is called on the client, as a result of a chunk becoming visible (but we don't have the data yet for it). + // In this case, we want to keep memory usage down and so create all data as empty/compressed as possible. On the client we get the full data for the chunk as a single + // update in a block region update packet, and so there is a single point where it is good to compress the data. + bool createEmpty = ( blocks.data == NULL ); + + if( createEmpty ) + { + lowerBlocks = new CompressedTileStorage(true); + lowerData = new SparseDataStorage(true); + + lowerSkyLight = new SparseLightStorage(true, true); + lowerBlockLight = new SparseLightStorage(false, true); + } + else + { + lowerBlocks = new CompressedTileStorage(blocks,0); + lowerData = new SparseDataStorage(); + + // 4J - changed to new SpareLightStorage class for these + lowerSkyLight = new SparseLightStorage(true); + lowerBlockLight = new SparseLightStorage(false); + } +// skyLight = new DataLayer(blocks.length, level->depthBits); +// blockLight = new DataLayer(blocks.length, level->depthBits); + + if(Level::maxBuildHeight > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + if(blocks.length > Level::COMPRESSED_CHUNK_SECTION_TILES) upperBlocks = new CompressedTileStorage(blocks,Level::COMPRESSED_CHUNK_SECTION_TILES); + else upperBlocks = new CompressedTileStorage(true); + upperData = new SparseDataStorage(true); + upperSkyLight = new SparseLightStorage(true, true); + upperBlockLight = new SparseLightStorage(false, true); + } + else + { + upperBlocks = NULL; + upperData = NULL; + upperSkyLight = NULL; + upperBlockLight = NULL; + } + + serverTerrainPopulated = NULL; +#ifdef SHARING_ENABLED + sharingTilesAndData = false; +#endif +} + +// 4J - this ctor added to be able to make a levelchunk that shares its underlying block data between the server chunk cache & the multiplayer chunk cache. +// The original version this is shared from owns all the data that is shared into this copy, so it isn't deleted in the dtor. +LevelChunk::LevelChunk(Level *level, int x, int z, LevelChunk *lc) : ENTITY_BLOCKS_LENGTH( Level::maxBuildHeight/16 ) +{ + init(level, x, z); + + // 4J Stu - Copy over the biome data + memcpy(biomes.data,lc->biomes.data,biomes.length); + +#ifdef SHARING_ENABLED + lowerBlocks = lc->lowerBlocks; + lowerData = lc->lowerData; + lowerSkyLight = new SparseLightStorage( lc->lowerSkyLight ); + lowerBlockLight = new SparseLightStorage( lc->lowerBlockLight ); + upperBlocks = lc->upperBlocks; + upperData = lc->upperData; + upperSkyLight = new SparseLightStorage( lc->upperSkyLight ); + upperBlockLight = new SparseLightStorage( lc->upperBlockLight ); + + sharingTilesAndData = true; + serverTerrainPopulated = &lc->terrainPopulated; +#else + this->blocks = new CompressedTileStorage(lc->blocks); + this->data = new SparseDataStorage(lc->data); + this->skyLight = new SparseLightStorage(lc->skyLight); + this->blockLight = new SparseLightStorage(lc->blockLight); + serverTerrainPopulated = NULL; +#endif +} + +// 4J Added so we can track unsaved chunks better +void LevelChunk::setUnsaved(bool unsaved) +{ +#ifdef _LARGE_WORLDS + if(m_unsaved != unsaved) + { + if(unsaved) level->incrementUnsavedChunkCount(); + else level->decrementUnsavedChunkCount(); + } +#endif + m_unsaved = unsaved; +} + +void LevelChunk::stopSharingTilesAndData() +{ +#ifdef SHARING_ENABLED + EnterCriticalSection(&m_csSharing); + lastUnsharedTime = System::currentTimeMillis(); + if( !sharingTilesAndData ) + { + LeaveCriticalSection(&m_csSharing); + return; + } + + // If we've got a reference to a server chunk's terrainPopulated flag that this LevelChunk is sharing with, then don't consider unsharing + // if it hasn't been set. This is because post-processing things that update the server chunks won't actually cause the server to send any updates + // to the tiles that they alter, so they completely depend on the data not being shared for it to get from the server to here + if( ( serverTerrainPopulated ) && ( ( (*serverTerrainPopulated) & sTerrainPopulatedAllAffecting ) != sTerrainPopulatedAllAffecting ) ) + { + LeaveCriticalSection(&m_csSharing); + return; + } + + // If this is the empty chunk, then it will have a x & z of 0,0 - if we don't drop out here we'll end up unsharing the chunk at this location for no reason + if( isEmpty() ) + { + LeaveCriticalSection(&m_csSharing); + return; + } + + MemSect(47); + + // Changed to used compressed storage - these CTORs make deep copies of the storage passed as a parameter + lowerBlocks = new CompressedTileStorage( lowerBlocks ); + + // Changed to use new sparse data storage - this CTOR makes a deep copy of the storage passed as a parameter + lowerData = new SparseDataStorage(lowerData); + + if(Level::maxBuildHeight > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + upperBlocks = new CompressedTileStorage( upperBlocks ); + upperData = new SparseDataStorage(upperData); + } + else + { + upperBlocks = NULL; + upperData = NULL; + } + + /* + newDataLayer = new DataLayer(skyLight->data.length*2, level->depthBits); + XMemCpy(newDataLayer->data.data, skyLight->data.data, skyLight->data.length); + skyLight = newDataLayer; + + newDataLayer = new DataLayer(blockLight->data.length*2, level->depthBits); + XMemCpy(newDataLayer->data.data, blockLight->data.data, blockLight->data.length); + blockLight = newDataLayer; + */ + + sharingTilesAndData = false; + MemSect(0); + LeaveCriticalSection(&m_csSharing); +#endif +} + +// This is a slight variation on the normal start/stop sharing methods here as in general we aren't sharing lighting anymore. This method +// discards the client lighting information, and sets up new (non-shared) lighting to match the server. So generally like stop sharing, +// for the case where we're already not sharing +void LevelChunk::reSyncLighting() +{ +#ifdef SHARING_ENABLED + EnterCriticalSection(&m_csSharing); + + if( isEmpty() ) + { + LeaveCriticalSection(&m_csSharing); + return; + } + +#ifdef _LARGE_WORLDS + LevelChunk *lc = MinecraftServer::getInstance()->getLevel( level->dimension->id )->cache->getChunkLoadedOrUnloaded(x,z); +#else + LevelChunk *lc = MinecraftServer::getInstance()->getLevel( level->dimension->id )->cache->getChunk(x,z); +#endif + + GameRenderer::AddForDelete(lowerSkyLight); + lowerSkyLight = new SparseLightStorage( lc->lowerSkyLight ); + GameRenderer::FinishedReassigning(); + GameRenderer::AddForDelete(lowerBlockLight); + lowerBlockLight = new SparseLightStorage( lc->lowerBlockLight ); + GameRenderer::FinishedReassigning(); + + if(Level::maxBuildHeight > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + GameRenderer::AddForDelete(upperSkyLight); + upperSkyLight = new SparseLightStorage( lc->upperSkyLight ); + GameRenderer::FinishedReassigning(); + GameRenderer::AddForDelete(upperBlockLight); + upperBlockLight = new SparseLightStorage( lc->upperBlockLight ); + GameRenderer::FinishedReassigning(); + } + LeaveCriticalSection(&m_csSharing); +#endif +} + +void LevelChunk::startSharingTilesAndData(int forceMs) +{ +#ifdef SHARING_ENABLED + EnterCriticalSection(&m_csSharing); + if( sharingTilesAndData ) + { + LeaveCriticalSection(&m_csSharing); + return; + } + + // If this is the empty chunk, then it will have a x & z of 0,0 - we'll end up potentially loading the 0,0 block if we proceed. And it obviously + // doesn't make sense to go resharing the 0,0 block on behalf of an empty chunk either + if( isEmpty() ) + { + LeaveCriticalSection(&m_csSharing); + return; + } + +#ifdef _LARGE_WORLDS + LevelChunk *lc = MinecraftServer::getInstance()->getLevel( level->dimension->id )->cache->getChunkLoadedOrUnloaded(x,z); +#else + LevelChunk *lc = MinecraftServer::getInstance()->getLevel( level->dimension->id )->cache->getChunk(x,z); +#endif + + // In normal usage, chunks should only reshare if their local data matched that on the server. The forceMs parameter though can + // be used to force a share if resharing hasn't happened after a period of time + if( forceMs == 0 ) + { + // Normal behaviour - just check that the data matches, and don't start sharing data if it doesn't (yet) + if( !lowerBlocks->isSameAs(lc->lowerBlocks) || (upperBlocks && lc->upperBlocks && !upperBlocks->isSameAs(lc->upperBlocks) ) ) + { + LeaveCriticalSection(&m_csSharing); + return; + } + } + else + { + // Only force if it has been more than forceMs milliseconds since we last wanted to unshare this chunk + __int64 timenow = System::currentTimeMillis(); + if( ( timenow - lastUnsharedTime ) < forceMs ) + { + LeaveCriticalSection(&m_csSharing); + return; + } + } + + // Note - data that was shared isn't directly deleted here, as it might still be in use in the game render update thread. Let that thread + // delete it when it is safe to do so instead. + GameRenderer::AddForDelete(lowerBlocks); + lowerBlocks = lc->lowerBlocks; + GameRenderer::FinishedReassigning(); + + GameRenderer::AddForDelete(lowerData); + lowerData = lc->lowerData; + GameRenderer::FinishedReassigning(); + + if(Level::maxBuildHeight > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + GameRenderer::AddForDelete(upperBlocks); + upperBlocks = lc->upperBlocks; + GameRenderer::FinishedReassigning(); + + GameRenderer::AddForDelete(upperData); + upperData = lc->upperData; + GameRenderer::FinishedReassigning(); + } + + sharingTilesAndData = true; + LeaveCriticalSection(&m_csSharing); +#endif +} + +LevelChunk::~LevelChunk() +{ +#ifdef SHARING_ENABLED + if( !sharingTilesAndData ) +#endif + { + delete lowerData; + delete lowerBlocks; + if(upperData) delete upperData; + if(upperBlocks) delete upperBlocks; + } + + delete lowerSkyLight; + delete lowerBlockLight; + if(upperSkyLight) delete upperSkyLight; + if(upperBlockLight) delete upperBlockLight; + + delete[] heightmap.data; + + for(int i = 0; i < ENTITY_BLOCKS_LENGTH; ++i) + delete entityBlocks[i]; + delete[] entityBlocks; + + delete[] biomes.data; + +#ifdef _LARGE_WORLDS + delete m_unloadedEntitiesTag; +#endif +} + +bool LevelChunk::isAt(int x, int z) +{ + return x == this->x && z == this->z; +} + +int LevelChunk::getHeightmap(int x, int z) +{ + return heightmap[z << 4 | x] & 0xff; +} + +int LevelChunk::getHighestSectionPosition() +{ + return Level::maxBuildHeight - 16; + // 4J Stu - Unused + //for (int i = sections.length - 1; i >= 0; i--) { + // if (sections[i] != null) { // && !sections[i].isEmpty()) { + // return sections[i].getYPosition(); + // } + //} + //return 0; +} + +void LevelChunk::recalcBlockLights() +{ +} + + + +void LevelChunk::recalcHeightmapOnly() +{ +#ifdef __PSVITA__ + // AP - lets fetch ALL the chunk data at the same time for a good speed up + byteArray blockData = byteArray(Level::CHUNK_TILE_COUNT); + getBlockData(blockData); +#endif + + int min = Level::maxBuildHeight - 1; + for (int x = 0; x < 16; x++) + for (int z = 0; z < 16; z++) + { + rainHeights[x + (z << 4)] = 255; // 4J - changed from int to unsigned char & this special value changed from -999 to 255 + + int y = Level::maxBuildHeight - 1; + // int p = x << level->depthBitsPlusFour | z << level->depthBits; // 4J - removed +#ifdef __PSVITA__ + int Index = ( x << 11 ) + ( z << 7 ); + int offset = Level::COMPRESSED_CHUNK_SECTION_TILES; + y = 127; + while (y > 0 && Tile::lightBlock[blockData[Index + offset + (y - 1)]] == 0) // 4J - was blocks->get() was blocks[p + y - 1] + { + y--; + } + if( y == 0 ) + { + offset = 0; + y = 127; + while (y > 0 && Tile::lightBlock[blockData[Index + offset + (y - 1)]] == 0) // 4J - was blocks->get() was blocks[p + y - 1] + { + y--; + } + } + else + { + y += 128; + } +#else + CompressedTileStorage *blocks = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks; + while (y > 0 && Tile::lightBlock[blocks->get(x,(y - 1) % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z) & 0xff] == 0) // 4J - was blocks->get() was blocks[p + y - 1] + { + y--; + blocks = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks; + } +#endif + heightmap[z << 4 | x] = (byte) y; + if (y < min) min = y; + } + + this->minHeight = min; + this->setUnsaved(true); + +#ifdef __PSVITA__ + delete blockData.data; +#endif +} + +void LevelChunk::recalcHeightmap() +{ +#ifdef __PSVITA__ + // AP - lets fetch ALL the chunk data at the same time for a good speed up + byteArray blockData = byteArray(Level::CHUNK_TILE_COUNT); + getBlockData(blockData); +#endif + + int min = Level::maxBuildHeight - 1; + for (int x = 0; x < 16; x++) + for (int z = 0; z < 16; z++) + { + int y = Level::maxBuildHeight - 1; +// int p = x << level->depthBitsPlusFour | z << level->depthBits; // 4J - removed + +#ifdef __PSVITA__ + int Index = ( x << 11 ) + ( z << 7 ); + int offset = Level::COMPRESSED_CHUNK_SECTION_TILES; + y = 127; + while (y > 0 && Tile::lightBlock[blockData[Index + offset + (y - 1)]] == 0) // 4J - was blocks->get() was blocks[p + y - 1] + { + y--; + } + if( y == 0 ) + { + offset = 0; + y = 127; + while (y > 0 && Tile::lightBlock[blockData[Index + offset + (y - 1)]] == 0) // 4J - was blocks->get() was blocks[p + y - 1] + { + y--; + } + } + else + { + y += 128; + } +#else + CompressedTileStorage *blocks = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks; + while (y > 0 && Tile::lightBlock[blocks->get(x,(y-1) % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z) & 0xff] == 0) // 4J - was blocks->get() was blocks[p + y - 1] + { + y--; + blocks = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks; + } +#endif + heightmap[z << 4 | x] = (byte) y; + if (y < min) min = y; + + if (!level->dimension->hasCeiling) + { + int br = Level::MAX_BRIGHTNESS; + int yy = Level::maxBuildHeight - 1; +#ifdef __PSVITA__ + int offset = Level::COMPRESSED_CHUNK_SECTION_TILES; + SparseLightStorage *skyLight = upperSkyLight; + yy = 127; + do + { + br -= Tile::lightBlock[blockData[Index + offset + yy]]; // 4J - blocks->get() was blocks[p + yy] + if (br > 0) + { + skyLight->set(x, yy, z, br); + } + yy--; + } while (yy > 0 && br > 0); + + if( yy == 0 && br > 0 ) + { + offset = 0; + skyLight = lowerSkyLight; + yy = 127; + do + { + br -= Tile::lightBlock[blockData[Index + offset + yy]]; // 4J - blocks->get() was blocks[p + yy] + if (br > 0) + { + skyLight->set(x, yy, z, br); + } + yy--; + } while (yy > 0 && br > 0); + } +#else + CompressedTileStorage *blocks = yy >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks; + SparseLightStorage *skyLight = yy >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight; + do + { + br -= Tile::lightBlock[blocks->get(x,(yy % Level::COMPRESSED_CHUNK_SECTION_HEIGHT),z) & 0xff]; // 4J - blocks->get() was blocks[p + yy] + if (br > 0) + { + skyLight->set(x, (yy % Level::COMPRESSED_CHUNK_SECTION_HEIGHT), z, br); + } + yy--; + blocks = yy >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks; + skyLight = yy >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight; + } while (yy > 0 && br > 0); +#endif + } + } + + this->minHeight = min; + + for (int x = 0; x < 16; x++) + for (int z = 0; z < 16; z++) + { + lightGaps(x, z); + } + + this->setUnsaved(true); + +#ifdef __PSVITA__ + delete blockData.data; +#endif +} + +// 4J - this code is fully commented out in the java version, but we have reimplemented something here to try and light +// lava as chunks are created, as otherwise they get shared before being lit, and then their lighting gets updated on the client +// and causes framerate stutters. +void LevelChunk::lightLava() +{ + if( !emissiveAdded ) return; + + for (int x = 0; x < 16; x++) + for (int z = 0; z < 16; z++) + { +// int p = x << 11 | z << 7; // 4J - removed + int ymax = getHeightmap(x,z); + for (int y = 0; y < Level::COMPRESSED_CHUNK_SECTION_HEIGHT; y++) + { + CompressedTileStorage *blocks = lowerBlocks; + int emit = Tile::lightEmission[blocks->get(x,y,z)]; // 4J - blocks->get() was blocks[p + y] + if( emit > 0 ) + { +// printf("(%d,%d,%d)",this->x * 16 + x, y, this->z * 16 + z); + // We'll be calling this function for a lot of chunks as they are post-processed. For every chunk that is + // post-processed we're calling this for each of its neighbours in case some post-processing also created something + // that needed lighting outside the starting chunk. Because of this, do a quick test on any emissive blocks that have + // been added to see if checkLight has already been run on this particular block - this is straightforward to check + // as being emissive blocks they'll have their block brightness set to their lightEmission level in this case. + if( getBrightness(LightLayer::Block, x, y, z) < emit ) + { + level->checkLight( LightLayer::Block, this->x * 16 + x, y, this->z * 16 + z, true); + } + } + } + } + emissiveAdded = false; +} + +void LevelChunk::lightGaps(int x, int z) +{ + // 4J - lighting change brought forward from 1.8.2, introduced an array of bools called gapsToRecheck, which are now a single bit in array of nybbles in this version + int slot = ( x >> 1 ) | (z * 8); + int shift = ( x & 1 ) * 4; + columnFlags[slot] |= ( eColumnFlag_recheck << shift ); + hasGapsToCheck = true; +} +void LevelChunk::recheckGaps(bool bForce) +{ + // 4J added - otherwise we can end up doing a very broken kind of lighting since for an empty chunk, the heightmap is all zero, but it + // still has an x and z of 0 which means that the level->getHeightmap references in here find a real chunk near the origin, and then attempt + // to light massive gaps between the height of 0 and whatever heights are in those. + if( isEmpty() ) return; + + // 4J added + int minXZ = - (level->dimension->getXZSize() * 16 ) / 2; + int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1; + + // 4J - note - this test will currently return true for chunks at the edge of our world. Making further checks inside the loop now to address this issue. + if (level->hasChunksAt(x * 16 + 8, Level::maxBuildHeight / 2, z * 16 + 8, 16)) + { + for (int x = 0; x < 16; x++) + for (int z = 0; z < 16; z++) + { + int slot = ( x >> 1 ) | (z * 8); + int shift = ( x & 1 ) * 4; + if (bForce || ( columnFlags[slot] & ( eColumnFlag_recheck << shift ) ) ) + { + columnFlags[slot] &= ~( eColumnFlag_recheck << shift ); + int height = getHeightmap(x, z); + int xOffs = (this->x * 16) + x; + int zOffs = (this->z * 16) + z; + + // 4J - rewritten this to make sure that the minimum neighbour height which is calculated doesn't involve getting any heights from beyond the edge of the world, + // which can lead to large, very expensive, non-existent cliff edges to be lit + int nmin = level->getHeightmap(xOffs, zOffs); + if( xOffs - 1 >= minXZ ) + { + int n = level->getHeightmap(xOffs - 1, zOffs); + if ( n < nmin ) nmin = n; + } + if( xOffs + 1 <= maxXZ ) + { + int n = level->getHeightmap(xOffs + 1, zOffs); + if ( n < nmin ) nmin = n; + } + if( zOffs - 1 >= minXZ ) + { + int n = level->getHeightmap(xOffs, zOffs - 1); + if ( n < nmin ) nmin = n; + } + if( zOffs + 1 <= maxXZ ) + { + int n = level->getHeightmap(xOffs, zOffs + 1); + if ( n < nmin ) nmin = n; + } + lightGap(xOffs, zOffs, nmin); + + if( !bForce ) // 4J - if doing a full forced thing over every single column, we don't need to do these offset checks too + { + if( xOffs - 1 >= minXZ ) lightGap(xOffs - 1, zOffs, height); + if( xOffs + 1 <= maxXZ ) lightGap(xOffs + 1, zOffs, height); + if( zOffs - 1 >= minXZ ) lightGap(xOffs, zOffs - 1, height); + if( zOffs + 1 <= maxXZ ) lightGap(xOffs, zOffs + 1, height); + } + hasGapsToCheck = false; + } + } + } +} + + +void LevelChunk::lightGap(int x, int z, int source) +{ + int height = level->getHeightmap(x, z); + + if (height > source) + { + lightGap(x, z, source, height + 1); + } + else if (height < source) + { + lightGap(x, z, height, source + 1); + } +} + +void LevelChunk::lightGap(int x, int z, int y1, int y2) +{ + if (y2 > y1) + { + if (level->hasChunksAt(x, Level::maxBuildHeight / 2, z, 16)) + { + for (int y = y1; y < y2; y++) + { + level->checkLight(LightLayer::Sky, x, y, z); + } + this->setUnsaved(true); + } + } +} + +void LevelChunk::recalcHeight(int x, int yStart, int z) +{ + int yOld = heightmap[z << 4 | x] & 0xff; + int y = yOld; + if (yStart > yOld) y = yStart; + +// int p = x << level->depthBitsPlusFour | z << level->depthBits; // 4J - removed + + CompressedTileStorage *blocks = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks; + while (y > 0 && Tile::lightBlock[blocks->get(x,(y-1) % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z) & 0xff] == 0) // 4J - blocks->get() was blocks[p + y - 1] + { + y--; + blocks = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT?upperBlocks : lowerBlocks; + } + if (y == yOld) return; + +// level->lightColumnChanged(x, z, y, yOld); // 4J - this call moved below & corrected - see comment further down + heightmap[z << 4 | x] = (byte) y; + + if (y < minHeight) + { + minHeight = y; + } + else + { + int min = Level::maxBuildHeight - 1; + for (int _x = 0; _x < 16; _x++) + for (int _z = 0; _z < 16; _z++) + { + if ((heightmap[_z << 4 | _x] & 0xff) < min) min = (heightmap[_z << 4 | _x] & 0xff); + } + this->minHeight = min; + } + + int xOffs = (this->x * 16) + x; + int zOffs = (this->z * 16) + z; + if (!level->dimension->hasCeiling) + { + if (y < yOld) + { + SparseLightStorage *skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight; + for (int yy = y; yy < yOld; yy++) + { + skyLight = yy >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight; + skyLight->set(x, (yy % Level::COMPRESSED_CHUNK_SECTION_HEIGHT), z, 15); + } + } else + { + // 4J - lighting change brought forward from 1.8.2 + // level->updateLight(LightLayer::Sky, xOffs, yOld, zOffs, xOffs, y, zOffs); + SparseLightStorage *skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight; + for (int yy = yOld; yy < y; yy++) + { + skyLight = yy >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight; + skyLight->set(x, (yy % Level::COMPRESSED_CHUNK_SECTION_HEIGHT), z, 0); + } + } + + int br = 15; + + SparseLightStorage *skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight; + while (y > 0 && br > 0) + { + y--; + skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT? upperSkyLight : lowerSkyLight; + int block = Tile::lightBlock[getTile(x, y, z)]; + if (block == 0) block = 1; + br -= block; + if (br < 0) br = 0; + skyLight->set(x, (y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT), z, br); + // level.updateLightIfOtherThan(LightLayer.Sky, xOffs, y, zOffs, + // -1); + } + } + // 4J - changed to use xOffs and zOffs rather than the (incorrect) x and z it used to, and also moved so that it happens after all the lighting should be + // done by this stage, as this will trigger our asynchronous render updates immediately (potentially) so don't want to say that the lighting is done & then do it + level->lightColumnChanged(xOffs, zOffs, y, yOld); + + // 4J - lighting changes brought forward from 1.8.2 + int height = heightmap[z << 4 | x]; + int y1 = yOld; + int y2 = height; + if (y2 < y1) + { + int tmp = y1; + y1 = y2; + y2 = tmp; + } + if (!level->dimension->hasCeiling) + { + PIXBeginNamedEvent(0,"Light gaps"); + lightGap(xOffs - 1, zOffs, y1, y2); + lightGap(xOffs + 1, zOffs, y1, y2); + lightGap(xOffs, zOffs - 1, y1, y2); + lightGap(xOffs, zOffs + 1, y1, y2); + lightGap(xOffs, zOffs, y1, y2); + PIXEndNamedEvent(); + } + + this->setUnsaved(true); +} + +/** +* The purpose of this method is to allow the EmptyLevelChunk to be all air +* but still block light. See EmptyLevelChunk.java +* +* @param x +* @param y +* @param z +* @return +*/ +int LevelChunk::getTileLightBlock(int x, int y, int z) +{ + return Tile::lightBlock[getTile(x, y, z)]; +} + +int LevelChunk::getTile(int x, int y, int z) +{ + CompressedTileStorage *blocks = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlocks : lowerBlocks; + return blocks->get(x,y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z); +} + +bool LevelChunk::setTileAndData(int x, int y, int z, int _tile, int _data) +{ + byte tile = (byte) _tile; + + // Optimisation brought forward from 1.8.2, change from int to unsigned char & this special value changed from -999 to 255 + int slot = z << 4 | x; + + if (y >= ((int)rainHeights[slot]) - 1) + { + rainHeights[slot] = 255; + } + + int oldHeight = heightmap[slot] & 0xff; + + CompressedTileStorage *blocks = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlocks : lowerBlocks; + SparseDataStorage *data = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperData : lowerData; + int old = blocks->get(x,y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z); + int oldData = data->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z); + if (old == _tile && oldData == _data) return false; + int xOffs = this->x * 16 + x; + int zOffs = this->z * 16 + z; + if (old != 0 && !level->isClientSide) + { + Tile::tiles[old]->onRemoving(level, xOffs, y, zOffs, oldData); + } + PIXBeginNamedEvent(0,"Chunk setting tile"); + blocks->set(x,y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z,tile); + PIXEndNamedEvent(); + if (old != 0) + { + if (!level->isClientSide) + { + Tile::tiles[old]->onRemove(level, xOffs, y, zOffs, old, oldData); + } + else if (Tile::tiles[old]->isEntityTile() && old != _tile ) + { + level->removeTileEntity(xOffs, y, zOffs); + } + } + PIXBeginNamedEvent(0,"Chunk setting data"); + data->set(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z, _data); + PIXEndNamedEvent(); + + // 4J added - flag if something emissive is being added. This is used during level creation to determine what chunks need extra lighting processing + if( Tile::lightEmission[tile & 0xff] > 0 ) + { + emissiveAdded = true; + } + + PIXBeginNamedEvent(0,"Updating lighting"); + // 4J - There isn't any point in recalculating heights or updating sky lighting if this tile has + // the same light-blocking capabilities as the one it is replacing + if( Tile::lightBlock[tile & 0xff] != Tile::lightBlock[old & 0xff] ) + { + if (!level->dimension->hasCeiling) + { + if (Tile::lightBlock[tile & 0xff] != 0) + { + if (y >= oldHeight) + { + PIXBeginNamedEvent(0,"Recalc height 1"); + recalcHeight(x, y + 1, z); + PIXEndNamedEvent(); + } + } else + { + if (y == oldHeight - 1) + { + PIXBeginNamedEvent(0,"Recalc height 2"); + recalcHeight(x, y, z); + PIXEndNamedEvent(); + } + } + } + + // level.updateLight(LightLayer.Carried, xOffs, y, zOffs, xOffs, y, + // zOffs); + PIXBeginNamedEvent(0,"Lighting gaps"); + lightGaps(x, z); + PIXEndNamedEvent(); + } + PIXEndNamedEvent(); + data->set(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z, _data); + if (_tile != 0) + { + if (!level->isClientSide) + { + Tile::tiles[_tile]->onPlace(level, xOffs, y, zOffs); + } + else + { + // 4J - in general we don't want to run the onPlace method on the client, but do a specific bit of the fireTile onPlace code here, + // otherwise we'll place fire on the client and if it isn't a suitable location then we have to wait a few frames before the server + // updates us to say it wasn't right. In the meantime, the client will have done some local lighting etc. and we can end up + // with errors when the update from the server comes in. + if( _tile == Tile::fire_Id ) + { + if(!Tile::tiles[_tile]->mayPlace(level, xOffs, y, zOffs )) + { + blocks->set(x,y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT,z,0); +// blocks[x << level->depthBitsPlusFour | z << level->depthBits | y] = 0; + } + } + } + // AP - changed the method of EntityTile detection cos it's well slow on Vita mate +// if (_tile > 0 && dynamic_cast(Tile::tiles[_tile]) != NULL) + if (_tile > 0 && Tile::tiles[_tile] != NULL && Tile::tiles[_tile]->isEntityTile()) + { + shared_ptr te = getTileEntity(x, y, z); + if (te == NULL) + { + te = ((EntityTile *) Tile::tiles[_tile])->newTileEntity(level); + //app.DebugPrintf("%s: Setting tile id %d, created tileEntity type %d\n", level->isClientSide?"Client":"Server", _tile, te->GetType()); + level->setTileEntity(xOffs, y, zOffs, te); + } + if (te != NULL) + { + //app.DebugPrintf("%s: Setting tile id %d, found tileEntity type %d\n", level->isClientSide?"Client":"Server", _tile, te->GetType()); + te->clearCache(); + } + } + } + // AP - changed the method of EntityTile detection cos it's well slow on Vita mate +// else if (old > 0 && dynamic_cast(Tile::tiles[old]) != NULL) + else if (old > 0 && Tile::tiles[_tile] != NULL && Tile::tiles[_tile]->isEntityTile()) + { + shared_ptr te = getTileEntity(x, y, z); + if (te != NULL) + { + te->clearCache(); + } + } + + this->setUnsaved(true); + return true; +} + +bool LevelChunk::setTile(int x, int y, int z, int _tile) +{ + // 4J Stu - Now using setTileAndData (like in 1.5 Java) so there is only one place we have to fix things + return setTileAndData(x,y,z,_tile,0); +} + +int LevelChunk::getData(int x, int y, int z) +{ + SparseDataStorage *data = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperData : lowerData; + return data->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z); +} + +bool LevelChunk::setData(int x, int y, int z, int val, int mask, bool *maskedBitsChanged) +{ + SparseDataStorage *data = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperData : lowerData; + this->setUnsaved(true); + int old = data->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z); + + *maskedBitsChanged = ( ( old & mask ) != ( val & mask ) ); + + if (old == val) + { + return false; + } + + data->set(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z, val); + int _tile = getTile(x, y, z); + if (_tile > 0 && dynamic_cast( Tile::tiles[_tile] ) != NULL) + { + shared_ptr te = getTileEntity(x, y, z); + if (te != NULL) + { + te->clearCache(); + te->data = val; + } + } + return true; +} + +int LevelChunk::getBrightness(LightLayer::variety layer, int x, int y, int z) +{ + if (layer == LightLayer::Sky) + { + SparseLightStorage *skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperSkyLight : lowerSkyLight; + if(!skyLight) return 0; + return skyLight->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z); + } + else if (layer == LightLayer::Block) + { + SparseLightStorage *blockLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlockLight : lowerBlockLight; + if(!blockLight) return 0; + return blockLight->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z); + } + else return 0; +} + +// 4J added +void LevelChunk::getNeighbourBrightnesses(int *brightnesses, LightLayer::variety layer, int x, int y, int z) +{ + SparseLightStorage *light; + if( layer == LightLayer::Sky ) light = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperSkyLight : lowerSkyLight; + else light = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlockLight : lowerBlockLight; + + if(light) + { + brightnesses[0] = light->get(x - 1, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z); + brightnesses[1] = light->get(x + 1, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z); + brightnesses[4] = light->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z - 1); + brightnesses[5] = light->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z + 1); + } + + + if( layer == LightLayer::Sky ) light = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperSkyLight : lowerSkyLight; + else light = (y-1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlockLight : lowerBlockLight; + if(light) brightnesses[2] = light->get(x, (y - 1) % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z); + + + if( layer == LightLayer::Sky ) light = (y+1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperSkyLight : lowerSkyLight; + else light = (y+1) >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlockLight : lowerBlockLight; + if(light) brightnesses[3] = light->get(x, (y + 1) % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z); +} + +void LevelChunk::setBrightness(LightLayer::variety layer, int x, int y, int z, int brightness) +{ + this->setUnsaved(true); + if (layer == LightLayer::Sky) + { + if(!level->dimension->hasCeiling) + { + SparseLightStorage *skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperSkyLight : lowerSkyLight; + skyLight->set(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z, brightness); + } + } + else if (layer == LightLayer::Block) + { + SparseLightStorage *blockLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlockLight : lowerBlockLight; + blockLight->set(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z, brightness); + } +} + +int LevelChunk::getRawBrightness(int x, int y, int z, int skyDampen) +{ + SparseLightStorage *skyLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperSkyLight : lowerSkyLight; + int light = level->dimension->hasCeiling ? 0 : skyLight->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z); + if (light > 0) LevelChunk::touchedSky = true; + light -= skyDampen; + SparseLightStorage *blockLight = y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ? upperBlockLight : lowerBlockLight; + int block = blockLight->get(x, y % Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z); + if (block > light) light = block; + + /* + * int xd = (absFloor(level.player.x-(this->x*16+x))); int yd = + * (absFloor(level.player.y-(y))); int zd = + * (absFloor(level.player.z-(this->z*16+z))); int dd = xd+yd+zd; if + * (dd<15){ int carried = 15-dd; if (carried<0) carried = 0; if + * (carried>15) carried = 15; if (carried > light) light = carried; } + */ + + return light; +} + +void LevelChunk::addEntity(shared_ptr e) +{ + lastSaveHadEntities = true; + + int xc = Mth::floor(e->x / 16); + int zc = Mth::floor(e->z / 16); + if (xc != this->x || zc != this->z) + { + app.DebugPrintf("Wrong location!"); +// System.out.println("Wrong location! " + e); +// Thread.dumpStack(); + } + int yc = Mth::floor(e->y / 16); + if (yc < 0) yc = 0; + if (yc >= ENTITY_BLOCKS_LENGTH) yc = ENTITY_BLOCKS_LENGTH - 1; + e->inChunk = true; + e->xChunk = x; + e->yChunk = yc; + e->zChunk = z; + +#ifdef _ENTITIES_RW_SECTION + EnterCriticalRWSection(&m_csEntities, true); +#else + EnterCriticalSection(&m_csEntities); +#endif + entityBlocks[yc]->push_back(e); +#ifdef _ENTITIES_RW_SECTION + LeaveCriticalRWSection(&m_csEntities, true); +#else + LeaveCriticalSection(&m_csEntities); +#endif +} + + +void LevelChunk::removeEntity(shared_ptr e) +{ + removeEntity(e, e->yChunk); +} + +void LevelChunk::removeEntity(shared_ptr e, int yc) +{ + if (yc < 0) yc = 0; + if (yc >= ENTITY_BLOCKS_LENGTH) yc = ENTITY_BLOCKS_LENGTH - 1; + +#ifdef _ENTITIES_RW_SECTION + EnterCriticalRWSection(&m_csEntities, true); +#else + EnterCriticalSection(&m_csEntities); +#endif + + // 4J - was entityBlocks[yc]->remove(e); + AUTO_VAR(it, find(entityBlocks[yc]->begin(),entityBlocks[yc]->end(),e)); + if( it != entityBlocks[yc]->end() ) + { + entityBlocks[yc]->erase(it); + // 4J - we don't want storage creeping up here as thinkgs move round the world accumulating up spare space + MemSect(31); +#ifdef __PS3__ + // MGH - have to sort this C++11 code + static bool bShowMsg = true; + if(bShowMsg) + { + app.DebugPrintf("Need to add C++11 shrink_to_fit for PS3\n"); + bShowMsg = false; + } +#else + entityBlocks[yc]->shrink_to_fit(); +#endif + MemSect(0); + } + +#ifdef _ENTITIES_RW_SECTION + LeaveCriticalRWSection(&m_csEntities, true); +#else + LeaveCriticalSection(&m_csEntities); +#endif +} + +bool LevelChunk::isSkyLit(int x, int y, int z) +{ + return y >= (heightmap[z << 4 | x] & 0xff); +} + +void LevelChunk::skyBrightnessChanged() +{ + int x0 = this->x * 16; + int y0 = this->minHeight - 16; + int z0 = this->z * 16; + int x1 = this->x * 16 + 16; + int y1 = Level::maxBuildHeight - 1; + int z1 = this->z * 16 + 16; + + level->setTilesDirty(x0, y0, z0, x1, y1, z1); +} + +shared_ptr LevelChunk::getTileEntity(int x, int y, int z) +{ + TilePos pos(x, y, z); + + // 4J Stu - Changed as we should not be using the [] accessor (causes an insert when we don't want one) + //shared_ptr tileEntity = tileEntities[pos]; + EnterCriticalSection(&m_csTileEntities); + shared_ptr tileEntity = nullptr; + AUTO_VAR(it, tileEntities.find(pos)); + + if (it == tileEntities.end()) + { + LeaveCriticalSection(&m_csTileEntities); // Note: don't assume iterator is valid for tileEntities after this point + + // Fix for #48450 - All: Code Defect: Hang: Game hangs in tutorial, when player arrive at the particular coordinate + // 4J Stu - Chests try to get their neighbours when being destroyed, + // which then causes new tile entities to be created if the neighbour has already been destroyed + if(level->m_bDisableAddNewTileEntities) return nullptr; + + int t = getTile(x, y, z); + if (t <= 0 || !Tile::tiles[t]->isEntityTile()) return nullptr; + + // 4J-PB changed from this in 1.7.3 + //EntityTile *et = (EntityTile *) Tile::tiles[t]; + //et->onPlace(level, this->x * 16 + x, y, this->z * 16 + z); + + //if (tileEntity == NULL) + //{ + tileEntity = ((EntityTile *) Tile::tiles[t])->newTileEntity(level); + level->setTileEntity(this->x * 16 + x, y, this->z * 16 + z, tileEntity); + //} + + //tileEntity = tileEntities[pos]; // 4J - TODO - this doesn't seem right - assignment wrong way? Check + + // 4J Stu - It should have been inserted by now, but check to be sure + EnterCriticalSection(&m_csTileEntities); + AUTO_VAR(newIt, tileEntities.find(pos)); + if (newIt != tileEntities.end()) + { + tileEntity = newIt->second; + } + LeaveCriticalSection(&m_csTileEntities); + } + else + { + tileEntity = it->second; + LeaveCriticalSection(&m_csTileEntities); + } + if (tileEntity != NULL && tileEntity->isRemoved()) + { + EnterCriticalSection(&m_csTileEntities); + tileEntities.erase(pos); + LeaveCriticalSection(&m_csTileEntities); + return nullptr; + } + + return tileEntity; +} + +void LevelChunk::addTileEntity(shared_ptr te) +{ + int xx = (int)(te->x - this->x * 16); + int yy = (int)te->y; + int zz = (int)(te->z - this->z * 16); + setTileEntity(xx, yy, zz, te); + if( loaded ) + { + EnterCriticalSection(&level->m_tileEntityListCS); + level->tileEntityList.push_back(te); + LeaveCriticalSection(&level->m_tileEntityListCS); + } +} + +void LevelChunk::setTileEntity(int x, int y, int z, shared_ptr tileEntity) +{ + TilePos pos(x, y, z); + + tileEntity->setLevel(level); + tileEntity->x = this->x * 16 + x; + tileEntity->y = y; + tileEntity->z = this->z * 16 + z; + + if (getTile(x, y, z) == 0 || !Tile::tiles[getTile(x, y, z)]->isEntityTile()) // 4J - was !(Tile.tiles[getTile(x, y, z)] instanceof EntityTile)) + { + app.DebugPrintf("Attempted to place a tile entity where there was no entity tile!\n"); + return; + } + + tileEntity->clearRemoved(); + + EnterCriticalSection(&m_csTileEntities); + tileEntities[pos] = tileEntity; + LeaveCriticalSection(&m_csTileEntities); +} + +void LevelChunk::removeTileEntity(int x, int y, int z) +{ + TilePos pos(x, y, z); + + if (loaded) + { + // 4J - was: + // TileEntity removeThis = tileEntities.remove(pos); + // if (removeThis != null) { + // removeThis.setRemoved(); + // } + EnterCriticalSection(&m_csTileEntities); + AUTO_VAR(it, tileEntities.find(pos)); + if( it != tileEntities.end() ) + { + shared_ptr te = tileEntities[pos]; + tileEntities.erase(pos); + if( te != NULL ) + { + if(level->isClientSide) + { + app.DebugPrintf("Removing tile entity of type %d\n", te->GetType()); + } + te->setRemoved(); + } + } + LeaveCriticalSection(&m_csTileEntities); + } +} + +void LevelChunk::load() +{ + loaded = true; + + if(!level->isClientSide) + { +#ifdef _LARGE_WORLDS + if(m_bUnloaded && m_unloadedEntitiesTag) + { + ListTag *entityTags = (ListTag *) m_unloadedEntitiesTag->getList(L"Entities"); + if (entityTags != NULL) + { + for (int i = 0; i < entityTags->size(); i++) + { + CompoundTag *teTag = entityTags->get(i); + shared_ptr te = EntityIO::loadStatic(teTag, level); + if (te != NULL) + { + addEntity(te); + } + } + } + + ListTag *tileEntityTags = (ListTag *) m_unloadedEntitiesTag->getList(L"TileEntities"); + if (tileEntityTags != NULL) + { + for (int i = 0; i < tileEntityTags->size(); i++) + { + CompoundTag *teTag = tileEntityTags->get(i); + shared_ptr te = TileEntity::loadStatic(teTag); + if (te != NULL) + { + addTileEntity(te); + } + } + } + delete m_unloadedEntitiesTag; + m_unloadedEntitiesTag = NULL; + m_bUnloaded = false; + } +#endif + + vector< shared_ptr > values; + EnterCriticalSection(&m_csTileEntities); + for( AUTO_VAR(it, tileEntities.begin()); it != tileEntities.end(); it++ ) + { + values.push_back(it->second); + } + LeaveCriticalSection(&m_csTileEntities); + level->addAllPendingTileEntities(values); + +#ifdef _ENTITIES_RW_SECTION + EnterCriticalRWSection(&m_csEntities, true); +#else + EnterCriticalSection(&m_csEntities); +#endif + for (int i = 0; i < ENTITY_BLOCKS_LENGTH; i++) + { + level->addEntities(entityBlocks[i]); + } +#ifdef _ENTITIES_RW_SECTION + LeaveCriticalRWSection(&m_csEntities, true); +#else + LeaveCriticalSection(&m_csEntities); +#endif + } + else + { +#ifdef _LARGE_WORLDS + m_bUnloaded = false; +#endif + } +} + +void LevelChunk::unload(bool unloadTileEntities) // 4J - added parameter +{ + loaded = false; + if( unloadTileEntities ) + { + EnterCriticalSection(&m_csTileEntities); + for( AUTO_VAR(it, tileEntities.begin()); it != tileEntities.end(); it++ ) + { + // 4J-PB -m 1.7.3 was it->second->setRemoved(); + level->markForRemoval(it->second); + } + LeaveCriticalSection(&m_csTileEntities); + } + +#ifdef _ENTITIES_RW_SECTION + EnterCriticalRWSection(&m_csEntities, true); +#else + EnterCriticalSection(&m_csEntities); +#endif + for (int i = 0; i < ENTITY_BLOCKS_LENGTH; i++) + { + level->removeEntities(entityBlocks[i]); + } +#ifdef _ENTITIES_RW_SECTION + LeaveCriticalRWSection(&m_csEntities, true); +#else + LeaveCriticalSection(&m_csEntities); +#endif + //app.DebugPrintf("Unloaded chunk %d, %d\n", x, z); + +#ifdef _LARGE_WORLDS + if ( !m_bUnloaded ) // 4J-JEV: If we unload a chunk twice, we delete all the entities/tile-entities its saved in the entitiesTag. + { + m_bUnloaded = true; + if(!level->isClientSide) + { + delete m_unloadedEntitiesTag; + // 4J Stu - Save out entities to a cached format that won't interfere with other systems + m_unloadedEntitiesTag = new CompoundTag(); + PIXBeginNamedEvent(0,"Saving entities"); + ListTag *entityTags = new ListTag(); + + EnterCriticalSection(&m_csEntities); + for (int i = 0; i < ENTITY_BLOCKS_LENGTH; i++) + { + AUTO_VAR(itEnd, entityBlocks[i]->end()); + for( vector >::iterator it = entityBlocks[i]->begin(); it != itEnd; it++ ) + { + shared_ptr e = *it; + CompoundTag *teTag = new CompoundTag(); + if (e->save(teTag)) + { + entityTags->add(teTag); + } + + } + + // Clear out this list + entityBlocks[i]->clear(); + } + LeaveCriticalSection(&m_csEntities); + + m_unloadedEntitiesTag->put(L"Entities", entityTags); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Saving tile entities"); + ListTag *tileEntityTags = new ListTag(); + + AUTO_VAR(itEnd,tileEntities.end()); + for( unordered_map, TilePosKeyHash, TilePosKeyEq>::iterator it = tileEntities.begin(); + it != itEnd; it++) + { + shared_ptr te = it->second; + CompoundTag *teTag = new CompoundTag(); + te->save(teTag); + tileEntityTags->add(teTag); + } + // Clear out the tileEntities list + tileEntities.clear(); + + m_unloadedEntitiesTag->put(L"TileEntities", tileEntityTags); + PIXEndNamedEvent(); + } + } +#endif +} + +#ifdef _LARGE_WORLDS +bool LevelChunk::isUnloaded() +{ + return m_bUnloaded; +} +#endif + +void LevelChunk::markUnsaved() +{ + this->setUnsaved(true); +} + + +void LevelChunk::getEntities(shared_ptr except, AABB *bb, vector > &es) +{ + int yc0 = Mth::floor((bb->y0 - 2) / 16); + int yc1 = Mth::floor((bb->y1 + 2) / 16); + if (yc0 < 0) yc0 = 0; + if (yc1 >= ENTITY_BLOCKS_LENGTH) yc1 = ENTITY_BLOCKS_LENGTH - 1; + +#ifndef __PSVITA__ + // AP - RW critical sections are expensive so enter once in Level::getEntities + EnterCriticalSection(&m_csEntities); +#endif + for (int yc = yc0; yc <= yc1; yc++) + { + vector > *entities = entityBlocks[yc]; + + AUTO_VAR(itEnd, entities->end()); + for (AUTO_VAR(it, entities->begin()); it != itEnd; it++) + { + shared_ptr e = *it; //entities->at(i); + if (e != except && e->bb->intersects(bb)) + { + es.push_back(e); + vector > *subs = e->getSubEntities(); + if (subs != NULL) + { + for (int j = 0; j < subs->size(); j++) + { + e = subs->at(j); + if (e != except && e->bb->intersects(bb)) + { + es.push_back(e); + } + } + } + } + } + } +#ifndef __PSVITA__ + LeaveCriticalSection(&m_csEntities); +#endif +} + +void LevelChunk::getEntitiesOfClass(const type_info& ec, AABB *bb, vector > &es) +{ + int yc0 = Mth::floor((bb->y0 - 2) / 16); + int yc1 = Mth::floor((bb->y1 + 2) / 16); + + if (yc0 < 0) + { + yc0 = 0; + } + else if (yc0 >= ENTITY_BLOCKS_LENGTH) + { + yc0 = ENTITY_BLOCKS_LENGTH - 1; + } + if (yc1 >= ENTITY_BLOCKS_LENGTH) + { + yc1 = ENTITY_BLOCKS_LENGTH - 1; + } + else if (yc1 < 0) + { + yc1 = 0; + } + +#ifndef __PSVITA__ + // AP - RW critical sections are expensive so enter once in Level::getEntitiesOfClass + EnterCriticalSection(&m_csEntities); +#endif + for (int yc = yc0; yc <= yc1; yc++) + { + vector > *entities = entityBlocks[yc]; + + AUTO_VAR(itEnd, entities->end()); + for (AUTO_VAR(it, entities->begin()); it != itEnd; it++) + { + shared_ptr e = *it; //entities->at(i); + + bool isAssignableFrom = false; + // Some special cases where the base class is a general type that our class may be derived from, otherwise do a direct comparison of type_info + if( ec == typeid(Player) ) { if( dynamic_pointer_cast(e) != NULL ) isAssignableFrom = true; } + else if ( ec == typeid(Mob) ) { if( dynamic_pointer_cast(e) != NULL ) isAssignableFrom = true; } + else if ( ec == typeid(Monster) ) { if( dynamic_pointer_cast(e) != NULL ) isAssignableFrom = true; } + else if ( ec == typeid(Zombie) ) { if( dynamic_pointer_cast(e) != NULL ) isAssignableFrom = true; } + else if(e != NULL && ec == typeid(*(e.get())) ) isAssignableFrom = true; + if (isAssignableFrom && e->bb->intersects(bb)) es.push_back(e); + // 4J - note needs to be equivalent to baseClass.isAssignableFrom(e.getClass()) + } + } +#ifndef __PSVITA__ + LeaveCriticalSection(&m_csEntities); +#endif +} + +int LevelChunk::countEntities() +{ + int entityCount = 0; +#ifdef _ENTITIES_RW_SECTION + EnterCriticalRWSection(&m_csEntities, false); +#else + EnterCriticalSection(&m_csEntities); +#endif + for (int yc = 0; yc < ENTITY_BLOCKS_LENGTH; yc++) + { + entityCount += (int)entityBlocks[yc]->size(); + } +#ifdef _ENTITIES_RW_SECTION + LeaveCriticalRWSection(&m_csEntities, false); +#else + LeaveCriticalSection(&m_csEntities); +#endif + return entityCount; +} + +bool LevelChunk::shouldSave(bool force) +{ + if (dontSave) return false; + if (force) + { + if (lastSaveHadEntities && level->getTime() != lastSaveTime) return true; + } else { + if (lastSaveHadEntities && level->getTime() >= lastSaveTime + 20 * 30) return true; + } + + return m_unsaved; +} + +int LevelChunk::getBlocksAndData(byteArray *data, int x0, int y0, int z0, int x1, int y1, int z1, int p, bool includeLighting/* = true*/) +{ + int xs = x1 - x0; + int ys = y1 - y0; + int zs = z1 - z0; + + // 4J Stu - Added this because some "min" functions don't let us use our constants :( + int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + + // 4J - replaced block storage as now using CompressedTileStorage + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerBlocks->getDataRegion( *data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p ); + if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperBlocks->getDataRegion( *data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p ); + + // 4J - replaced data storage as now using SparseDataStorage + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerData->getDataRegion( *data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p ); + if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperData->getDataRegion( *data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p ); + + if( includeLighting ) + { + // 4J - replaced block and skylight storage as these now use our SparseLightStorage + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerBlockLight->getDataRegion( *data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p ); + if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperBlockLight->getDataRegion( *data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p ); + + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerSkyLight->getDataRegion( *data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p ); + if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperSkyLight->getDataRegion( *data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p ); + } + + /* + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + int slot = (x << level->depthBitsPlusFour | z << level->depthBits | y0) >> 1; + int len = (y1 - y0) / 2; + System::arraycopy(blockLight->data, slot, data, p, len); + p += len; + } + + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + int slot = (x << level->depthBitsPlusFour | z << level->depthBits | y0) >> 1; + int len = (y1 - y0) / 2; + System::arraycopy(skyLight->data, slot, data, p, len); + p += len; + } + */ + + return p; +} + +// 4J added - return true if setBlocksAndData would change any blocks +bool LevelChunk::testSetBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p) +{ + bool changed = false; + + // 4J Stu - Added this because some "min" functions don't let us use our constants :( + int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) changed = lowerBlocks->testSetDataRegion(data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p); + if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) changed = changed || upperBlocks->testSetDataRegion(data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p); + + return changed; +} + +void LevelChunk::tileUpdatedCallback(int x, int y, int z, void *param, int yparam) +{ + LevelChunk *lc = (LevelChunk *)param; + int xx = lc->x * 16 + x; + int yy = y + yparam; + int zz = lc->z * 16 + z; + lc->level->checkLight(xx, yy, zz); +} + +int LevelChunk::setBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p, bool includeLighting/* = true*/) +{ + // If includeLighting is set, then this is a full chunk's worth of data that we are receiving on the client. We'll have made this chunk initially as compressed, + // so throw that data away and make some fully uncompressed storage now to improve the speed up writing to it. Only doing this for lower chunks as quite likely + // that the upper chunk doesn't have anything in anyway. + if( includeLighting ) + { + GameRenderer::AddForDelete(lowerBlocks); + byteArray emptyByteArray; + lowerBlocks = new CompressedTileStorage(emptyByteArray,0); + GameRenderer::FinishedReassigning(); + + GameRenderer::AddForDelete(lowerSkyLight); + lowerSkyLight = new SparseLightStorage(true,false); + GameRenderer::FinishedReassigning(); + + GameRenderer::AddForDelete(lowerBlockLight); + lowerBlockLight = new SparseLightStorage(false,false); + GameRenderer::FinishedReassigning(); + + GameRenderer::AddForDelete(lowerData); + lowerData = new SparseDataStorage(false); + GameRenderer::FinishedReassigning(); + } + + // 4J Stu - Added this because some "min" functions don't let us use our constants :( + int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + + // 4J - replaced block storage as now uses CompressedTileStorage + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerBlocks->setDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p, includeLighting ? NULL : tileUpdatedCallback, this, 0 ); + if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperBlocks->setDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p, includeLighting ? NULL : tileUpdatedCallback, this, Level::COMPRESSED_CHUNK_SECTION_HEIGHT ); + /* + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + int slot = x << level->depthBitsPlusFour | z << level->depthBits | y0; + int len = y1 - y0; + System::arraycopy(data, p, &blocks, slot, len); + p += len; + }*/ + + recalcHeightmapOnly(); + + // 4J - replaced data storage as now uses SparseDataStorage + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerData->setDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p, includeLighting ? NULL : tileUpdatedCallback, this, 0 ); + if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperData->setDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p, includeLighting ? NULL : tileUpdatedCallback, this, Level::COMPRESSED_CHUNK_SECTION_HEIGHT ); + + if( includeLighting ) + { + // 4J - replaced block and skylight storage as these now use our SparseLightStorage + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerBlockLight->setDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p ); + if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperBlockLight->setDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p ); + + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerSkyLight->setDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p ); + if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperSkyLight->setDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p ); + + memcpy(biomes.data, &data.data[p],biomes.length); + p += biomes.length; + } + else + { + // Because the host's local client shares data with it, the lighting updates that are done via callbacks in the setDataRegion calls above when things don't work, as they + // don't detect changes because they've already happened just because the data was being shared when the server updated them. This will leave the lighting information out of sync on + // the client, so resync for this & surrounding chunks that might have been affected + if( level->isClientSide && g_NetworkManager.IsHost() ) + { + reSyncLighting(); + level->getChunk(x-1,z-1)->reSyncLighting(); + level->getChunk(x-0,z-1)->reSyncLighting(); + level->getChunk(x+1,z-1)->reSyncLighting(); + level->getChunk(x-1,z+0)->reSyncLighting(); + level->getChunk(x+1,z+0)->reSyncLighting(); + level->getChunk(x-1,z+1)->reSyncLighting(); + level->getChunk(x+0,z+1)->reSyncLighting(); + level->getChunk(x+1,z+1)->reSyncLighting(); + } + } + + /* + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + int slot = (x << level->depthBitsPlusFour | z << level->depthBits | y0) >> 1; + int len = (y1 - y0) / 2; + System::arraycopy(data, p, &blockLight->data, slot, len); + p += len; + } + + for (int x = x0; x < x1; x++) + for (int z = z0; z < z1; z++) + { + int slot = (x << level->depthBitsPlusFour | z << level->depthBits | y0) >> 1; + int len = (y1 - y0) / 2; + System::arraycopy(data, p, &skyLight->data, slot, len); + p += len; + } + */ + + for(AUTO_VAR(it, tileEntities.begin()); it != tileEntities.end(); ++it) + { + it->second->clearCache(); + } +// recalcHeightmap(); + + // If the includeLighting flag is set, then this is a full chunk's worth of data. This is a good time to compress everything that we've just set up. + if( includeLighting ) + { + compressLighting(); + compressBlocks(); + compressData(); + } + + return p; +} + +void LevelChunk::setCheckAllLight() +{ + checkLightPosition = 0; +} + +Random *LevelChunk::getRandom(__int64 l) +{ + return new Random((level->getSeed() + x * x * 4987142 + x * 5947611 + z * z * 4392871l + z * 389711) ^ l); +} + +bool LevelChunk::isEmpty() +{ + return false; +} +void LevelChunk::attemptCompression() +{ + // 4J - removed +#if 0 + try { + ByteArrayOutputStream *baos = new ByteArrayOutputStream(); + GZIPOutputStream *gzos = new GZIPOutputStream(baos); + DataOutputStream *dos = new DataOutputStream(gzos); + dos.close(); + System.out.println("Compressed size: " + baos.toByteArray().length); + } catch (Exception e) { + + } +#endif +} + +void LevelChunk::checkPostProcess(ChunkSource *source, ChunkSource *parent, int x, int z) +{ + if ( ( ( terrainPopulated & sTerrainPopulatedFromHere ) == 0 ) && source->hasChunk(x + 1, z + 1) && source->hasChunk(x, z + 1) && source->hasChunk(x + 1, z)) + { + source->postProcess(parent, x, z); + } + if (source->hasChunk(x - 1, z) && ( (source->getChunk(x - 1, z)->terrainPopulated & sTerrainPopulatedFromHere ) == 0 ) && source->hasChunk(x - 1, z + 1) && source->hasChunk(x, z + 1) && source->hasChunk(x - 1, z + 1)) + { + source->postProcess(parent, x - 1, z); + } + if (source->hasChunk(x, z - 1) && ( (source->getChunk(x, z - 1)->terrainPopulated & sTerrainPopulatedFromHere ) == 0 ) && source->hasChunk(x + 1, z - 1) && source->hasChunk(x + 1, z)) + { + source->postProcess(parent, x, z - 1); + } + if (source->hasChunk(x - 1, z - 1) && ( (source->getChunk(x - 1, z - 1)->terrainPopulated & sTerrainPopulatedFromHere ) == 0 ) && source->hasChunk(x, z - 1) && source->hasChunk(x - 1, z)) + { + source->postProcess(parent, x - 1, z - 1); + } +} + +// 4J added - check for any pre-1.8.2 chests in the chunk at (x,z), and calculate their facing direction & relight to bring up to date with the post 1.8.2 build +void LevelChunk::checkChests(ChunkSource *source, int x, int z ) +{ + LevelChunk *lc = source->getChunk( x, z ); + + for( int xx = 0; xx < 16; xx++ ) + for( int zz = 0; zz < 16; zz++ ) + for( int yy = 0; yy < 128; yy++ ) + { + if( lc->getTile( xx, yy, zz ) == Tile::chest_Id ) + { + if( lc->getData( xx, yy, zz ) == 0 ) + { + int xOffs = x * 16 + xx; + int zOffs = z * 16 + zz; + ChestTile *tile = (ChestTile *)Tile::tiles[Tile::chest_Id]; + tile->recalcLockDir( level, xOffs, yy, zOffs ); + level->checkLight(xOffs, yy, zOffs, true); + } + } + } +} + +// 4J - lighting change brought forward from 1.8.2 +void LevelChunk::tick() +{ + if (hasGapsToCheck && !level->dimension->hasCeiling) recheckGaps(); +} + +ChunkPos *LevelChunk::getPos() +{ + return new ChunkPos(x, z); +} + +bool LevelChunk::isYSpaceEmpty(int y1, int y2) +{ + return false; + // 4J Unused + /*if (y1 < 0) { + y1 = 0; + } + if (y2 >= Level.maxBuildHeight) { + y2 = Level.maxBuildHeight - 1; + } + for (int y = y1; y <= y2; y += 16) { + LevelChunkSection section = sections[y >> 4]; + if (section != null && !section.isEmpty()) { + return false; + } + } + return true;*/ +} + +Biome *LevelChunk::getBiome(int x, int z, BiomeSource *biomeSource) +{ + int value = biomes[(z << 4) | x] & 0xff; + if (value == 0xff) + { + Biome *biome = biomeSource->getBiome((this->x << 4) + x, (this->z << 4) + z); + value = biome->id; + biomes[(z << 4) | x] = (byte) (value & 0xff); + } + if (Biome::biomes[value] == NULL) + { + return Biome::plains; + } + return Biome::biomes[value]; +} + +byteArray LevelChunk::getBiomes() +{ + return biomes; +} + +void LevelChunk::setBiomes(byteArray biomes) +{ + if(this->biomes.data != NULL) delete[] this->biomes.data; + this->biomes = biomes; +} + +// 4J - optimisation brought forward from 1.8.2 +int LevelChunk::getTopRainBlock(int x, int z) +{ + int slot = x | (z << 4); + int h = rainHeights[slot]; + + if (h == 255) + { + int y = Level::maxBuildHeight - 1; + h = -1; + while (y > 0 && h == -1) + { + int t = getTile(x, y, z); + Material *m = t == 0 ? Material::air : Tile::tiles[t]->material; + if (!m->blocksMotion() && !m->isLiquid()) + { + y--; + } + else + { + h = y + 1; + } + } + // 255 indicates that the rain height needs recalculated. If the rain height ever actually Does get to 255, then it will just keep not being cached, so + // probably better just to let the rain height be 254 in this instance and suffer a slightly incorrect results + if( h == 255 ) h = 254; + rainHeights[slot] = h; + } + + return h; +} + +// 4J added as optimisation, these biome checks are expensive so caching through flags in levelchunk +bool LevelChunk::biomeHasRain(int x, int z) +{ + updateBiomeFlags(x, z); + int slot = ( x >> 1 ) | (z * 8); + int shift = ( x & 1 ) * 4; + return ( ( columnFlags[slot] & ( eColumnFlag_biomeHasRain << shift ) ) != 0 ); +} + +// 4J added as optimisation, these biome checks are expensive so caching through flags in levelchunk +bool LevelChunk::biomeHasSnow(int x, int z) +{ + updateBiomeFlags(x, z); + int slot = ( x >> 1 ) | (z * 8); + int shift = ( x & 1 ) * 4; + return ( ( columnFlags[slot] & ( eColumnFlag_biomeHasSnow << shift ) ) != 0 ); +} + +void LevelChunk::updateBiomeFlags(int x, int z) +{ + int slot = ( x >> 1 ) | (z * 8); + int shift = ( x & 1 ) * 4; + if( ( columnFlags[slot] & ( eColumnFlag_biomeOk << shift ) ) == 0 ) + { + int xOffs = (this->x * 16) + x; + int zOffs = (this->z * 16) + z; + BiomeArray biomes; + level->getBiomeSource()->getBiomeBlock(biomes, xOffs, zOffs, 1, 1, true); + if( biomes[0]->hasRain()) columnFlags[slot] |= ( eColumnFlag_biomeHasRain << shift ); + if( biomes[0]->hasSnow()) columnFlags[slot] |= ( eColumnFlag_biomeHasSnow << shift ); + columnFlags[slot] |= ( eColumnFlag_biomeOk << shift ); + delete biomes.data; + } +} + +// Get a byte array of length 16384 ( 128 x 16 x 16 x 0.5 ), containing data. Ordering same as java version if originalOrder set; +void LevelChunk::getDataData(byteArray data) +{ + lowerData->getData(data,0); + if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES/2) upperData->getData(data,Level::COMPRESSED_CHUNK_SECTION_TILES/2); +} + +// Set data to data passed in input byte array of length 16384. This data must be in original (java version) order if originalOrder set. +void LevelChunk::setDataData(byteArray data) +{ + if( lowerData == NULL ) lowerData = new SparseDataStorage(); + if( upperData == NULL ) upperData = new SparseDataStorage(true); + lowerData->setData(data,0); + if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES/2) upperData->setData(data,Level::COMPRESSED_CHUNK_SECTION_TILES/2); +} + +// Get a byte array of length 16384 ( 128 x 16 x 16 x 0.5 ), containing sky light data. Ordering same as java version if originalOrder set; +void LevelChunk::getSkyLightData(byteArray data) +{ + lowerSkyLight->getData(data,0); + if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES/2) upperSkyLight->getData(data,Level::COMPRESSED_CHUNK_SECTION_TILES/2); +} + +// Get a byte array of length 16384 ( 128 x 16 x 16 x 0.5 ), containing block light data. Ordering same as java version if originalOrder set; +void LevelChunk::getBlockLightData(byteArray data) +{ + lowerBlockLight->getData(data,0); + if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES/2) upperBlockLight->getData(data,Level::COMPRESSED_CHUNK_SECTION_TILES/2); +} + +// Set sky light data to data passed in input byte array of length 16384. This data must be in original (java version) order if originalOrder set. +void LevelChunk::setSkyLightData(byteArray data) +{ + if( lowerSkyLight == NULL ) lowerSkyLight = new SparseLightStorage(true); + if( upperSkyLight == NULL ) upperSkyLight = new SparseLightStorage(true,true); + lowerSkyLight->setData(data,0); + if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES/2) upperSkyLight->setData(data,Level::COMPRESSED_CHUNK_SECTION_TILES/2); +} + +// Set block light data to data passed in input byte array of length 16384. This data must be in original (java version) order if originalOrder set. +void LevelChunk::setBlockLightData(byteArray data) +{ + if( lowerBlockLight == NULL ) lowerBlockLight = new SparseLightStorage(false); + if( upperBlockLight == NULL ) upperBlockLight = new SparseLightStorage(false, true); + lowerBlockLight->setData(data,0); + if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES/2) upperBlockLight->setData(data,Level::COMPRESSED_CHUNK_SECTION_TILES/2); +} + +// Set sky light data to be all fully lit +void LevelChunk::setSkyLightDataAllBright() +{ + lowerSkyLight->setAllBright(); + upperSkyLight->setAllBright(); +} + +// Attempt to compress lighting data. Doesn't make any guarantee that it will succeed - can only compress if the lighting data is being shared, and nothing else is trying to update it from another thread. +void LevelChunk::compressLighting() +{ + // The lighting data is now generally not shared between host & local client, but is for a while at the start of level creation (until the point where the chunk data would be transferred by network + // data for remote clients). We'll therefore either be compressing a shared copy here or one of the server or client copies depending on + lowerSkyLight->compress(); + upperSkyLight->compress(); + lowerBlockLight->compress(); + upperBlockLight->compress(); +} + +void LevelChunk::compressBlocks() +{ +#ifdef SHARING_ENABLED + CompressedTileStorage *blocksToCompressLower = NULL; + CompressedTileStorage *blocksToCompressUpper = NULL; + + // If we're the host machine, and this is the client level, then we only want to do this if we are sharing data. This means that we will be compressing the data that is shared from the server. + // No point trying to compress the local client copy of the data if the data is unshared, since we'll be throwing this data away again anyway once we share with the server again. + if( level->isClientSide && g_NetworkManager.IsHost() ) + { + // Note - only the extraction of the pointers needs to be done in the critical section, since even if the data is unshared whilst we are processing this data is still valid (for the server) + EnterCriticalSection(&m_csSharing); + if( sharingTilesAndData ) + { + blocksToCompressLower = lowerBlocks; + blocksToCompressUpper = upperBlocks; + } + LeaveCriticalSection(&m_csSharing); + } + else + { + // Not the host, simple case + blocksToCompressLower = lowerBlocks; + blocksToCompressUpper = upperBlocks; + } + + // Attempt to do the actual compression + if( blocksToCompressLower ) blocksToCompressLower->compress(); + if( blocksToCompressUpper ) blocksToCompressUpper->compress(); +#else + blocks->compress(); +#endif +} + +bool LevelChunk::isLowerBlockStorageCompressed() +{ + return lowerBlocks->isCompressed(); +} + +int LevelChunk::isLowerBlockLightStorageCompressed() +{ + return lowerBlockLight->isCompressed(); +} + +int LevelChunk::isLowerDataStorageCompressed() +{ + return lowerData->isCompressed(); +} + +void LevelChunk::writeCompressedBlockData(DataOutputStream *dos) +{ + lowerBlocks->write(dos); + upperBlocks->write(dos); +} + +void LevelChunk::writeCompressedDataData(DataOutputStream *dos) +{ + lowerData->write(dos); + upperData->write(dos); +} + +void LevelChunk::writeCompressedSkyLightData(DataOutputStream *dos) +{ + lowerSkyLight->write(dos); + upperSkyLight->write(dos); +} + +void LevelChunk::writeCompressedBlockLightData(DataOutputStream *dos) +{ + lowerBlockLight->write(dos); + upperBlockLight->write(dos); +} + +void LevelChunk::readCompressedBlockData(DataInputStream *dis) +{ + lowerBlocks->read(dis); + upperBlocks->read(dis); +} + +void LevelChunk::readCompressedDataData(DataInputStream *dis) +{ + if( lowerData == NULL ) lowerData = new SparseDataStorage(); + if( upperData == NULL ) upperData = new SparseDataStorage(true); + lowerData->read(dis); + upperData->read(dis); +} + +void LevelChunk::readCompressedSkyLightData(DataInputStream *dis) +{ + if( lowerSkyLight == NULL ) lowerSkyLight = new SparseLightStorage(true); + if( upperSkyLight == NULL ) upperSkyLight = new SparseLightStorage(true,true); + lowerSkyLight->read(dis); + upperSkyLight->read(dis); +} + +void LevelChunk::readCompressedBlockLightData(DataInputStream *dis) +{ + if( lowerBlockLight == NULL ) lowerBlockLight = new SparseLightStorage(false); + if( upperBlockLight == NULL ) upperBlockLight = new SparseLightStorage(false, true); + lowerBlockLight->read(dis); + upperBlockLight->read(dis); +} + +// Attempt to compress data. Doesn't make any guarantee that it will succeed - can only compress if the data is being shared, and nothing else is trying to update it from another thread. +void LevelChunk::compressData() +{ +#ifdef SHARING_ENABLED + SparseDataStorage *dataToCompressLower = NULL; + SparseDataStorage *dataToCompressUpper = NULL; + + // If we're the host machine, and this is the client level, then we only want to do this if we are sharing data. This means that we will be compressing the data that is shared from the server. + // No point trying to compress the local client copy of the data if the data is unshared, since we'll be throwing this data away again anyway once we share with the server again. + if( level->isClientSide && g_NetworkManager.IsHost() ) + { + // Note - only the extraction of the pointers needs to be done in the critical section, since even if the data is unshared whilst we are processing this data is still valid (for the server) + EnterCriticalSection(&m_csSharing); + if( sharingTilesAndData ) + { + dataToCompressLower = lowerData; + dataToCompressUpper = upperData; + } + LeaveCriticalSection(&m_csSharing); + } + else + { + // Not the host, simple case + dataToCompressLower = lowerData; + dataToCompressUpper = upperData; + } + + // Attempt to do the actual compression + if( dataToCompressLower ) dataToCompressLower->compress(); + if( dataToCompressUpper ) dataToCompressUpper->compress(); +#else + data->compress(); +#endif +} + +bool LevelChunk::isRenderChunkEmpty(int y) +{ + if( isEmpty() ) + { + return true; + } + if( y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT ) + { + return upperBlocks->isRenderChunkEmpty( y - Level::COMPRESSED_CHUNK_SECTION_HEIGHT ); + } + else + { + return lowerBlocks->isRenderChunkEmpty( y ); + } +} + +// Set block data to that passed in in the input array of size 32768 +void LevelChunk::setBlockData(byteArray data) +{ + lowerBlocks->setData(data,0); + if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES) upperBlocks->setData(data,Level::COMPRESSED_CHUNK_SECTION_TILES); +} + +// Sets data in passed in array of size 32768, from the block data in this chunk +void LevelChunk::getBlockData(byteArray data) +{ + lowerBlocks->getData(data,0); + if(data.length > Level::COMPRESSED_CHUNK_SECTION_TILES) upperBlocks->getData(data,Level::COMPRESSED_CHUNK_SECTION_TILES); +} + +int LevelChunk::getBlocksAllocatedSize(int *count0, int *count1, int *count2, int *count4, int *count8) +{ + return lowerBlocks->getAllocatedSize(count0, count1, count2, count4, count8); +} + +int LevelChunk::getHighestNonEmptyY() +{ + int highestNonEmptyY = -1; + if(upperBlocks) highestNonEmptyY = upperBlocks->getHighestNonEmptyY() + Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + if(highestNonEmptyY < 0) highestNonEmptyY = lowerBlocks->getHighestNonEmptyY(); + if(highestNonEmptyY < 0) highestNonEmptyY = 0; + + return highestNonEmptyY; +} + +byteArray LevelChunk::getReorderedBlocksAndData(int x0, int y0, int z0, int xs, int &ys, int zs) +{ + int highestNonEmpty = getHighestNonEmptyY(); + + ys = min(highestNonEmpty - y0, ys); + if(ys < 0 ) ys = 0; + + int x1 = x0 + xs; + int y1 = y0 + ys; + int z1 = z0 + zs; + + unsigned int tileCount = xs * ys * zs; + unsigned int halfTileCount = tileCount/2; + + byteArray data = byteArray( tileCount + (3* halfTileCount) + biomes.length ); + for( int x = 0; x < xs; x++ ) + { + for( int z = 0; z < zs; z++ ) + { + for( int y = 0; y < ys; y++ ) + { + int slot = (y*xs*zs) + (z*xs) + x; + + data[slot] = getTile(x,y,z); + } + } + } + + int p = tileCount; + + // 4J Stu - Added this because some "min" functions don't let us use our constants :( + int compressedHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + + // 4J - replaced data storage as now using SparseDataStorage + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerData->getDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p ); + if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperData->getDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p ); + + // 4J - replaced block and skylight storage as these now use our SparseLightStorage + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerBlockLight->getDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p ); + if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperBlockLight->getDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p ); + + if(y0 < Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += lowerSkyLight->getDataRegion( data, x0, y0, z0, x1, min(compressedHeight, y1), z1, p ); + if(y1 > Level::COMPRESSED_CHUNK_SECTION_HEIGHT) p += upperSkyLight->getDataRegion( data, x0, max(y0-compressedHeight,0), z0, x1, y1-Level::COMPRESSED_CHUNK_SECTION_HEIGHT, z1, p ); + + memcpy(&data.data[p],biomes.data,biomes.length); + + return data; + + //byteArray rawBuffer = byteArray( Level::CHUNK_TILE_COUNT + (3* Level::HALF_CHUNK_TILE_COUNT) ); + //for( int x = 0; x < 16; x++ ) + //{ + // for( int z = 0; z < 16; z++ ) + // { + // for( int y = 0; y < Level::maxBuildHeight; y++ ) + // { + // int slot = y << 8 | z << 4 | x; + + // rawBuffer[slot] = lc->getTile(x,y,z); + // } + // } + //} + // + //unsigned int offset = Level::CHUNK_TILE_COUNT; + //// Don't bother reordering block data, block light or sky light as they don't seem to make much difference + //byteArray dataData = byteArray(rawBuffer.data+offset, Level::HALF_CHUNK_TILE_COUNT); + //lc->getDataData(dataData); + //offset += Level::HALF_CHUNK_TILE_COUNT; + //byteArray blockLightData = byteArray(rawBuffer.data + offset, Level::HALF_CHUNK_TILE_COUNT); + //offset += Level::HALF_CHUNK_TILE_COUNT; + //byteArray skyLightData = byteArray(rawBuffer.data + offset, Level::HALF_CHUNK_TILE_COUNT); + //lc->getBlockLightData(blockLightData); + //lc->getSkyLightData(skyLightData); + //return rawBuffer; +} + +void LevelChunk::reorderBlocksAndDataToXZY(int y0, int xs, int ys, int zs, byteArray *data) +{ + int y1 = y0 + ys; + unsigned int tileCount = xs * ys * zs; + unsigned int halfTileCount = tileCount/2; + + int sectionHeight = Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + int lowerYSpan = min(y1, sectionHeight) - y0; + int upperYSpan = ys - lowerYSpan; + int upperSlotOffset = xs * zs * lowerYSpan; + + int biomesLength = 16 * 16; + byteArray newBuffer = byteArray(tileCount + (3* halfTileCount) + biomesLength); + for( int x = 0; x < xs; x++ ) + { + for( int z = 0; z < zs; z++ ) + { + for( int y = 0; y < ys; y++ ) + { + int slotY = y; + unsigned int targetSlotOffset = 0; + int ySpan = lowerYSpan; + if(y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + slotY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + targetSlotOffset = upperSlotOffset; + ySpan = upperYSpan; + } + int slot = (x*zs*ySpan) + (z*ySpan) + slotY; + int slot2 = (y*xs*zs) + (z*xs) + x; + + newBuffer[slot + targetSlotOffset] = data->data[slot2]; + } + } + } + // Copy over block data, block light, skylight and biomes as-is + memcpy(newBuffer.data + tileCount, data->data + tileCount, 3*halfTileCount + biomesLength); + delete [] data->data; + data->data = newBuffer.data; + + //int p = 0; + //setBlocksAndData(*data, x0, y0, z0, x1, y1, z1, p); + + //// If it is a full chunk, we'll need to rearrange into the order the rest of the game expects + //if( xs == 16 && ys == 128 && zs == 16 && ( ( x & 15 ) == 0 ) && ( y == 0 ) && ( ( z & 15 ) == 0 ) ) + //{ + // byteArray newBuffer = byteArray(81920); + // for( int x = 0; x < 16; x++ ) + // { + // for( int z = 0; z < 16; z++ ) + // { + // for( int y = 0; y < 128; y++ ) + // { + // int slot = x << 11 | z << 7 | y; + // int slot2 = y << 8 | z << 4 | x; + + // newBuffer[slot] = buffer[slot2]; + // } + // } + // } + // // Copy over block data, block light & skylight as-is + // memcpy(newBuffer.data + 32768, buffer.data + 32768, 49152); + // delete buffer.data; + // buffer.data = newBuffer.data; + //} +} diff --git a/Minecraft.World/LevelChunk.h b/Minecraft.World/LevelChunk.h new file mode 100644 index 00000000..e9510ad5 --- /dev/null +++ b/Minecraft.World/LevelChunk.h @@ -0,0 +1,260 @@ +#pragma once +using namespace std; + +class DataLayer; +class TileEntity; +class Random; +class ChunkSource; +#include "SparseLightStorage.h" +#include "CompressedTileStorage.h" +#include "SparseDataStorage.h" + +#include "LightLayer.h" +#include "Entity.h" +#include "Level.h" + +#define SHARING_ENABLED +class TileCompressData_SPU; + +#if 0//__PSVITA__ +#define _ENTITIES_RW_SECTION +#endif + +class LevelChunk +{ + friend class TileCompressData_SPU; + friend class LevelRenderer; +public: + byteArray biomes; // 4J Stu - Made public + + // 4J Stu - No longer static in 1.8.2 + const int ENTITY_BLOCKS_LENGTH; + static const int BLOCKS_LENGTH = Level::CHUNK_TILE_COUNT; // 4J added + + static bool touchedSky; + + enum EColumnFlag + { + eColumnFlag_recheck = 1, + eColumnFlag_biomeOk = 2, + eColumnFlag_biomeHasSnow = 4, + eColumnFlag_biomeHasRain = 8, + }; + +// byteArray blocks; + // 4J - actual storage for blocks is now private with public methods to access it +private: + CompressedTileStorage *lowerBlocks; // 0 - 127 + CompressedTileStorage *upperBlocks; // 128 - 255 +public: + bool isRenderChunkEmpty(int y); + void setBlockData(byteArray data); // Set block data to that passed in in the input array of size 32768 + void getBlockData(byteArray data); // Sets data in passed in array of size 32768, from the block data in this chunk + int getBlocksAllocatedSize(int *count0, int *count1, int *count2, int *count4, int *count8); + + bool loaded; + unsigned char rainHeights[16*16]; // 4J - optimisation brought forward from 1.8.2 (was int arrayb in java though) + unsigned char columnFlags[16*8]; // 4J - lighting update brought forward from 1.8.2, was a bool array but now mixed with other flags in our version, and stored in nybbles + Level *level; + + // 4J - actual storage for data is now private with public methods to access it +private: + SparseDataStorage *lowerData; // 0 - 127 + SparseDataStorage *upperData; // 128 - 255 +public: + void setDataData(byteArray data); // Set data to that passed in in the input array of size 32768 + void getDataData(byteArray data); // Sets data in passed in array of size 16384, from the data in this chunk + +// DataLayer *data; +private: + // 4J - actual storage for sky & block lights is now private with new methods to be able to access it. + + SparseLightStorage *lowerSkyLight; // 0 - 127 + SparseLightStorage *upperSkyLight; // 128 - 255 + SparseLightStorage *lowerBlockLight; // 0 - 127 + SparseLightStorage *upperBlockLight; // 128 - 255 +public: + void getSkyLightData(byteArray data); // Get a byte array of length 16384 ( 128 x 16 x 16 x 0.5 ), containing sky light data. Ordering same as java version. + void getBlockLightData(byteArray data); // Get a byte array of length 16384 ( 128 x 16 x 16 x 0.5 ), containing block light data. Ordering same as java version. + void setSkyLightData(byteArray data); // Set sky light data to data passed in input byte array of length 16384. This data must be in original (java version) order + void setBlockLightData(byteArray data); // Set block light data to data passed in input byte array of length 16384. This data must be in original (java version) order + void setSkyLightDataAllBright(); // Set sky light data to be all fully lit + bool isLowerBlockStorageCompressed(); + int isLowerBlockLightStorageCompressed(); + int isLowerDataStorageCompressed(); + + void writeCompressedBlockData(DataOutputStream *dos); + void writeCompressedDataData(DataOutputStream *dos); + void writeCompressedSkyLightData(DataOutputStream *dos); + void writeCompressedBlockLightData(DataOutputStream *dos); + + void readCompressedBlockData(DataInputStream *dis); + void readCompressedDataData(DataInputStream *dis); + void readCompressedSkyLightData(DataInputStream *dis); + void readCompressedBlockLightData(DataInputStream *dis); + + byteArray heightmap; + int minHeight; + int x, z; +private: + bool hasGapsToCheck; +public: + + unordered_map, TilePosKeyHash, TilePosKeyEq> tileEntities; + vector > **entityBlocks; + + static const int sTerrainPopulatedFromHere = 2; + static const int sTerrainPopulatedFromW = 4; + static const int sTerrainPopulatedFromS = 8; + static const int sTerrainPopulatedFromSW = 16; + static const int sTerrainPopulatedAllAffecting = 30; // All the post-processing that can actually place tiles in this chunk are complete + static const int sTerrainPopulatedFromNW = 32; + static const int sTerrainPopulatedFromN = 64; + static const int sTerrainPopulatedFromNE = 128; + static const int sTerrainPopulatedFromE = 256; + static const int sTerrainPopulatedFromSE = 512; + static const int sTerrainPopulatedAllNeighbours = 1022; // The post-processing passes of all neighbours to this chunk are complete + static const int sTerrainPostPostProcessed = 1024; // This chunk has been post-post-processed, which is only done when all neighbours have been post-processed + + short terrainPopulated; // 4J - changed from bool to bitfield within short + short *serverTerrainPopulated; // 4J added + + void setUnsaved(bool unsaved); // 4J added +protected: + // 4J Stu - Stopped this being private so we can add some more logic to it + bool m_unsaved; + +public: + bool dontSave; + bool lastSaveHadEntities; +#ifdef SHARING_ENABLED + bool sharingTilesAndData; // 4J added +#endif + bool emissiveAdded; // 4J added + void stopSharingTilesAndData(); // 4J added + virtual void reSyncLighting(); // 4J added + void startSharingTilesAndData(int forceMs = 0); // 4J added + __int64 lastUnsharedTime; // 4J added + __int64 lastSaveTime; + bool seenByPlayer; + +#ifdef _LARGE_WORLDS + bool m_bUnloaded; + CompoundTag *m_unloadedEntitiesTag; +#endif + + //static const int LIGHT_CHECK_MAX_POS = NUM_SECTIONS * 16 * 16; +private: + int checkLightPosition; + +public: + virtual void init(Level *level, int x, int z); + LevelChunk(Level *level, int x, int z); + LevelChunk(Level *level, byteArray blocks, int x, int z); + LevelChunk(Level *level, int x, int z, LevelChunk *lc); + ~LevelChunk(); + + virtual bool isAt(int x, int z); + + virtual int getHeightmap(int x, int z); + int getHighestSectionPosition(); + virtual void recalcBlockLights(); + + virtual void recalcHeightmapOnly(); + + virtual void recalcHeightmap(); + + virtual void lightLava(); + +private: + void lightGaps(int x, int z); + // 4J - changes for lighting brought forward from 1.8.2 +public: + void recheckGaps(bool bForce = false); // 4J - added parameter, made public +private: + void lightGap(int x, int z, int source); + void lightGap(int x, int z, int y1, int y2); + + void recalcHeight(int x, int yStart, int z); + +public: + virtual int getTileLightBlock(int x, int y, int z); + virtual int getTile(int x, int y, int z); + virtual bool setTileAndData(int x, int y, int z, int _tile, int _data); + virtual bool setTile(int x, int y, int z, int _tile); + virtual int getData(int x, int y, int z); + virtual bool setData(int x, int y, int z, int val, int mask, bool *maskedBitsChanged); // 4J added mask + virtual int getBrightness(LightLayer::variety layer, int x, int y, int z); + virtual void getNeighbourBrightnesses(int *brightnesses, LightLayer::variety layer, int x, int y, int z); // 4J added + virtual void setBrightness(LightLayer::variety layer, int x, int y, int z, int brightness); + virtual int getRawBrightness(int x, int y, int z, int skyDampen); + virtual void addEntity(shared_ptr e); + virtual void removeEntity(shared_ptr e); + virtual void removeEntity(shared_ptr e, int yc); + virtual bool isSkyLit(int x, int y, int z); + virtual void skyBrightnessChanged(); + virtual shared_ptr getTileEntity(int x, int y, int z); + virtual void addTileEntity(shared_ptr te); + virtual void setTileEntity(int x, int y, int z, shared_ptr tileEntity); + virtual void removeTileEntity(int x, int y, int z); + virtual void load(); + virtual void unload(bool unloadTileEntities) ; // 4J - added parameter +#ifdef _LARGE_WORLDS + virtual bool isUnloaded(); +#endif + virtual void markUnsaved(); + virtual void getEntities(shared_ptr except, AABB *bb, vector > &es); + virtual void getEntitiesOfClass(const type_info& ec, AABB *bb, vector > &es); + virtual int countEntities(); + virtual bool shouldSave(bool force); + virtual int getBlocksAndData(byteArray *data, int x0, int y0, int z0, int x1, int y1, int z1, int p, bool includeLighting = true); // 4J - added includeLighting parameter + static void tileUpdatedCallback(int x, int y, int z, void *param, int yparam); // 4J added + virtual int setBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p, bool includeLighting = true); // 4J - added includeLighting parameter + virtual bool testSetBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p); // 4J added + virtual void setCheckAllLight(); + + virtual Random *getRandom(__int64 l); + virtual bool isEmpty(); + virtual void attemptCompression(); + +#ifdef SHARING_ENABLED + static CRITICAL_SECTION m_csSharing; // 4J added +#endif + // 4J added +#ifdef _ENTITIES_RW_SECTION + static CRITICAL_RW_SECTION m_csEntities; // AP - we're using a RW critical so we can do multiple reads without contention +#else + static CRITICAL_SECTION m_csEntities; +#endif + static CRITICAL_SECTION m_csTileEntities; // 4J added + static void staticCtor(); + void checkPostProcess(ChunkSource *source, ChunkSource *parent, int x, int z); + void checkChests(ChunkSource *source, int x, int z ); // 4J added + int getTopRainBlock(int x, int z); // 4J - optimisation brought forward from 1.8.2 + void tick(); // 4J - lighting change brought forward from 1.8.2 + ChunkPos *getPos(); + bool isYSpaceEmpty(int y1, int y2); + virtual Biome *getBiome(int x, int z, BiomeSource *biomeSource); + byteArray getBiomes(); + void setBiomes(byteArray biomes); + bool biomeHasRain(int x, int z); // 4J added + bool biomeHasSnow(int x, int z); // 4J added +private: + void updateBiomeFlags(int x, int z); // 4J added +public: + void compressLighting(); // 4J added + void compressBlocks(); // 4J added + void compressData(); // 4J added + int getHighestNonEmptyY(); + byteArray getReorderedBlocksAndData(int x, int y, int z, int xs, int &ys, int zs); + static void reorderBlocksAndDataToXZY(int y0, int xs, int ys, int zs, byteArray *data); +#ifdef LIGHT_COMPRESSION_STATS + int getBlockLightPlanesLower() { return lowerBlockLight->count; } + int getSkyLightPlanesLower() { return lowerSkyLight->count; } + int getBlockLightPlanesUpper() { return upperBlockLight->count; } + int getSkyLightPlanesUpper() { return upperSkyLight->count; } +#endif +#ifdef DATA_COMPRESSION_STATS + int getDataPlanes() { return data->count; } +#endif +}; diff --git a/Minecraft.World/LevelConflictException.cpp b/Minecraft.World/LevelConflictException.cpp new file mode 100644 index 00000000..8ca543a7 --- /dev/null +++ b/Minecraft.World/LevelConflictException.cpp @@ -0,0 +1,6 @@ +#include "stdafx.h" +#include "LevelConflictException.h" + +LevelConflictException::LevelConflictException(const wstring& msg) : RuntimeException(msg) +{ +} \ No newline at end of file diff --git a/Minecraft.World/LevelConflictException.h b/Minecraft.World/LevelConflictException.h new file mode 100644 index 00000000..e8b2bc32 --- /dev/null +++ b/Minecraft.World/LevelConflictException.h @@ -0,0 +1,13 @@ +#pragma once +using namespace std; + +#include "Exceptions.h" + +class LevelConflictException : public RuntimeException +{ +private: + static const __int32 serialVersionUID = 1L; + +public: + LevelConflictException(const wstring& msg); +}; \ No newline at end of file diff --git a/Minecraft.World/LevelData.cpp b/Minecraft.World/LevelData.cpp new file mode 100644 index 00000000..026dca70 --- /dev/null +++ b/Minecraft.World/LevelData.cpp @@ -0,0 +1,589 @@ +#include "stdafx.h" +#include "System.h" +#include "net.minecraft.world.entity.player.h" +#include "com.mojang.nbt.h" +#include "LevelData.h" +#include "LevelType.h" +#include "LevelSettings.h" + +LevelData::LevelData() +{ +} + +LevelData::LevelData(CompoundTag *tag) +{ + seed = tag->getLong(L"RandomSeed"); + m_pGenerator = LevelType::lvl_normal; + if (tag->contains(L"generatorName")) + { + wstring generatorName = tag->getString(L"generatorName"); + m_pGenerator = LevelType::getLevelType(generatorName); + if (m_pGenerator == NULL) + { + m_pGenerator = LevelType::lvl_normal; + } + else if (m_pGenerator->hasReplacement()) + { + int generatorVersion = 0; + if (tag->contains(L"generatorVersion")) + { + generatorVersion = tag->getInt(L"generatorVersion"); + } + m_pGenerator = m_pGenerator->getReplacementForVersion(generatorVersion); + } + } + + gameType = GameType::byId(tag->getInt(L"GameType")); + if (tag->contains(L"MapFeatures")) + { + generateMapFeatures = tag->getBoolean(L"MapFeatures"); + } + else + { + generateMapFeatures = true; + } + spawnBonusChest = tag->getBoolean(L"spawnBonusChest"); + + xSpawn = tag->getInt(L"SpawnX"); + ySpawn = tag->getInt(L"SpawnY"); + zSpawn = tag->getInt(L"SpawnZ"); + time = tag->getLong(L"Time"); + lastPlayed = tag->getLong(L"LastPlayed"); + sizeOnDisk = tag->getLong(L"SizeOnDisk"); + levelName = tag->getString(L"LevelName"); + version = tag->getInt(L"version"); + rainTime = tag->getInt(L"rainTime"); + raining = tag->getBoolean(L"raining"); + thunderTime = tag->getInt(L"thunderTime"); + thundering = tag->getBoolean(L"thundering"); + hardcore = tag->getBoolean(L"hardcore"); + + if (tag->contains(L"initialized")) + { + initialized = tag->getBoolean(L"initialized"); + } + else + { + initialized = true; + } + + if (tag->contains(L"allowCommands")) + { + allowCommands = tag->getBoolean(L"allowCommands"); + } + else + { + allowCommands = gameType == GameType::CREATIVE; + } + + newSeaLevel = tag->getBoolean(L"newSeaLevel"); // 4J added - only use new sea level for newly created maps. This read defaults to false. (sea level changes in 1.8.2) + hasBeenInCreative = tag->getBoolean(L"hasBeenInCreative"); // 4J added so we can not award achievements to levels modified in creative + + // 4J added - for stronghold position + bStronghold = tag->getBoolean(L"hasStronghold"); + + if(bStronghold==false) + { + // we need to generate the position + xStronghold=yStronghold=zStronghold=0; + } + else + { + xStronghold = tag->getInt(L"StrongholdX"); + yStronghold = tag->getInt(L"StrongholdY"); + zStronghold = tag->getInt(L"StrongholdZ"); + } + + // 4J added - for stronghold end portal position + bStrongholdEndPortal = tag->getBoolean(L"hasStrongholdEndPortal"); + + if(bStrongholdEndPortal==false) + { + // we need to generate the position + xStrongholdEndPortal=zStrongholdEndPortal=0; + } + else + { + xStrongholdEndPortal = tag->getInt(L"StrongholdEndPortalX"); + zStrongholdEndPortal = tag->getInt(L"StrongholdEndPortalZ"); + } + + // 4J Added + m_xzSize = tag->getInt(L"XZSize"); + m_hellScale = tag->getInt(L"HellScale"); + + m_xzSize = min(m_xzSize,LEVEL_MAX_WIDTH); + m_xzSize = max(m_xzSize,LEVEL_MIN_WIDTH); + + m_hellScale = min(m_hellScale,HELL_LEVEL_MAX_SCALE); + m_hellScale = max(m_hellScale,HELL_LEVEL_MIN_SCALE); + + int hellXZSize = m_xzSize / m_hellScale; + while(hellXZSize > HELL_LEVEL_MAX_WIDTH && m_hellScale < HELL_LEVEL_MAX_SCALE) + { + ++m_hellScale; + hellXZSize = m_xzSize / m_hellScale; + } + + /* 4J - we don't store this anymore + if (tag->contains(L"Player")) + { + loadedPlayerTag = tag->getCompound(L"Player"); + dimension = loadedPlayerTag->getInt(L"Dimension"); + } + else + { + this->loadedPlayerTag = NULL; + } + */ + dimension = 0; +} + +LevelData::LevelData(LevelSettings *levelSettings, const wstring& levelName) +{ + this->seed = levelSettings->getSeed(); + this->gameType = levelSettings->getGameType(); + this->generateMapFeatures = levelSettings->isGenerateMapFeatures(); + this->spawnBonusChest = levelSettings->hasStartingBonusItems(); + this->levelName = levelName; + this->m_pGenerator = levelSettings->getLevelType(); + this->hardcore = levelSettings->isHardcore(); + + // 4J Stu - Default initers + this->xSpawn = 0; + this->ySpawn = 0; + this->zSpawn = 0; + this->time = -1; // 4J-JEV: Edited: To know when this is uninitialized. + this->lastPlayed = 0; + this->sizeOnDisk = 0; +// this->loadedPlayerTag = NULL; // 4J - we don't store this anymore + this->dimension = 0; + this->version = 0; + this->rainTime = 0; + this->raining = false; + this->thunderTime = 0; + this->thundering = false; + this->allowCommands = levelSettings->getAllowCommands(); + this->initialized = false; + this->newSeaLevel = levelSettings->useNewSeaLevel(); // 4J added - only use new sea level for newly created maps (sea level changes in 1.8.2) + this->hasBeenInCreative = levelSettings->getGameType() == GameType::CREATIVE; // 4J added + + // 4J-PB for the stronghold position + this->bStronghold=false; + this->xStronghold = 0; + this->yStronghold = 0; + this->zStronghold = 0; + + this->xStrongholdEndPortal = 0; + this->zStrongholdEndPortal = 0; + this->bStrongholdEndPortal = false; + m_xzSize = levelSettings->getXZSize(); + m_hellScale = levelSettings->getHellScale(); + + m_xzSize = min(m_xzSize,LEVEL_MAX_WIDTH); + m_xzSize = max(m_xzSize,LEVEL_MIN_WIDTH); + + m_hellScale = min(m_hellScale,HELL_LEVEL_MAX_SCALE); + m_hellScale = max(m_hellScale,HELL_LEVEL_MIN_SCALE); + + int hellXZSize = m_xzSize / m_hellScale; + while(hellXZSize > HELL_LEVEL_MAX_WIDTH && m_hellScale < HELL_LEVEL_MAX_SCALE) + { + ++m_hellScale; + hellXZSize = m_xzSize / m_hellScale; +} +} + +LevelData::LevelData(LevelData *copy) +{ + this->seed = copy->seed; + this->m_pGenerator = copy->m_pGenerator; + this->gameType = copy->gameType; + this->generateMapFeatures = copy->generateMapFeatures; + this->spawnBonusChest = copy->spawnBonusChest; + this->xSpawn = copy->xSpawn; + this->ySpawn = copy->ySpawn; + this->zSpawn = copy->zSpawn; + this->time = copy->time; + this->lastPlayed = copy->lastPlayed; + this->sizeOnDisk = copy->sizeOnDisk; +// this->loadedPlayerTag = copy->loadedPlayerTag; // 4J - we don't store this anymore + this->dimension = copy->dimension; + this->levelName = copy->levelName; + this->version = copy->version; + this->rainTime = copy->rainTime; + this->raining = copy->raining; + this->thunderTime = copy->thunderTime; + this->thundering = copy->thundering; + this->hardcore = copy->hardcore; + this->allowCommands = copy->allowCommands; + this->initialized = copy->initialized; + this->newSeaLevel = copy->newSeaLevel; + this->hasBeenInCreative = copy->hasBeenInCreative; + + // 4J-PB for the stronghold position + this->bStronghold=copy->bStronghold; + this->xStronghold = copy->xStronghold; + this->yStronghold = copy->yStronghold; + this->zStronghold = copy->zStronghold; + + this->xStrongholdEndPortal = copy->xStrongholdEndPortal; + this->zStrongholdEndPortal = copy->zStrongholdEndPortal; + this->bStrongholdEndPortal = copy->bStrongholdEndPortal; + m_xzSize = copy->m_xzSize; + m_hellScale = copy->m_hellScale; +} + +CompoundTag *LevelData::createTag() +{ + CompoundTag *tag = new CompoundTag(); + + setTagData(tag); + + return tag; +} + +CompoundTag *LevelData::createTag(vector > *players) +{ + // 4J - removed all code for storing tags for players + return createTag(); +} + +void LevelData::setTagData(CompoundTag *tag) +{ + tag->putLong(L"RandomSeed", seed); + tag->putString(L"generatorName", m_pGenerator->getGeneratorName()); + tag->putInt(L"generatorVersion", m_pGenerator->getVersion()); + tag->putInt(L"GameType", gameType->getId()); + tag->putBoolean(L"MapFeatures", generateMapFeatures); + tag->putBoolean(L"spawnBonusChest",spawnBonusChest); + tag->putInt(L"SpawnX", xSpawn); + tag->putInt(L"SpawnY", ySpawn); + tag->putInt(L"SpawnZ", zSpawn); + tag->putLong(L"Time", time); + tag->putLong(L"SizeOnDisk", sizeOnDisk); + tag->putLong(L"LastPlayed", System::currentTimeMillis()); + tag->putString(L"LevelName", levelName); + tag->putInt(L"version", version); + tag->putInt(L"rainTime", rainTime); + tag->putBoolean(L"raining", raining); + tag->putInt(L"thunderTime", thunderTime); + tag->putBoolean(L"thundering", thundering); + tag->putBoolean(L"hardcore", hardcore); + tag->putBoolean(L"allowCommands", allowCommands); + tag->putBoolean(L"initialized", initialized); + tag->putBoolean(L"newSeaLevel", newSeaLevel); + tag->putBoolean(L"hasBeenInCreative", hasBeenInCreative); + // store the stronghold position + tag->putBoolean(L"hasStronghold", bStronghold); + tag->putInt(L"StrongholdX", xStronghold); + tag->putInt(L"StrongholdY", yStronghold); + tag->putInt(L"StrongholdZ", zStronghold); + // store the stronghold end portal position + tag->putBoolean(L"hasStrongholdEndPortal", bStrongholdEndPortal); + tag->putInt(L"StrongholdEndPortalX", xStrongholdEndPortal); + tag->putInt(L"StrongholdEndPortalZ", zStrongholdEndPortal); + tag->putInt(L"XZSize", m_xzSize); + tag->putInt(L"HellScale", m_hellScale); +} + +__int64 LevelData::getSeed() +{ + return seed; +} + +int LevelData::getXSpawn() +{ + return xSpawn; +} + +int LevelData::getYSpawn() +{ + return ySpawn; +} + +int LevelData::getZSpawn() +{ + return zSpawn; +} + +int LevelData::getXStronghold() +{ + return xStronghold; +} + + +int LevelData::getZStronghold() +{ + return zStronghold; +} + +int LevelData::getXStrongholdEndPortal() +{ + return xStrongholdEndPortal; +} + + +int LevelData::getZStrongholdEndPortal() +{ + return zStrongholdEndPortal; +} + +__int64 LevelData::getTime() +{ + return time; +} + +__int64 LevelData::getSizeOnDisk() +{ + return sizeOnDisk; +} + +CompoundTag *LevelData::getLoadedPlayerTag() +{ + return NULL; // 4J - we don't store this anymore +} + +// 4J Removed TU9 as it's never accurate due to the dimension never being set +//int LevelData::getDimension() +//{ +// return dimension; +//} + +void LevelData::setSeed(__int64 seed) +{ + this->seed = seed; +} + +void LevelData::setXSpawn(int xSpawn) +{ + this->xSpawn = xSpawn; +} + +void LevelData::setYSpawn(int ySpawn) +{ + this->ySpawn = ySpawn; +} + +void LevelData::setZSpawn(int zSpawn) +{ + this->zSpawn = zSpawn; +} + +void LevelData::setHasStronghold() +{ + this->bStronghold = true; +} + +bool LevelData::getHasStronghold() +{ + return this->bStronghold; +} + + +void LevelData::setXStronghold(int xStronghold) +{ + this->xStronghold = xStronghold; +} + +void LevelData::setZStronghold(int zStronghold) +{ + this->zStronghold = zStronghold; +} + +void LevelData::setHasStrongholdEndPortal() +{ + this->bStrongholdEndPortal = true; +} + +bool LevelData::getHasStrongholdEndPortal() +{ + return this->bStrongholdEndPortal; +} + +void LevelData::setXStrongholdEndPortal(int xStrongholdEndPortal) +{ + this->xStrongholdEndPortal = xStrongholdEndPortal; +} + +void LevelData::setZStrongholdEndPortal(int zStrongholdEndPortal) +{ + this->zStrongholdEndPortal = zStrongholdEndPortal; +} + +void LevelData::setTime(__int64 time) +{ + this->time = time; +} + +void LevelData::setSizeOnDisk(__int64 sizeOnDisk) +{ + this->sizeOnDisk = sizeOnDisk; +} + +void LevelData::setLoadedPlayerTag(CompoundTag *loadedPlayerTag) +{ + // 4J - we don't store this anymore +// this->loadedPlayerTag = loadedPlayerTag; +} + +// 4J Remove TU9 as it's never used +//void LevelData::setDimension(int dimension) +//{ +// this->dimension = dimension; +//} + +void LevelData::setSpawn(int xSpawn, int ySpawn, int zSpawn) +{ + this->xSpawn = xSpawn; + this->ySpawn = ySpawn; + this->zSpawn = zSpawn; +} + +wstring LevelData::getLevelName() +{ + return levelName; +} + +void LevelData::setLevelName(const wstring& levelName) +{ + this->levelName = levelName; +} + +int LevelData::getVersion() +{ + return version; +} + +void LevelData::setVersion(int version) +{ + this->version = version; +} + +__int64 LevelData::getLastPlayed() +{ + return lastPlayed; +} + +bool LevelData::isThundering() +{ + return thundering; +} + +void LevelData::setThundering(bool thundering) +{ + this->thundering = thundering; +} + +int LevelData::getThunderTime() +{ + return thunderTime; +} + +void LevelData::setThunderTime(int thunderTime) +{ + this->thunderTime = thunderTime; +} + +bool LevelData::isRaining() +{ + return raining; +} + +void LevelData::setRaining(bool raining) +{ + this->raining = raining; +} + +int LevelData::getRainTime() +{ + return rainTime; +} + +void LevelData::setRainTime(int rainTime) +{ + this->rainTime = rainTime; +} + +GameType *LevelData::getGameType() +{ + return gameType; +} + +bool LevelData::isGenerateMapFeatures() +{ + return generateMapFeatures; +} + +bool LevelData::getSpawnBonusChest() +{ + return spawnBonusChest; +} + +void LevelData::setGameType(GameType *gameType) +{ + this->gameType = gameType; + + // 4J Added + hasBeenInCreative = hasBeenInCreative || (gameType == GameType::CREATIVE) || app.GetGameHostOption(eGameHostOption_CheatsEnabled) > 0; +} + +bool LevelData::useNewSeaLevel() +{ + return newSeaLevel; +} + +bool LevelData::getHasBeenInCreative() +{ + return hasBeenInCreative; +} + +void LevelData::setHasBeenInCreative(bool value) +{ + hasBeenInCreative = value; +} + +LevelType *LevelData::getGenerator() +{ + return m_pGenerator; +} + +void LevelData::setGenerator(LevelType *generator) +{ + m_pGenerator = generator; +} + +bool LevelData::isHardcore() +{ + return hardcore; +} + +bool LevelData::getAllowCommands() +{ + return allowCommands; +} + +void LevelData::setAllowCommands(bool allowCommands) +{ + this->allowCommands = allowCommands; +} + +bool LevelData::isInitialized() +{ + return initialized; +} + +void LevelData::setInitialized(bool initialized) +{ + this->initialized = initialized; +} + +int LevelData::getXZSize() +{ + return m_xzSize; +} + +int LevelData::getHellScale() +{ + return m_hellScale; +} diff --git a/Minecraft.World/LevelData.h b/Minecraft.World/LevelData.h new file mode 100644 index 00000000..0afe9be2 --- /dev/null +++ b/Minecraft.World/LevelData.h @@ -0,0 +1,133 @@ +#pragma once +using namespace std; + +class Player; +class CompoundTag; +class LevelSettings; +class LevelType; +class GameType; + +class LevelData +{ + friend class DerivedLevelData; +private: + __int64 seed; + LevelType *m_pGenerator;// = LevelType.normal; + int xSpawn; + int ySpawn; + int zSpawn; + __int64 time; + __int64 lastPlayed; + __int64 sizeOnDisk; +// CompoundTag *loadedPlayerTag; // 4J removed + int dimension; + wstring levelName; + int version; + + bool raining; + int rainTime; + + bool thundering; + int thunderTime; + GameType *gameType; + bool generateMapFeatures; + bool hardcore; + bool allowCommands; + bool initialized; + bool newSeaLevel; // 4J added + bool hasBeenInCreative; // 4J added + bool spawnBonusChest; // 4J added + int m_xzSize; // 4J Added + int m_hellScale; // 4J Added + + // 4J added + int xStronghold; + int yStronghold; + int zStronghold; + bool bStronghold; + + int xStrongholdEndPortal; + int zStrongholdEndPortal; + bool bStrongholdEndPortal; + +protected: + LevelData(); + +public: + LevelData(CompoundTag *tag); + LevelData(LevelSettings *levelSettings, const wstring& levelName); + LevelData(LevelData *copy); + CompoundTag *createTag(); + CompoundTag *createTag(vector > *players); + + enum + { + DIMENSION_NETHER=-1, + DIMENSION_OVERWORLD=0, + DIMENSION_END=1 + }; + +protected: + virtual void setTagData(CompoundTag *tag); // 4J - removed CompoundTag *playerTag + +public: + virtual __int64 getSeed(); + virtual int getXSpawn(); + virtual int getYSpawn(); + virtual int getZSpawn(); + virtual int getXStronghold(); + virtual int getZStronghold(); + virtual int getXStrongholdEndPortal(); + virtual int getZStrongholdEndPortal(); + virtual __int64 getTime(); + virtual __int64 getSizeOnDisk(); + virtual CompoundTag *getLoadedPlayerTag(); + //int getDimension(); // 4J Removed TU 9 as it's never accurate + virtual void setSeed(__int64 seed); + virtual void setXSpawn(int xSpawn); + virtual void setYSpawn(int ySpawn); + virtual void setZSpawn(int zSpawn); + virtual void setHasStronghold(); + virtual bool getHasStronghold(); + virtual void setXStronghold(int xStronghold); + virtual void setZStronghold(int zStronghold); + virtual void setHasStrongholdEndPortal(); + virtual bool getHasStrongholdEndPortal(); + virtual void setXStrongholdEndPortal(int xStrongholdEndPortal); + virtual void setZStrongholdEndPortal(int zStrongholdEndPortal); + + virtual void setTime(__int64 time); + virtual void setSizeOnDisk(__int64 sizeOnDisk); + virtual void setLoadedPlayerTag(CompoundTag *loadedPlayerTag); + //void setDimension(int dimension); // 4J Removed TU 9 as it's never used + virtual void setSpawn(int xSpawn, int ySpawn, int zSpawn); + virtual wstring getLevelName(); + virtual void setLevelName(const wstring& levelName); + virtual int getVersion(); + virtual void setVersion(int version); + virtual __int64 getLastPlayed(); + virtual bool isThundering(); + virtual void setThundering(bool thundering); + virtual int getThunderTime(); + virtual void setThunderTime(int thunderTime); + virtual bool isRaining(); + virtual void setRaining(bool raining); + virtual int getRainTime(); + virtual void setRainTime(int rainTime); + virtual GameType *getGameType(); + virtual bool isGenerateMapFeatures(); + virtual bool getSpawnBonusChest(); + virtual void setGameType(GameType *gameType); + virtual bool useNewSeaLevel(); + virtual bool getHasBeenInCreative(); // 4J Added + virtual void setHasBeenInCreative(bool value); // 4J Added + virtual LevelType *getGenerator(); + virtual void setGenerator(LevelType *generator); + virtual bool isHardcore(); + virtual bool getAllowCommands(); + virtual void setAllowCommands(bool allowCommands); + virtual bool isInitialized(); + virtual void setInitialized(bool initialized); + virtual int getXZSize(); // 4J Added + virtual int getHellScale(); // 4J Addded +}; diff --git a/Minecraft.World/LevelEvent.h b/Minecraft.World/LevelEvent.h new file mode 100644 index 00000000..c78c3569 --- /dev/null +++ b/Minecraft.World/LevelEvent.h @@ -0,0 +1,41 @@ +#pragma once + +// 4J - brought forward new sound events from 1.2.3 +class LevelEvent +{ +public: + static const int SOUND_CLICK = 1000; + static const int SOUND_CLICK_FAIL = 1001; + static const int SOUND_LAUNCH = 1002; + static const int SOUND_OPEN_DOOR = 1003; + static const int SOUND_FIZZ = 1004; + static const int SOUND_PLAY_RECORDING = 1005; + + static const int SOUND_GHAST_WARNING = 1007; + static const int SOUND_GHAST_FIREBALL = 1008; + static const int SOUND_BLAZE_FIREBALL = 1009; + + static const int SOUND_ZOMBIE_WOODEN_DOOR = 1010; + static const int SOUND_ZOMBIE_IRON_DOOR = 1011; + static const int SOUND_ZOMBIE_DOOR_CRASH = 1012; + static const int SOUND_WITHER_BOSS_SPAWN = 1013; + static const int SOUND_WITHER_BOSS_SHOOT = 1014; + static const int SOUND_BAT_LIFTOFF = 1015; + static const int SOUND_ZOMBIE_INFECTED = 1016; + static const int SOUND_ZOMBIE_CONVERTED = 1017; + static const int SOUND_DRAGON_DEATH = 1018; + + static const int SOUND_ANVIL_BROKEN = 1020; + static const int SOUND_ANVIL_USED = 1021; + static const int SOUND_ANVIL_LAND = 1022; + + static const int PARTICLES_SHOOT = 2000; + static const int PARTICLES_DESTROY_BLOCK = 2001; + static const int PARTICLES_POTION_SPLASH = 2002; + static const int PARTICLES_EYE_OF_ENDER_DEATH = 2003; + static const int PARTICLES_MOBTILE_SPAWN = 2004; + + //static const int ENDERDRAGON_KILLED = 9000; // 4J Added to signal the the enderdragon was killed + static const int ENDERDRAGON_FIREBALL_SPLASH = 9001; + static const int END_EGG_TELEPORT = 9002; +}; \ No newline at end of file diff --git a/Minecraft.World/LevelEventPacket.cpp b/Minecraft.World/LevelEventPacket.cpp new file mode 100644 index 00000000..a044aded --- /dev/null +++ b/Minecraft.World/LevelEventPacket.cpp @@ -0,0 +1,54 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "LevelEventPacket.h" + + + +LevelEventPacket::LevelEventPacket() +{ + type = 0; + data = 0; + x = 0; + y = 0; + z = 0; +} + +LevelEventPacket::LevelEventPacket(int type, int x, int y, int z, int data) +{ + this->type = type; + this->x = x; + this->y = y; + this->z = z; + this->data = data; +} + +void LevelEventPacket::read(DataInputStream *dis) //throws IOException +{ + type = dis->readInt(); + x = dis->readInt(); + y = dis->readByte() & 0xff; + z = dis->readInt(); + data = dis->readInt(); +} + +void LevelEventPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(type); + dos->writeInt(x); + dos->writeByte(y & 0xff); + dos->writeInt(z); + dos->writeInt(data); +} + +void LevelEventPacket::handle(PacketListener *listener) +{ + listener->handleLevelEvent(shared_from_this()); +} + +int LevelEventPacket::getEstimatedSize() +{ + return 4 * 5 + 1; +} + diff --git a/Minecraft.World/LevelEventPacket.h b/Minecraft.World/LevelEventPacket.h new file mode 100644 index 00000000..6c2fdbaf --- /dev/null +++ b/Minecraft.World/LevelEventPacket.h @@ -0,0 +1,24 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class LevelEventPacket : public Packet, public enable_shared_from_this +{ +public: + int type; + int data; + int x, y, z; + + LevelEventPacket(); + LevelEventPacket(int type, int x, int y, int z, int data); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new LevelEventPacket()); } + virtual int getId() { return 61; } +}; \ No newline at end of file diff --git a/Minecraft.World/LevelListener.h b/Minecraft.World/LevelListener.h new file mode 100644 index 00000000..ec9b11de --- /dev/null +++ b/Minecraft.World/LevelListener.h @@ -0,0 +1,40 @@ +#pragma once +using namespace std; + +class TileEntity; +#include "Player.h" +#include "ParticleTypes.h" + +class LevelListener +{ +public: + virtual void tileChanged(int x, int y, int z) = 0; + + virtual void tileLightChanged(int x, int y, int z) = 0; + + virtual void setTilesDirty(int x0, int y0, int z0, int x1, int y1, int z1, Level *level) = 0; // 4J - added level param + + virtual void allChanged() = 0; + + //virtual void playSound(const wstring& name, double x, double y, double z, float volume, float pitch) = 0; + virtual void playSound(int iSound, double x, double y, double z, float volume, float pitch, float fSoundClipDist=16.0f) = 0; + virtual void playSound(shared_ptr entity,int iSound, double x, double y, double z, float volume, float pitch, float fSoundClipDist=16.0f) = 0; + + // 4J removed - virtual void addParticle(const wstring& name, double x, double y, double z, double xa, double ya, double za) = 0; + + virtual void addParticle(ePARTICLE_TYPE name, double x, double y, double z, double xa, double ya, double za) = 0; // 4J added + + virtual void entityAdded(shared_ptr entity) = 0; + + virtual void entityRemoved(shared_ptr entity) = 0; + + virtual void playerRemoved(shared_ptr entity) = 0; // 4J added - for when a player is removed from the level's player array, not just the entity storage + + virtual void skyColorChanged() = 0; + + virtual void playStreamingMusic(const wstring& name, int x, int y, int z) = 0; + + virtual void levelEvent(shared_ptr source, int type, int x, int y, int z, int data) = 0; + + virtual void destroyTileProgress(int id, int x, int y, int z, int progress) = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/LevelObjectInputStream.h b/Minecraft.World/LevelObjectInputStream.h new file mode 100644 index 00000000..41f3fc8f --- /dev/null +++ b/Minecraft.World/LevelObjectInputStream.h @@ -0,0 +1,34 @@ +#pragma once + +class LevelObjectInputStream; + +/* + +4J This code is not used. + +class LevelObjectInputStream : ObjectInputStream +{ + private Set autoReplacers = new HashSet(); + + public LevelObjectInputStream(InputStream in) throws IOException + { + super(in); + + autoReplacers.add("com.mojang.minecraft.player.Player$1"); + autoReplacers.add("com.mojang.minecraft.mob.Creeper$1"); + autoReplacers.add("com.mojang.minecraft.mob.Skeleton$1"); + } + +protected: + ObjectStreamClass readClassDescriptor() // 4J - throws IOException, ClassNotFoundException + { + ObjectStreamClass osc = super.readClassDescriptor(); + + if (autoReplacers.contains(osc.getName())) + { + return ObjectStreamClass.lookup(Class.forName(osc.getName())); + } + return osc; + } +} +*/ \ No newline at end of file diff --git a/Minecraft.World/LevelSettings.cpp b/Minecraft.World/LevelSettings.cpp new file mode 100644 index 00000000..59faa1ca --- /dev/null +++ b/Minecraft.World/LevelSettings.cpp @@ -0,0 +1,185 @@ +#include "stdafx.h" +#include "LevelSettings.h" +#include "net.minecraft.world.level.storage.h" +#include "LevelType.h" + +GameType *GameType::NOT_SET = NULL; +GameType *GameType::SURVIVAL= NULL; +GameType *GameType::CREATIVE = NULL; +GameType *GameType::ADVENTURE = NULL; + +void GameType::staticCtor() +{ + NOT_SET = new GameType(-1, L""); + SURVIVAL = new GameType(0, L"survival"); + CREATIVE = new GameType(1, L"creative"); + ADVENTURE = new GameType(2, L"adventure"); +} + +GameType::GameType(int id, const wstring &name) +{ + this->id = id; + this->name = name; +} + +int GameType::getId() +{ + return id; +} + +wstring GameType::getName() +{ + return name; +} + +void GameType::updatePlayerAbilities(Abilities *abilities) +{ + if (this == CREATIVE) + { + abilities->mayfly = true; + abilities->instabuild = true; + abilities->invulnerable = true; + } + else + { + abilities->mayfly = false; + abilities->instabuild = false; + abilities->invulnerable = false; + abilities->flying = false; + } + abilities->mayBuild = !isReadOnly(); +} + +bool GameType::isReadOnly() +{ + return this == ADVENTURE; +} + +bool GameType::isCreative() +{ + return this == CREATIVE; +} + +bool GameType::isSurvival() +{ + return this == SURVIVAL || this == ADVENTURE; +} + +GameType *GameType::byId(int id) +{ + if(id == NOT_SET->id) return NOT_SET; + else if(id == SURVIVAL->id) return SURVIVAL; + else if(id == CREATIVE->id) return CREATIVE; + else if(id == ADVENTURE->id) return ADVENTURE; + + return SURVIVAL; +} + +GameType *GameType::byName(const wstring &name) +{ + if(name.compare(NOT_SET->name) == 0) return NOT_SET; + else if(name.compare(SURVIVAL->name) == 0) return SURVIVAL; + else if(name.compare(CREATIVE->name) == 0) return CREATIVE; + else if(name.compare(ADVENTURE->name) == 0) return ADVENTURE; + + return SURVIVAL; +} + +void LevelSettings::_init(__int64 seed, GameType *gameType, bool generateMapFeatures, bool hardcore, bool newSeaLevel, LevelType *levelType, int xzSize, int hellScale) +{ + this->seed = seed; + this->gameType = gameType; + this->hardcore = hardcore; + this->generateMapFeatures = generateMapFeatures; + this->newSeaLevel = newSeaLevel; + this->levelType = levelType; + this->allowCommands = false; + this->startingBonusItems = false; + m_xzSize = xzSize; + m_hellScale = hellScale; +} + +LevelSettings::LevelSettings(__int64 seed, GameType *gameType, bool generateMapFeatures, bool hardcore, bool newSeaLevel, LevelType *levelType, int xzSize, int hellScale) : + seed(seed), + gameType(gameType), + hardcore(hardcore), + generateMapFeatures(generateMapFeatures), + newSeaLevel(newSeaLevel), + levelType(levelType), + startingBonusItems(false) +{ + _init(seed, gameType, generateMapFeatures, hardcore, newSeaLevel, levelType, xzSize, hellScale); +} + +LevelSettings::LevelSettings(LevelData *levelData) +{ + _init(levelData->getSeed(), levelData->getGameType(), levelData->isGenerateMapFeatures(), levelData->isHardcore(), levelData->useNewSeaLevel(), levelData->getGenerator(), levelData->getXZSize(), levelData->getHellScale() ); +} + +LevelSettings *LevelSettings::enableStartingBonusItems() +{ + startingBonusItems = true; + return this; +} + +LevelSettings *LevelSettings::enableSinglePlayerCommands() +{ + allowCommands = true; + return this; +} + +bool LevelSettings::hasStartingBonusItems() +{ + return startingBonusItems; +} + +__int64 LevelSettings::getSeed() +{ + return seed; +} + +GameType *LevelSettings::getGameType() +{ + return gameType; +} + +bool LevelSettings::isHardcore() +{ + return hardcore; +} + +LevelType *LevelSettings::getLevelType() +{ + return levelType; +} + +bool LevelSettings::getAllowCommands() +{ + return allowCommands; +} + +bool LevelSettings::isGenerateMapFeatures() +{ + return generateMapFeatures; +} + +GameType *LevelSettings::validateGameType(int gameType) +{ + return GameType::byId(gameType); +} + +bool LevelSettings::useNewSeaLevel() +{ + return newSeaLevel; +} + +// 4J Added +int LevelSettings::getXZSize() +{ + return m_xzSize; +} + +int LevelSettings::getHellScale() +{ + return m_hellScale; +} diff --git a/Minecraft.World/LevelSettings.h b/Minecraft.World/LevelSettings.h new file mode 100644 index 00000000..c183df8c --- /dev/null +++ b/Minecraft.World/LevelSettings.h @@ -0,0 +1,67 @@ +#pragma once +class LevelType; + +class Abilities; +class LevelData; + +// 4J Stu - Was Java enum class +class GameType +{ +public: + static GameType *NOT_SET; + static GameType *SURVIVAL; + static GameType *CREATIVE; + static GameType *ADVENTURE; + + static void staticCtor(); + +private: + int id; + wstring name; + + GameType(int id, const wstring &name); + +public: + int getId(); + wstring getName(); + void updatePlayerAbilities(Abilities *abilities); + bool isReadOnly(); + bool isCreative(); + bool isSurvival(); + static GameType *byId(int id); + static GameType *byName(const wstring &name); +}; + +class LevelSettings +{ +private: + __int64 seed; + GameType *gameType; + bool generateMapFeatures; + bool hardcore; + bool newSeaLevel; + LevelType *levelType; + bool allowCommands; + bool startingBonusItems; // 4J - brought forward from 1.3.2 + int m_xzSize; // 4J Added + int m_hellScale; + + void _init(__int64 seed, GameType *gameType, bool generateMapFeatures, bool hardcore, bool newSeaLevel, LevelType *levelType, int xzSize, int hellScale); // 4J Added xzSize and hellScale param + +public: + LevelSettings(__int64 seed, GameType *gameType, bool generateMapFeatures, bool hardcore, bool newSeaLevel, LevelType *levelType, int xzSize, int hellScale); // 4J Added xzSize and hellScale param + LevelSettings(LevelData *levelData); + LevelSettings *enableStartingBonusItems(); // 4J - brought forward from 1.3.2 + LevelSettings *enableSinglePlayerCommands(); + bool hasStartingBonusItems(); // 4J - brought forward from 1.3.2 + __int64 getSeed(); + GameType *getGameType(); + bool isHardcore(); + LevelType *getLevelType(); + bool getAllowCommands(); + bool isGenerateMapFeatures(); + bool useNewSeaLevel(); + int getXZSize(); // 4J Added + int getHellScale(); // 4J Added + static GameType *validateGameType(int gameType); +}; diff --git a/Minecraft.World/LevelSoundPacket.cpp b/Minecraft.World/LevelSoundPacket.cpp new file mode 100644 index 00000000..cbe31a2d --- /dev/null +++ b/Minecraft.World/LevelSoundPacket.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include "BasicTypeContainers.h" +#include "InputOutputStream.h" +#include "net.minecraft.network.packet.h" +#include "LevelSoundPacket.h" + +const float LevelSoundPacket::PITCH_ACCURACY = Byte::MAX_VALUE / 2.0f; +const float LevelSoundPacket::LOCATION_ACCURACY = 8.0f; + +LevelSoundPacket::LevelSoundPacket() +{ + sound = 0; + x = 0; + y = Integer::MAX_VALUE; + z = 0; + volume = 0.0f; + pitch = 0; +} + +LevelSoundPacket::LevelSoundPacket(int sound, double x, double y, double z, float volume, float pitch) +{ + this->sound = sound; + this->x = (int) (x * LOCATION_ACCURACY); + this->y = (int) (y * LOCATION_ACCURACY); + this->z = (int) (z * LOCATION_ACCURACY); + this->volume = volume; + // 4J-PB - Let's make the pitch a float so it doesn't get mangled and make the noteblock people unhappy + //this->pitch = (int) (pitch * PITCH_ACCURACY); + this->pitch = pitch; + +// if (this->pitch < 0) this->pitch = 0; +// if (this->pitch > 255) this->pitch = 255; +} + +void LevelSoundPacket::read(DataInputStream *dis) +{ + sound = dis->readInt(); + x = dis->readInt(); + y = dis->readInt(); + z = dis->readInt(); + volume = dis->readFloat(); + //pitch = dis->readUnsignedByte(); + pitch = dis->readFloat(); +} + +void LevelSoundPacket::write(DataOutputStream *dos) +{ + dos->writeInt(sound); + dos->writeInt(x); + dos->writeInt(y); + dos->writeInt(z); + dos->writeFloat(volume); + //dos->writeByte(pitch); + dos->writeFloat(pitch); +} + +int LevelSoundPacket::getSound() +{ + return sound; +} + +double LevelSoundPacket::getX() +{ + return x / LOCATION_ACCURACY; +} + +double LevelSoundPacket::getY() +{ + return y / LOCATION_ACCURACY; +} + +double LevelSoundPacket::getZ() +{ + return z / LOCATION_ACCURACY; +} + +float LevelSoundPacket::getVolume() +{ + return volume; +} + +float LevelSoundPacket::getPitch() +{ + //return pitch / PITCH_ACCURACY; + return pitch; +} + +void LevelSoundPacket::handle(PacketListener *listener) +{ + listener->handleSoundEvent(shared_from_this()); +} + +int LevelSoundPacket::getEstimatedSize() +{ + return 4 * 6; +} diff --git a/Minecraft.World/LevelSoundPacket.h b/Minecraft.World/LevelSoundPacket.h new file mode 100644 index 00000000..403ac092 --- /dev/null +++ b/Minecraft.World/LevelSoundPacket.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Packet.h" + +class LevelSoundPacket : public Packet, public enable_shared_from_this +{ +public: + static const float PITCH_ACCURACY; + static const float LOCATION_ACCURACY; + +private: + int sound; + int x; + int y; + int z; + float volume; + //int pitch; + float pitch; + +public: + LevelSoundPacket(); + LevelSoundPacket(int iSound, double x, double y, double z, float volume, float pitch); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getSound(); + double getX(); + double getY(); + double getZ(); + float getVolume(); + float getPitch(); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new LevelSoundPacket()); } + virtual int getId() { return 62; } +}; diff --git a/Minecraft.World/LevelSource.h b/Minecraft.World/LevelSource.h new file mode 100644 index 00000000..7ff99146 --- /dev/null +++ b/Minecraft.World/LevelSource.h @@ -0,0 +1,29 @@ +#pragma once + +class BiomeSource; +class Material; +class TileEntity; + +#include "LightLayer.h" + +class LevelSource +{ +public: + virtual int getTile(int x, int y, int z) = 0; + virtual shared_ptr getTileEntity(int x, int y, int z) = 0; + virtual int getLightColor(int x, int y, int z, int emitt, int tileId = -1) = 0; // 4J - brought forward from 1.8.2, added tileId + virtual float getBrightness(int x, int y, int z, int emitt) = 0; + virtual float getBrightness(int x, int y, int z) = 0; + virtual int getData(int x, int y, int z) = 0; + virtual Material *getMaterial(int xx, int i, int zz) = 0; + virtual bool isSolidRenderTile(int x, int y, int z) = 0; + virtual bool isSolidBlockingTile(int x, int y, int z) = 0; + virtual bool isEmptyTile(int x, int y, int z) = 0; + virtual Biome *getBiome(int x, int z) = 0; + virtual BiomeSource *getBiomeSource() = 0; + virtual int getBrightness(LightLayer::variety layer, int x, int y, int z) = 0; + virtual int getMaxBuildHeight() = 0; + virtual bool isAllEmpty() = 0; + virtual bool isTopSolidBlocking(int x, int y, int z) = 0; + virtual ~LevelSource() {} +}; \ No newline at end of file diff --git a/Minecraft.World/LevelStorage.cpp b/Minecraft.World/LevelStorage.cpp new file mode 100644 index 00000000..7602e144 --- /dev/null +++ b/Minecraft.World/LevelStorage.cpp @@ -0,0 +1,6 @@ +#include "stdafx.h" + +#include "LevelStorage.h" + +const wstring LevelStorage::NETHER_FOLDER = L"DIM-1"; +const wstring LevelStorage::ENDER_FOLDER = L"DIM1/"; \ No newline at end of file diff --git a/Minecraft.World/LevelStorage.h b/Minecraft.World/LevelStorage.h new file mode 100644 index 00000000..25d2aba2 --- /dev/null +++ b/Minecraft.World/LevelStorage.h @@ -0,0 +1,37 @@ +#pragma once +using namespace std; + +#include "ConsoleSavePath.h" + +class PlayerIO; +class Dimension; +class ChunkStorage; +class LevelData; +class Player; +class File; + +class ConsoleSaveFile; + +class LevelStorage +{ +public: + static const wstring NETHER_FOLDER; + static const wstring ENDER_FOLDER; + + virtual LevelData *prepareLevel() = 0; + virtual void checkSession() = 0; + virtual ChunkStorage *createChunkStorage(Dimension *dimension) = 0; + virtual void saveLevelData(LevelData *levelData, vector > *players) = 0; + virtual void saveLevelData(LevelData *levelData) = 0; + virtual PlayerIO *getPlayerIO() = 0; + virtual void closeAll() = 0; + virtual ConsoleSavePath getDataFile(const wstring& id) = 0; + virtual wstring getLevelId() = 0; + +public: + virtual ConsoleSaveFile *getSaveFile() { return NULL; } + virtual void flushSaveFile(bool autosave) {} + + // 4J Added + virtual int getAuxValueForMap(PlayerUID xuid, int dimension, int centreXC, int centreZC, int scale) { return 0; } +}; diff --git a/Minecraft.World/LevelStorageProfilerDecorator.cpp b/Minecraft.World/LevelStorageProfilerDecorator.cpp new file mode 100644 index 00000000..b6a4276e --- /dev/null +++ b/Minecraft.World/LevelStorageProfilerDecorator.cpp @@ -0,0 +1,57 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.chunk.storage.h" +#include "net.minecraft.world.level.dimension.h" +#include "LevelStorageProfilerDecorator.h" + +#include "ConsoleSaveFileIO.h" + +LevelStorageProfilerDecorator::LevelStorageProfilerDecorator(LevelStorage *capsulated) : capsulated ( capsulated ) +{ +} + +LevelData *LevelStorageProfilerDecorator::prepareLevel() +{ + return capsulated->prepareLevel(); +} + +void LevelStorageProfilerDecorator::checkSession() // throws LevelConflictException +{ + capsulated->checkSession(); +} + +ChunkStorage *LevelStorageProfilerDecorator::createChunkStorage(Dimension *dimension) +{ + return new ChunkStorageProfilerDecorator(capsulated->createChunkStorage(dimension)); +} + +void LevelStorageProfilerDecorator::saveLevelData(LevelData *levelData, vector > *players) +{ + capsulated->saveLevelData(levelData, players); +} + +void LevelStorageProfilerDecorator::saveLevelData(LevelData *levelData) +{ + capsulated->saveLevelData(levelData); +} + +PlayerIO *LevelStorageProfilerDecorator::getPlayerIO() +{ + return capsulated->getPlayerIO(); +} + +void LevelStorageProfilerDecorator::closeAll() +{ + capsulated->closeAll(); +} + +ConsoleSavePath LevelStorageProfilerDecorator::getDataFile(const wstring& id) +{ + return capsulated->getDataFile(id); +} + +wstring LevelStorageProfilerDecorator::getLevelId() +{ + return capsulated->getLevelId(); +} \ No newline at end of file diff --git a/Minecraft.World/LevelStorageProfilerDecorator.h b/Minecraft.World/LevelStorageProfilerDecorator.h new file mode 100644 index 00000000..98ed7620 --- /dev/null +++ b/Minecraft.World/LevelStorageProfilerDecorator.h @@ -0,0 +1,27 @@ +#pragma once +using namespace std; + +#include "LevelStorage.h" + +class ConsoleSaveFile; + +class LevelStorageProfilerDecorator : public LevelStorage +{ +private: + /* final */ LevelStorage *capsulated; + +public: + virtual ConsoleSaveFile *getSaveFile() { return capsulated->getSaveFile(); } + +public: + LevelStorageProfilerDecorator(LevelStorage *capsulated); + LevelData *prepareLevel(); + void checkSession(); + ChunkStorage *createChunkStorage(Dimension *dimension); + void saveLevelData(LevelData *levelData, vector > *players); + void saveLevelData(LevelData *levelData); + PlayerIO *getPlayerIO(); + void closeAll(); + ConsoleSavePath getDataFile(const wstring& id); + wstring getLevelId(); +}; \ No newline at end of file diff --git a/Minecraft.World/LevelStorageSource.h b/Minecraft.World/LevelStorageSource.h new file mode 100644 index 00000000..16b8df88 --- /dev/null +++ b/Minecraft.World/LevelStorageSource.h @@ -0,0 +1,37 @@ +#pragma once +using namespace std; + +#include "stdafx.h" + +class LevelSummary; +class ProgressListener; +class LevelData; +class LevelStorage; +class ConsoleSaveFile; + +class LevelStorageSource +{ +public: + virtual wstring getName() = 0; + virtual shared_ptr selectLevel(ConsoleSaveFile *saveFile, const wstring& levelId, bool createPlayerDir) = 0; + virtual vector *getLevelList() = 0; + virtual void clearAll() = 0; + virtual LevelData *getDataTagFor(ConsoleSaveFile *saveFile, const wstring& levelId) = 0; + + /** + * Tests if a levelId can be used to store a level. For example, a levelId + * can't be called COM1 on Windows systems, because that is a reserved file + * handle. + *

+ * Also, a new levelId may not overwrite an existing one. + * + * @param levelId + * @return + */ + virtual bool isNewLevelIdAcceptable(const wstring& levelId) = 0; + virtual void deleteLevel(const wstring& levelId) = 0; + virtual void renameLevel(const wstring& levelId, const wstring& newLevelName) = 0; + virtual bool isConvertible(ConsoleSaveFile *saveFile, const wstring& levelId) = 0; + virtual bool requiresConversion(ConsoleSaveFile *saveFile, const wstring& levelId) = 0; + virtual bool convertLevel(ConsoleSaveFile *saveFile, const wstring &levelId, ProgressListener *progress) = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/LevelSummary.cpp b/Minecraft.World/LevelSummary.cpp new file mode 100644 index 00000000..d8e7ddee --- /dev/null +++ b/Minecraft.World/LevelSummary.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "LevelSummary.h" + +LevelSummary::LevelSummary(const wstring& levelId, const wstring& levelName, __int64 lastPlayed, __int64 sizeOnDisk, GameType *gameMode, bool requiresConversion, bool hardcore, bool hasCheats) : + levelId( levelId ), + levelName( levelName ), + lastPlayed( lastPlayed ), + sizeOnDisk( sizeOnDisk ), + gameMode( gameMode ), + requiresConversion( requiresConversion ), + hardcore( hardcore ), + _hasCheats( hasCheats ) +{ +} + +wstring LevelSummary::getLevelId() +{ + return levelId; +} + +wstring LevelSummary::getLevelName() +{ + return levelName; +} + +__int64 LevelSummary::getSizeOnDisk() +{ + return sizeOnDisk; +} + +bool LevelSummary::isRequiresConversion() +{ + return requiresConversion; +} + +__int64 LevelSummary::getLastPlayed() +{ + return lastPlayed; +} + +int LevelSummary::compareTo(LevelSummary *rhs) +{ + if (lastPlayed < rhs->lastPlayed) + { + return 1; + } + if (lastPlayed > rhs->lastPlayed) + { + return -1; + } + + // TODO 4J Jev, used to be compareTo in java, is this right? + return levelId.compare(rhs->levelId); +} + +GameType *LevelSummary::getGameMode() +{ + return gameMode; +} + +bool LevelSummary::isHardcore() +{ + return hardcore; +} + +bool LevelSummary::hasCheats() +{ + return _hasCheats; +} \ No newline at end of file diff --git a/Minecraft.World/LevelSummary.h b/Minecraft.World/LevelSummary.h new file mode 100644 index 00000000..bbf391ff --- /dev/null +++ b/Minecraft.World/LevelSummary.h @@ -0,0 +1,28 @@ +#pragma once +using namespace std; + +class GameType; + +class LevelSummary +{ + const wstring levelId; + const wstring levelName; + const __int64 lastPlayed; + const __int64 sizeOnDisk; + const bool requiresConversion; + GameType *gameMode; + const bool hardcore; + const bool _hasCheats; + +public: + LevelSummary(const wstring& levelId, const wstring& levelName, __int64 lastPlayed, __int64 sizeOnDisk, GameType *gameMode, bool requiresConversion, bool hardcore, bool hasCheats); + wstring getLevelId(); + wstring getLevelName(); + __int64 getSizeOnDisk(); + bool isRequiresConversion(); + __int64 getLastPlayed(); + int compareTo(LevelSummary *rhs); + GameType *getGameMode(); + bool isHardcore(); + bool hasCheats(); +}; diff --git a/Minecraft.World/LevelType.cpp b/Minecraft.World/LevelType.cpp new file mode 100644 index 00000000..2fe1df95 --- /dev/null +++ b/Minecraft.World/LevelType.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" + + +// public static final LevelType[] levelTypes = new LevelType[16]; +// +// + +// +// +// private final String generatorName; +// private final int version; +// private boolean selectable; +// private boolean replacement; + +LevelType *LevelType::levelTypes[16];// = new LevelType[16]; +LevelType *LevelType::lvl_normal=NULL;// = new LevelType(0, "default", 1).setHasReplacement(); +LevelType *LevelType::lvl_flat=NULL;// = new LevelType(1, "flat"); +LevelType *LevelType::lvl_largeBiomes = NULL;// = new LevelType(2, "largeBiomes"); +LevelType *LevelType::lvl_normal_1_1=NULL;// = new LevelType(8, "default_1_1", 0).setSelectableByUser(false); + +void LevelType::staticCtor() +{ + for(int i=0;i<16;i++) levelTypes[i]=NULL; + lvl_normal = new LevelType(0, L"default", 1); + lvl_normal->setHasReplacement(); + lvl_flat = new LevelType(1, L"flat"); + lvl_largeBiomes = new LevelType(2, L"largeBiomes"); + lvl_normal_1_1 = new LevelType(8, L"default_1_1", 0); + lvl_normal_1_1->setSelectableByUser(false); +} + +LevelType::LevelType(int id, wstring generatorName) +{ + init(id, generatorName, 0); +} + +LevelType::LevelType(int id, wstring generatorName, int version) +{ + m_generatorName = generatorName; + m_version = version; + m_selectable = true; + levelTypes[id] = this; +} + + +void LevelType::init(int id, wstring generatorName, int version) +{ + m_generatorName = generatorName; + m_version = version; + m_selectable = true; + levelTypes[id] = this; +} + +wstring LevelType::getGeneratorName() +{ + return m_generatorName; +} + +wstring LevelType::getDescriptionId() +{ + return L"generator." + m_generatorName; +} + +int LevelType::getVersion() +{ + return m_version; +} + +LevelType *LevelType::getReplacementForVersion(int oldVersion) +{ + if (this == lvl_normal && oldVersion == 0) + { + return lvl_normal_1_1; + } + return this; +} + +LevelType *LevelType::setSelectableByUser(bool selectable) +{ + m_selectable = selectable; + return this; +} + +bool LevelType::isSelectable() +{ + return m_selectable; +} + +LevelType *LevelType::setHasReplacement() +{ + m_replacement = true; + return this; +} + +bool LevelType::hasReplacement() +{ + return m_replacement; +} + +LevelType *LevelType::getLevelType(wstring name) +{ + if(name.length()>0) + { + for (int i = 0; i < 16; i++) + { + wstring genname=levelTypes[i]->m_generatorName; + + if (levelTypes[i] != NULL && (genname.compare(name)==0)) + { + return levelTypes[i]; + } + } + } + return NULL; +} + + diff --git a/Minecraft.World/LevelType.h b/Minecraft.World/LevelType.h new file mode 100644 index 00000000..a269c583 --- /dev/null +++ b/Minecraft.World/LevelType.h @@ -0,0 +1,39 @@ +#pragma once +using namespace std; +#include "net.minecraft.world.level.h" + +class LevelType +{ +public: + static LevelType *levelTypes[16];// = new LevelType[16]; + static LevelType *lvl_normal;// = new LevelType(0, "default", 1).setHasReplacement(); + static LevelType *lvl_flat;// = new LevelType(1, "flat"); + static LevelType *lvl_largeBiomes;// = new LevelType(2, "largeBiomes"); + static LevelType *lvl_normal_1_1;// = new LevelType(8, "default_1_1", 0).setSelectableByUser(false); + + static void staticCtor(); + +private: + wstring m_generatorName; + int m_version; + bool m_selectable; + bool m_replacement; + + LevelType(int id, wstring generatorName); + LevelType(int id, wstring generatorName, int version); + void init(int id, wstring generatorName, int version); +public: + wstring getGeneratorName(); + wstring getDescriptionId(); + int getVersion(); + LevelType *getReplacementForVersion(int oldVersion); +private: + LevelType *setSelectableByUser(bool selectable); +public: + bool isSelectable(); +private: + LevelType *setHasReplacement(); +public: + bool hasReplacement(); + static LevelType *getLevelType(wstring name); +}; diff --git a/Minecraft.World/LeverTile.cpp b/Minecraft.World/LeverTile.cpp new file mode 100644 index 00000000..a8231251 --- /dev/null +++ b/Minecraft.World/LeverTile.cpp @@ -0,0 +1,309 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.h" +#include "LeverTile.h" +#include "SoundTypes.h" + +LeverTile::LeverTile(int id) : Tile(id, Material::decoration,isSolidRender()) +{ +} + +AABB *LeverTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +bool LeverTile::blocksLight() +{ + return false; +} + +bool LeverTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool LeverTile::isCubeShaped() +{ + return false; +} + +int LeverTile::getRenderShape() +{ + return Tile::SHAPE_LEVER; +} + +bool LeverTile::mayPlace(Level *level, int x, int y, int z, int face) +{ + if (face == 0 && level->isSolidBlockingTile(x, y + 1, z)) return true; + if (face == 1 && level->isTopSolidBlocking(x, y - 1, z)) return true; + if (face == 2 && level->isSolidBlockingTile(x, y, z + 1)) return true; + if (face == 3 && level->isSolidBlockingTile(x, y, z - 1)) return true; + if (face == 4 && level->isSolidBlockingTile(x + 1, y, z)) return true; + if (face == 5 && level->isSolidBlockingTile(x - 1, y, z)) return true; + return false; +} + +bool LeverTile::mayPlace(Level *level, int x, int y, int z) +{ + if (level->isSolidBlockingTile(x - 1, y, z)) + { + return true; + } else if (level->isSolidBlockingTile(x + 1, y, z)) + { + return true; + } else if (level->isSolidBlockingTile(x, y, z - 1)) + { + return true; + } else if (level->isSolidBlockingTile(x, y, z + 1)) + { + return true; + } else if (level->isTopSolidBlocking(x, y - 1, z)) + { + return true; + } + else if (level->isSolidBlockingTile(x, y + 1, z)) + { + return true; + } + return false; +} + +int LeverTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) +{ + int dir = itemValue; + + int oldFlip = dir & 8; + dir &= 7; + + dir = -1; + + if (face == Facing::DOWN && level->isSolidBlockingTile(x, y + 1, z)) dir = 0; + if (face == Facing::UP && level->isTopSolidBlocking(x, y - 1, z)) dir = 5; + if (face == Facing::NORTH && level->isSolidBlockingTile(x, y, z + 1)) dir = 4; + if (face == Facing::SOUTH && level->isSolidBlockingTile(x, y, z - 1)) dir = 3; + if (face == Facing::WEST && level->isSolidBlockingTile(x + 1, y, z)) dir = 2; + if (face == Facing::EAST && level->isSolidBlockingTile(x - 1, y, z)) dir = 1; + + return dir + oldFlip; +} + +int LeverTile::getLeverFacing(int facing) +{ + switch (facing) + { + case Facing::DOWN: + return 0; + case Facing::UP: + return 5; + case Facing::NORTH: + return 4; + case Facing::SOUTH: + return 3; + case Facing::WEST: + return 2; + case Facing::EAST: + return 1; + } + return -1; +} + +void LeverTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (checkCanSurvive(level, x, y, z)) + { + int dir = level->getData(x, y, z) & 7; + bool replace = false; + + if (!level->isSolidBlockingTile(x - 1, y, z) && dir == 1) replace = true; + if (!level->isSolidBlockingTile(x + 1, y, z) && dir == 2) replace = true; + if (!level->isSolidBlockingTile(x, y, z - 1) && dir == 3) replace = true; + if (!level->isSolidBlockingTile(x, y, z + 1) && dir == 4) replace = true; + if (!level->isTopSolidBlocking(x, y - 1, z) && dir == 5) replace = true; + if (!level->isTopSolidBlocking(x, y - 1, z) && dir == 6) replace = true; + if (!level->isSolidBlockingTile(x, y + 1, z) && dir == 0) replace = true; + if (!level->isSolidBlockingTile(x, y + 1, z) && dir == 7) replace = true; + + if (replace) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } + } + +} + +bool LeverTile::checkCanSurvive(Level *level, int x, int y, int z) +{ + if (!mayPlace(level, x, y, z)) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + return false; + } + return true; +} + +void LeverTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + int dir = level->getData(x, y, z) & 7; + float r = 3 / 16.0f; + if (dir == 1) + { + setShape(0, 0.2f, 0.5f - r, r * 2, 0.8f, 0.5f + r); + } + else if (dir == 2) + { + setShape(1 - r * 2, 0.2f, 0.5f - r, 1, 0.8f, 0.5f + r); + } + else if (dir == 3) + { + setShape(0.5f - r, 0.2f, 0, 0.5f + r, 0.8f, r * 2); + } + else if (dir == 4) + { + setShape(0.5f - r, 0.2f, 1 - r * 2, 0.5f + r, 0.8f, 1); + } + else if (dir == 5 || dir == 6) + { + r = 4 / 16.0f; + setShape(0.5f - r, 0.0f, 0.5f - r, 0.5f + r, 0.6f, 0.5f + r); + } + else if (dir == 0 || dir == 7) + { + r = 4 / 16.0f; + setShape(0.5f - r, 0.4f, 0.5f - r, 0.5f + r, 1.0f, 0.5f + r); + } +} + +void LeverTile::attack(Level *level, int x, int y, int z, shared_ptr player) +{ + use(level, x, y, z, player, 0, 0, 0, 0); +} + +// 4J-PB - Adding a TestUse for tooltip display +bool LeverTile::TestUse() +{ + return true; +} + +bool LeverTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if( soundOnly ) + { + // 4J - added - just do enough to play the sound + int data = level->getData(x, y, z); + int dir = data & 7; + int open = 8 - (data & 8); + level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_CLICK, 0.3f, open > 0 ? 0.6f : 0.5f); + return false; + } + if (level->isClientSide) + { + // 4J - added stuff to play sound in this case too + int data = level->getData(x, y, z); + int dir = data & 7; + int open = 8 - (data & 8); + level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_CLICK, 0.3f, open > 0 ? 0.6f : 0.5f); + + return true; + } + int data = level->getData(x, y, z); + int dir = data & 7; + int open = 8 - (data & 8); + + level->setData(x, y, z, dir + open); + level->setTilesDirty(x, y, z, x, y, z); + + level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_RANDOM_CLICK, 0.3f, open > 0 ? 0.6f : 0.5f); + + level->updateNeighborsAt(x, y, z, id); + if (dir == 1) + { + level->updateNeighborsAt(x - 1, y, z, id); + } + else if (dir == 2) + { + level->updateNeighborsAt(x + 1, y, z, id); + } + else if (dir == 3) + { + level->updateNeighborsAt(x, y, z - 1, id); + } + else if (dir == 4) + { + level->updateNeighborsAt(x, y, z + 1, id); + } + else if (dir == 5 || dir == 6) + { + level->updateNeighborsAt(x, y - 1, z, id); + } + else if (dir == 0 || dir == 7) + { + level->updateNeighborsAt(x, y + 1, z, id); + } + + return true; +} + +void LeverTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + if ((data & 8) > 0) + { + level->updateNeighborsAt(x, y, z, this->id); + int dir = data & 7; + if (dir == 1) + { + level->updateNeighborsAt(x - 1, y, z, this->id); + } + else if (dir == 2) + { + level->updateNeighborsAt(x + 1, y, z, this->id); + } + else if (dir == 3) + { + level->updateNeighborsAt(x, y, z - 1, this->id); + } + else if (dir == 4) + { + level->updateNeighborsAt(x, y, z + 1, this->id); + } + else if (dir == 5 || dir == 6) + { + level->updateNeighborsAt(x, y - 1, z, this->id); + } + else if (dir == 0 || dir == 7) + { + level->updateNeighborsAt(x, y + 1, z, this->id); + } + } + Tile::onRemove(level, x, y, z, id, data); +} + +bool LeverTile::getSignal(LevelSource *level, int x, int y, int z, int dir) +{ + return (level->getData(x, y, z) & 8) > 0; +} + +bool LeverTile::getDirectSignal(Level *level, int x, int y, int z, int dir) +{ + int data = level->getData(x, y, z); + if ((data & 8) == 0) return false; + int myDir = data & 7; + + if (myDir == 0 && dir == 0) return true; + if (myDir == 7 && dir == 0) return true; + if (myDir == 6 && dir == 1) return true; + if (myDir == 5 && dir == 1) return true; + if (myDir == 4 && dir == 2) return true; + if (myDir == 3 && dir == 3) return true; + if (myDir == 2 && dir == 4) return true; + if (myDir == 1 && dir == 5) return true; + + return false; +} + +bool LeverTile::isSignalSource() +{ + return true; +} diff --git a/Minecraft.World/LeverTile.h b/Minecraft.World/LeverTile.h new file mode 100644 index 00000000..4d00db78 --- /dev/null +++ b/Minecraft.World/LeverTile.h @@ -0,0 +1,31 @@ +#pragma once +#include "Tile.h" + +class LeverTile : public Tile +{ + friend class Tile; +protected: + LeverTile(int id); +public: + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual bool blocksLight(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual int getRenderShape(); + virtual bool mayPlace(Level *level, int x, int y, int z, int face); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + static int getLeverFacing(int facing); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); +private: + virtual bool checkCanSurvive(Level *level, int x, int y, int z); +public: + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual void attack(Level *level, int x, int y, int z, shared_ptr player); + virtual bool TestUse(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); + virtual bool getSignal(LevelSource *level, int x, int y, int z, int dir); + virtual bool getDirectSignal(Level *level, int x, int y, int z, int dir); + virtual bool isSignalSource(); +}; diff --git a/Minecraft.World/LightGemFeature.cpp b/Minecraft.World/LightGemFeature.cpp new file mode 100644 index 00000000..f42d78ff --- /dev/null +++ b/Minecraft.World/LightGemFeature.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "LightGemFeature.h" + +bool LightGemFeature::place(Level *level, Random *random, int x, int y, int z) +{ + if (!level->isEmptyTile(x, y, z)) return false; + if (level->getTile(x, y + 1, z) != Tile::hellRock_Id) return false; + level->setTile(x, y, z, Tile::lightGem_Id); + + for (int i = 0; i < 1500; i++) + { + int x2 = x + random->nextInt(8) - random->nextInt(8); + int y2 = y - random->nextInt(12); + int z2 = z + random->nextInt(8) - random->nextInt(8); + if (level->getTile(x2, y2, z2) != 0) continue; + + int count = 0; + for (int t = 0; t < 6; t++) + { + int tile = 0; + if (t == 0) tile = level->getTile(x2 - 1, y2, z2); + if (t == 1) tile = level->getTile(x2 + 1, y2, z2); + if (t == 2) tile = level->getTile(x2, y2 - 1, z2); + if (t == 3) tile = level->getTile(x2, y2 + 1, z2); + if (t == 4) tile = level->getTile(x2, y2, z2 - 1); + if (t == 5) tile = level->getTile(x2, y2, z2 + 1); + + if (tile == Tile::lightGem_Id) count++; + } + + if (count == 1) level->setTile(x2, y2, z2, Tile::lightGem_Id); + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/LightGemFeature.h b/Minecraft.World/LightGemFeature.h new file mode 100644 index 00000000..1eaf5c08 --- /dev/null +++ b/Minecraft.World/LightGemFeature.h @@ -0,0 +1,8 @@ +#pragma once +#include "Feature.h" + +class LightGemFeature : public Feature +{ +public: + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/LightGemTile.cpp b/Minecraft.World/LightGemTile.cpp new file mode 100644 index 00000000..3aa7d101 --- /dev/null +++ b/Minecraft.World/LightGemTile.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "LightGemTile.h" +#include "net.minecraft.world.item.h" + +LightGemTile::LightGemTile(int id, Material *material) : Tile(id, material) +{ +} + +int LightGemTile::getResourceCountForLootBonus(int bonusLevel, Random *random) +{ + return Mth::clamp(getResourceCount(random) + random->nextInt(bonusLevel + 1), 1, 4); +} + +int LightGemTile::getResourceCount(Random *random) +{ + return 2 + random->nextInt(3); +} + +int LightGemTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::yellowDust->id; +} \ No newline at end of file diff --git a/Minecraft.World/LightGemTile.h b/Minecraft.World/LightGemTile.h new file mode 100644 index 00000000..fade11ed --- /dev/null +++ b/Minecraft.World/LightGemTile.h @@ -0,0 +1,13 @@ +#pragma once +#include "Tile.h" + +class Random; + +class LightGemTile : public Tile +{ +public: + LightGemTile(int id, Material *material); + virtual int getResourceCountForLootBonus(int bonusLevel, Random *random); + virtual int getResourceCount(Random *random); + virtual int getResource(int data, Random *random, int playerBonusLevel); +}; \ No newline at end of file diff --git a/Minecraft.World/LightLayer.h b/Minecraft.World/LightLayer.h new file mode 100644 index 00000000..bceab76b --- /dev/null +++ b/Minecraft.World/LightLayer.h @@ -0,0 +1,14 @@ +#pragma once + +// 4J Stu Richard has added the class like this to the stubs, although the original Java code +// had an enum with a "surrounding" data member, which is set to the value we are setting the enum to + +class LightLayer +{ +public: + enum variety + { + Sky = 15, + Block = 0, + }; +}; \ No newline at end of file diff --git a/Minecraft.World/LightningBolt.cpp b/Minecraft.World/LightningBolt.cpp new file mode 100644 index 00000000..901bedf1 --- /dev/null +++ b/Minecraft.World/LightningBolt.cpp @@ -0,0 +1,143 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "LightningBolt.h" +#include "SoundTypes.h" +#include "..\Minecraft.Client\MinecraftServer.h" +#include "..\Minecraft.Client\PlayerList.h" +#include "net.minecraft.world.level.dimension.h" + + +LightningBolt::LightningBolt(Level *level, double x, double y, double z) : + life( 0 ), + seed( 0 ), + flashes( 0 ), + GlobalEntity( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + moveTo(x, y, z, 0, 0); + life = START_LIFE; + seed = random->nextLong(); + // 4J-PB - Microsoft request due to photosensitivity issue with multiple flashes of lightning + //flashes = random->nextInt(3) + 1; + flashes = 1; + + // 4J - added clientside check + if( !level->isClientSide ) + { + if (level->difficulty >= 2 && level->hasChunksAt( Mth::floor(x), Mth::floor(y), Mth::floor(z), 10)) + { + { + int xt = Mth::floor(x); + int yt = Mth::floor(y); + int zt = Mth::floor(z); + // 4J added - don't go setting tiles if we aren't tracking them for network synchronisation + if( MinecraftServer::getInstance()->getPlayers()->isTrackingTile(xt, yt, zt, level->dimension->id) ) + { + if (level->getTile(xt, yt, zt) == 0 && Tile::fire->mayPlace(level, xt, yt, zt)) level->setTile(xt, yt, zt, Tile::fire_Id); + } + } + + for (int i = 0; i < 4; i++) + { + int xt = Mth::floor(x) + random->nextInt(3) - 1; + int yt = Mth::floor(y) + random->nextInt(3) - 1; + int zt = Mth::floor(z) + random->nextInt(3) - 1; + // 4J added - don't go setting tiles if we aren't tracking them for network synchronisation + if( MinecraftServer::getInstance()->getPlayers()->isTrackingTile(xt, yt, zt, level->dimension->id) ) + { + if (level->getTile(xt, yt, zt) == 0 && Tile::fire->mayPlace(level, xt, yt, zt)) level->setTile(xt, yt, zt, Tile::fire_Id); + } + } + } + } +} + +void LightningBolt::tick() +{ + GlobalEntity::tick(); + + if (life == START_LIFE) { + // 4J-PB - this volume seems off the scale! But the volume is used to check the distance from the camera player - (volume*32) squared + // so we'll limit the sound in the sound engine + level->playSound(x, y, z, eSoundType_AMBIENT_WEATHER_THUNDER, 10000, 0.8f + random->nextFloat() * 0.2f); + level->playSound(x, y, z, eSoundType_RANDOM_EXPLODE, 2, 0.5f + random->nextFloat() * 0.2f); + } + + life--; + if (life < 0) + { + if (flashes == 0) + { + remove(); + } + else if (life < -random->nextInt(10)) + { + flashes--; + life = 1; + // 4J - added clientside check + if( !level->isClientSide ) + { + seed = random->nextLong(); + if (level->hasChunksAt( (int) floor(x), (int) floor(y), (int) floor(z), 10)) + { + int xt = (int) floor(x); + int yt = (int) floor(y); + int zt = (int) floor(z); + + // 4J added - don't go setting tiles if we aren't tracking them for network synchronisation + if( MinecraftServer::getInstance()->getPlayers()->isTrackingTile(xt, yt, zt, level->dimension->id) ) + { + if (level->getTile(xt, yt, zt) == 0 && Tile::fire->mayPlace(level, xt, yt, zt)) level->setTile(xt, yt, zt, Tile::fire_Id); + } + } + } + } + } + + if (life >= 0) + { + double r = 3; + // 4J - added clientside check + if( !level->isClientSide ) + { + vector > *entities = level->getEntities(shared_from_this(), AABB::newTemp(x - r, y - r, z - r, x + r, y + 6 + r, z + r)); + AUTO_VAR(itEnd, entities->end()); + for (AUTO_VAR(it, entities->begin()); it != itEnd; it++) + { + shared_ptr e = (*it); //entities->at(i); + e->thunderHit(this); + } + } + + level->lightningBoltTime = 2; + } +} + + +void LightningBolt::defineSynchedData() +{ +} + +void LightningBolt::readAdditionalSaveData(CompoundTag *tag) +{ +} + +void LightningBolt::addAdditonalSaveData(CompoundTag *tag) +{ +} + + +bool LightningBolt::shouldAlwaysRender() +{ + return true; +} + +bool LightningBolt::shouldRender(Vec3 *c) +{ + return life >= 0; +} diff --git a/Minecraft.World/LightningBolt.h b/Minecraft.World/LightningBolt.h new file mode 100644 index 00000000..28d5e004 --- /dev/null +++ b/Minecraft.World/LightningBolt.h @@ -0,0 +1,36 @@ +#pragma once +using namespace std; +#include "GlobalEntity.h" +#include "net.minecraft.world.level.tile.h" + + +//class LightningBolt : public GlobalEntity +class LightningBolt : public GlobalEntity +{ +public: + eINSTANCEOF GetType() { return eTYPE_LIGHTNINGBOLT; } + +private: + static const int START_LIFE = 2; + + int life; + +public: + __int64 seed; + +private: + int flashes; + +public: + LightningBolt(Level *level, double x, double y, double z); + virtual void tick(); + +protected: + virtual void defineSynchedData(); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual void addAdditonalSaveData(CompoundTag *tag); + +public: + bool shouldAlwaysRender(); + virtual bool shouldRender(Vec3 *c); +}; \ No newline at end of file diff --git a/Minecraft.World/LiquidMaterial.h b/Minecraft.World/LiquidMaterial.h new file mode 100644 index 00000000..15a3c294 --- /dev/null +++ b/Minecraft.World/LiquidMaterial.h @@ -0,0 +1,12 @@ +#pragma once +#include "Material.h" + +class LiquidMaterial : public Material +{ +public: + LiquidMaterial(MaterialColor *color) : Material(color) { replaceable(); destroyOnPush(); } + + virtual bool isLiquid() { return true; } + virtual bool blocksMotion() { return false; } + virtual bool isSolid() { return false; } +}; \ No newline at end of file diff --git a/Minecraft.World/LiquidTile.cpp b/Minecraft.World/LiquidTile.cpp new file mode 100644 index 00000000..466589a4 --- /dev/null +++ b/Minecraft.World/LiquidTile.cpp @@ -0,0 +1,418 @@ +#include "stdafx.h" +#include "JavaMath.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.h" +#include "LiquidTile.h" +#include "Facing.h" +#include "SoundTypes.h" + +const wstring LiquidTile::TEXTURE_LAVA_STILL = L"lava"; +const wstring LiquidTile::TEXTURE_WATER_STILL = L"water"; +const wstring LiquidTile::TEXTURE_WATER_FLOW = L"water_flow"; +const wstring LiquidTile::TEXTURE_LAVA_FLOW = L"lava_flow"; + +LiquidTile::LiquidTile(int id, Material *material) : Tile(id, material,isSolidRender()) +{ + float yo = 0; + float e = 0; + + setShape(0 + e, 0 + yo, 0 + e, 1 + e, 1 + yo, 1 + e); + setTicking(true); +} + +bool LiquidTile::isPathfindable(LevelSource *level, int x, int y, int z) +{ + return material != Material::lava; +} + +int LiquidTile::getColor() const +{ + return 0xffffff; +} + +int LiquidTile::getColor(LevelSource *level, int x, int y, int z) +{ + + return getColor(level, x, y, z, 0); +} + +int LiquidTile::getColor(LevelSource *level, int x, int y, int z, int d) +{ + if (material == Material::water) + { + int totalRed = 0; + int totalGreen = 0; + int totalBlue = 0; + + for (int oz = -1; oz <= 1; oz++) + { + for (int ox = -1; ox <= 1; ox++) + { + int waterColor = level->getBiome(x + ox, z + oz)->getWaterColor(); + + totalRed += (waterColor & 0xff0000) >> 16; + totalGreen += (waterColor & 0xff00) >> 8; + totalBlue += (waterColor & 0xff); + } + } + + return (((totalRed / 9) & 0xFF) << 16) | (((totalGreen / 9) & 0xFF) << 8) | (((totalBlue / 9) & 0xFF)); + } + return 0xffffff; +} + +float LiquidTile::getHeight(int d) +{ + if (d >= 8) d = 0; + return (d + 1) / 9.0f; +} + +Icon *LiquidTile::getTexture(int face, int data) +{ + if (face == Facing::DOWN || face == Facing::UP) + { + return icons[0]; + } + else + { + return icons[1]; + } +} + +int LiquidTile::getDepth(Level *level, int x, int y, int z) +{ + if (level->getMaterial(x, y, z) == material) return level->getData(x, y, z); + else return -1; +} + +int LiquidTile::getRenderedDepth(LevelSource *level, int x, int y, int z) +{ + if (level->getMaterial(x, y, z) != material) return -1; + int d = level->getData(x, y, z); + if (d >= 8) d = 0; + return d; +} + +bool LiquidTile::isCubeShaped() +{ + return false; +} + +bool LiquidTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool LiquidTile::mayPick(int data, bool liquid) +{ + return liquid && data == 0; +} + +bool LiquidTile::isSolidFace(LevelSource *level, int x, int y, int z, int face) +{ + Material *m = level->getMaterial(x, y, z); + if (m == this->material) return false; + if (face == Facing::UP) return true; + if (m == Material::ice) return false; + + return Tile::isSolidFace(level, x, y, z, face); +} + +bool LiquidTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + Material *m = level->getMaterial(x, y, z); + if (m == this->material) return false; + if (face == Facing::UP) return true; + if (m == Material::ice) return false; + return Tile::shouldRenderFace(level, x, y, z, face); +} + +AABB *LiquidTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +int LiquidTile::getRenderShape() +{ + return Tile::SHAPE_WATER; +} + +int LiquidTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return 0; +} + +int LiquidTile::getResourceCount(Random *random) +{ + return 0; +} + +Vec3 *LiquidTile::getFlow(LevelSource *level, int x, int y, int z) +{ + Vec3 *flow = Vec3::newTemp(0,0,0); + int mid = getRenderedDepth(level, x, y, z); + for (int d = 0; d < 4; d++) + { + + int xt = x; + int yt = y; + int zt = z; + + if (d == 0) xt--; + if (d == 1) zt--; + if (d == 2) xt++; + if (d == 3) zt++; + + int t = getRenderedDepth(level, xt, yt, zt); + if (t < 0) + { + if (!level->getMaterial(xt, yt, zt)->blocksMotion()) + { + t = getRenderedDepth(level, xt, yt - 1, zt); + if (t >= 0) + { + int dir = t - (mid - 8); + flow = flow->add((xt - x) * dir, (yt - y) * dir, (zt - z) * dir); + } + } + } else + { + if (t >= 0) + { + int dir = t - mid; + flow = flow->add((xt - x) * dir, (yt - y) * dir, (zt - z) * dir); + } + } + + } + if (level->getData(x, y, z) >= 8) + { + bool ok = false; + if (ok || isSolidFace(level, x, y, z - 1, 2)) ok = true; + if (ok || isSolidFace(level, x, y, z + 1, 3)) ok = true; + if (ok || isSolidFace(level, x - 1, y, z, 4)) ok = true; + if (ok || isSolidFace(level, x + 1, y, z, 5)) ok = true; + if (ok || isSolidFace(level, x, y + 1, z - 1, 2)) ok = true; + if (ok || isSolidFace(level, x, y + 1, z + 1, 3)) ok = true; + if (ok || isSolidFace(level, x - 1, y + 1, z, 4)) ok = true; + if (ok || isSolidFace(level, x + 1, y + 1, z, 5)) ok = true; + if (ok) flow = flow->normalize()->add(0, -6, 0); + } + flow = flow->normalize(); + return flow; +} + +void LiquidTile::handleEntityInside(Level *level, int x, int y, int z, shared_ptr e, Vec3 *current) +{ + Vec3 *flow = getFlow(level, x, y, z); + current->x += flow->x; + current->y += flow->y; + current->z += flow->z; +} + +int LiquidTile::getTickDelay() +{ + if (material == Material::water) return 5; + if (material == Material::lava) return 30; + return 0; +} + +// 4J - change brought forward from 1.8.2 +int LiquidTile::getLightColor(LevelSource *level, int x, int y, int z, int tileId/*=-1*/) +{ + // 4J - note that this code seems to basically be a hack to fix a problem where post-processed things like lakes aren't getting lit properly + int a = level->getLightColor(x, y, z, 0, tileId); + int b = level->getLightColor(x, y + 1, z, 0, tileId); + + int aa = a & 0xff; + int ba = b & 0xff; + int ab = (a >> 16) & 0xff; + int bb = (b >> 16) & 0xff; + + return (aa > ba ? aa : ba) | ((ab > bb ? ab : bb) << 16); +} + +float LiquidTile::getBrightness(LevelSource *level, int x, int y, int z) +{ + float a = level->getBrightness(x, y, z); + float b = level->getBrightness(x, y + 1, z); + return a > b ? a : b; +} + +int LiquidTile::getRenderLayer() +{ + return material == Material::water ? 1 : 0; +} + +void LiquidTile::animateTick(Level *level, int x, int y, int z, Random *random) +{ + if (material == Material::water) + { + if (random->nextInt(10) == 0) + { + int d = level->getData(x, y, z); + if (d <= 0 || d >= 8) + { + level->addParticle(eParticleType_suspended, x + random->nextFloat(), y + random->nextFloat(), z + random->nextFloat(), 0, 0, 0); + } + } + // 4J-PB - this loop won't run! + for (int i = 0; i < 0; i++) + { // This was an attempt to add foam to + // the bottoms of waterfalls. It + // didn't went ok. + int dir = random->nextInt(4); + int xt = x; + int zt = z; + if (dir == 0) xt--; + if (dir == 1) xt++; + if (dir == 2) zt--; + if (dir == 3) zt++; + if (level->getMaterial(xt, y, zt) == Material::air && (level->getMaterial(xt, y - 1, zt)->blocksMotion() || level->getMaterial(xt, y - 1, zt)->isLiquid())) + { + float r = 1 / 16.0f; + double xx = x + random->nextFloat(); + double yy = y + random->nextFloat(); + double zz = z + random->nextFloat(); + if (dir == 0) xx = x - r; + if (dir == 1) xx = x + 1 + r; + if (dir == 2) zz = z - r; + if (dir == 3) zz = z + 1 + r; + + double xd = 0; + double zd = 0; + + if (dir == 0) xd = -r; + if (dir == 1) xd = +r; + if (dir == 2) zd = -r; + if (dir == 3) zd = +r; + + level->addParticle(eParticleType_splash, xx, yy, zz, xd, 0, zd); + } + } + } + if (material == Material::water && random->nextInt(64) == 0) + { + int d = level->getData(x, y, z); + if (d > 0 && d < 8) + { + level->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_LIQUID_WATER, random->nextFloat() * 0.25f + 0.75f, random->nextFloat() * 1.0f + 0.5f); + } + } + if (material == Material::lava) + { + if (level->getMaterial(x, y + 1, z) == Material::air && !level->isSolidRenderTile(x, y + 1, z)) + { + if (random->nextInt(100) == 0) + { + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + double xx = x + random->nextFloat(); + double yy = y + tls->yy1; + double zz = z + random->nextFloat(); + level->addParticle(eParticleType_lava, xx, yy, zz, 0, 0, 0); + // 4J - new sound brought forward from 1.2.3 + level->playLocalSound(xx, yy, zz, eSoundType_LIQUID_LAVA_POP, 0.2f + random->nextFloat() * 0.2f, 0.9f + random->nextFloat() * 0.15f); + } + // 4J - new sound brought forward from 1.2.3 + if (random->nextInt(200) == 0) + { + level->playLocalSound(x, y, z, eSoundType_LIQUID_LAVA, 0.2f + random->nextFloat() * 0.2f, 0.9f + random->nextFloat() * 0.15f); + } + } + } + + if (random->nextInt(10) == 0) + { + if (level->isTopSolidBlocking(x, y - 1, z) && !level->getMaterial(x, y - 2, z)->blocksMotion()) + { + double xx = x + random->nextFloat(); + double yy = y - 1.05; + double zz = z + random->nextFloat(); + + if (material == Material::water) level->addParticle(eParticleType_dripWater, xx, yy, zz, 0, 0, 0); + else level->addParticle(eParticleType_dripLava, xx, yy, zz, 0, 0, 0); + } + } +} + +double LiquidTile::getSlopeAngle(LevelSource *level, int x, int y, int z, Material *m) +{ + Vec3 *flow = NULL; + if (m == Material::water) flow = ((LiquidTile *) Tile::water)->getFlow(level, x, y, z); + if (m == Material::lava) flow = ((LiquidTile *) Tile::lava)->getFlow(level, x, y, z); + if (flow->x == 0 && flow->z == 0) return -1000; + return atan2(flow->z, flow->x) - PI / 2; +} + +void LiquidTile::onPlace(Level *level, int x, int y, int z) +{ + updateLiquid(level, x, y, z); +} + +void LiquidTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + updateLiquid(level, x, y, z); +} + +void LiquidTile::updateLiquid(Level *level, int x, int y, int z) +{ + if (level->getTile(x, y, z) != id) return; + if (material == Material::lava) + { + bool water = false; + if (water || level->getMaterial(x, y, z - 1) == Material::water) water = true; + if (water || level->getMaterial(x, y, z + 1) == Material::water) water = true; + if (water || level->getMaterial(x - 1, y, z) == Material::water) water = true; + if (water || level->getMaterial(x + 1, y, z) == Material::water) water = true; + if (water || level->getMaterial(x, y + 1, z) == Material::water) water = true; + if (water) + { + int data = level->getData(x, y, z); + if (data == 0) + { + level->setTile(x, y, z, Tile::obsidian_Id); + } + else if (data <= 4) + { + level->setTile(x, y, z, Tile::stoneBrick_Id); + } + fizz(level, x, y, z); + } + } + +} + +void LiquidTile::fizz(Level *level, int x, int y, int z) +{ + MemSect(31); + level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_RANDOM_FIZZ, 0.5f, 2.6f + (level->random->nextFloat() - level->random->nextFloat()) * 0.8f); + MemSect(0); + for (int i = 0; i < 8; i++) + { + level->addParticle(eParticleType_largesmoke, x +Math::random(), y + 1.2, z + Math::random(), 0, 0, 0); + } +} + +void LiquidTile::registerIcons(IconRegister *iconRegister) +{ + if (material == Material::lava) + { + icons[0] = iconRegister->registerIcon(TEXTURE_LAVA_STILL); + icons[1] = iconRegister->registerIcon(TEXTURE_LAVA_FLOW); + } + else + { + icons[0] = iconRegister->registerIcon(TEXTURE_WATER_STILL); + icons[1] = iconRegister->registerIcon(TEXTURE_WATER_FLOW); + } +} + +Icon *LiquidTile::getTexture(const wstring &name) +{ + if (name.compare(TEXTURE_WATER_STILL)==0) return Tile::water->icons[0]; + if (name.compare(TEXTURE_WATER_FLOW)==0) return Tile::water->icons[1]; + if (name.compare(TEXTURE_LAVA_STILL)==0) return Tile::lava->icons[0]; + if (name.compare(TEXTURE_LAVA_FLOW)==0) return Tile::lava->icons[1]; + return NULL; +} diff --git a/Minecraft.World/LiquidTile.h b/Minecraft.World/LiquidTile.h new file mode 100644 index 00000000..67eb5b65 --- /dev/null +++ b/Minecraft.World/LiquidTile.h @@ -0,0 +1,62 @@ +#pragma once +#include "Tile.h" +#include "Definitions.h" + +class Random; +class ChunkRebuildData; + +class LiquidTile : public Tile +{ + friend class ChunkRebuildData; +public: + static const wstring TEXTURE_LAVA_STILL; + static const wstring TEXTURE_WATER_STILL; + static const wstring TEXTURE_WATER_FLOW; + static const wstring TEXTURE_LAVA_FLOW; + +private: + Icon *icons[2]; + +protected: + LiquidTile(int id, Material *material); +public: + virtual bool isPathfindable(LevelSource *level, int x, int y, int z); + virtual int getColor() const; + virtual int getColor(LevelSource *level, int x, int y, int z); + virtual int getColor(LevelSource *level, int x, int y, int z, int data); // 4J added + static float getHeight(int d); + virtual Icon *getTexture(int face, int data); +protected: + virtual int getDepth(Level *level, int x, int y, int z); + virtual int getRenderedDepth(LevelSource *level, int x, int y, int z); +public: + virtual bool isCubeShaped(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool mayPick(int data, bool liquid); + virtual bool isSolidFace(LevelSource *level, int x, int y, int z, int face); + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual int getRenderShape(); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int getResourceCount(Random *random); +private: + virtual Vec3 *getFlow(LevelSource *level, int x, int y, int z); +public: + virtual void handleEntityInside(Level *level, int x, int y, int z, shared_ptr e, Vec3 *current); + virtual int getTickDelay(); + virtual int getLightColor(LevelSource *level, int x, int y, int z, int tileId=-1); // 4J - brought forward from 1.8.2 + virtual float getBrightness(LevelSource *level, int x, int y, int z); + virtual int getRenderLayer(); + virtual void animateTick(Level *level, int x, int y, int z, Random *random); + static double getSlopeAngle(LevelSource *level, int x, int y, int z, Material *m); + virtual void onPlace(Level *level, int x, int y, int z); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); +private: + virtual void updateLiquid(Level *level, int x, int y, int z); +protected: + virtual void fizz(Level *level, int x, int y, int z); + +public: + void registerIcons(IconRegister *iconRegister); + static Icon *getTexture(const wstring &name); +}; \ No newline at end of file diff --git a/Minecraft.World/LiquidTileDynamic.cpp b/Minecraft.World/LiquidTileDynamic.cpp new file mode 100644 index 00000000..14a93b93 --- /dev/null +++ b/Minecraft.World/LiquidTileDynamic.cpp @@ -0,0 +1,342 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "LiquidTileDynamic.h" +#include "net.minecraft.world.level.dimension.h" + +LiquidTileDynamic::LiquidTileDynamic(int id, Material *material) : LiquidTile(id, material) +{ + maxCount = 0; + result = new bool[4]; + dist = new int[4]; + m_iterativeInstatick = false; +} + +LiquidTileDynamic::~LiquidTileDynamic() +{ + delete[] result; + delete[] dist; +} + +void LiquidTileDynamic::setStatic(Level *level, int x, int y, int z) +{ + int d = level->getData(x, y, z); + level->setTileAndDataNoUpdate(x, y, z, id + 1, d); + level->setTilesDirty(x, y, z, x, y, z); +} + +bool LiquidTileDynamic::isPathfindable(LevelSource *level, int x, int y, int z) +{ + return material != Material::lava; +} + +void LiquidTileDynamic::iterativeTick(Level *level, int x, int y, int z, Random *random) +{ + m_tilesToTick.push_back(LiquidTickData(level, x,y,z,random)); + + while(m_tilesToTick.size() > 0) + { + LiquidTickData tickData = m_tilesToTick.front(); + m_tilesToTick.pop_front(); + mainTick(tickData.level, tickData.x, tickData.y, tickData.z, tickData.random); + } +} + +void LiquidTileDynamic::tick(Level *level, int x, int y, int z, Random *random) +{ + if(!m_iterativeInstatick && level->getInstaTick() ) + { + m_iterativeInstatick = true; + iterativeTick(level, x, y, z, random); + m_iterativeInstatick = false; + } + else if(m_iterativeInstatick && level->getInstaTick()) + { + m_tilesToTick.push_back(LiquidTickData(level, x,y,z,random)); + } + else + { + mainTick(level, x, y, z, random); + } +} + +// 4J Stu - Split off what was the tick function to be able to change between recursive and iterative ticking +// This is to fix the stack overflow that occurs sometimes when instaticking on level gen. +void LiquidTileDynamic::mainTick(Level *level, int x, int y, int z, Random *random) +{ + int depth = getDepth(level, x, y, z); + + int dropOff = 1; + if (material == Material::lava && !level->dimension->ultraWarm) dropOff = 2; + + bool becomeStatic = true; + if (depth > 0) + { + int highest = -100; + maxCount = 0; + highest = getHighest(level, x - 1, y, z, highest); + highest = getHighest(level, x + 1, y, z, highest); + highest = getHighest(level, x, y, z - 1, highest); + highest = getHighest(level, x, y, z + 1, highest); + + int newDepth = highest + dropOff; + if (newDepth >= 8 || highest < 0) + { + newDepth = -1; + } + if (getDepth(level, x, y + 1, z) >= 0) + { + int above = getDepth(level, x, y + 1, z); + if (above >= 8) newDepth = above; + else newDepth = above + 8; + } + if (maxCount >= 2 && material == Material::water) + { + // Only spread spring if it's on top of an existing spring, or + // on top of solid ground. + if (level->getMaterial(x, y - 1, z)->isSolid()) + { + newDepth = 0; + } + else if (level->getMaterial(x, y - 1, z) == material && level->getData(x, y - 1, z) == 0) + { + newDepth = 0; + } + } + if (material == Material::lava) + { + if (depth < 8 && newDepth < 8) + { + if (newDepth > depth) + { + if (random->nextInt(4) != 0) + { + newDepth = depth; + becomeStatic = false; + } + } + } + } + if (newDepth == depth) + { + if (becomeStatic) + { + setStatic(level, x, y, z); + } + } + else + { + depth = newDepth; + if (depth < 0) + { + level->setTile(x, y, z, 0); + } else + { + level->setData(x, y, z, depth); + level->addToTickNextTick(x, y, z, id, getTickDelay()); + level->updateNeighborsAt(x, y, z, id); + } + } + } else + { + setStatic(level, x, y, z); + } + if (canSpreadTo(level, x, y - 1, z)) + { + if (material == Material::lava) + { + if (level->getMaterial(x, y - 1, z) == Material::water) + { + level->setTile(x, y - 1, z, Tile::rock_Id); + fizz(level, x, y - 1, z); + return; + } + } + + if (depth >= 8) trySpreadTo(level, x, y - 1, z, depth); + else trySpreadTo(level, x, y - 1, z, depth + 8); + } + else if (depth >= 0 && (depth == 0 || isWaterBlocking(level, x, y - 1, z))) + { + bool *spreads = getSpread(level, x, y, z); + int neighbor = depth + dropOff; + if (depth >= 8) + { + neighbor = 1; + } + if (neighbor >= 8) return; + if (spreads[0]) trySpreadTo(level, x - 1, y, z, neighbor); + if (spreads[1]) trySpreadTo(level, x + 1, y, z, neighbor); + if (spreads[2]) trySpreadTo(level, x, y, z - 1, neighbor); + if (spreads[3]) trySpreadTo(level, x, y, z + 1, neighbor); + } +} + +void LiquidTileDynamic::trySpreadTo(Level *level, int x, int y, int z, int neighbor) +{ + if (canSpreadTo(level, x, y, z)) + { + { + int old = level->getTile(x, y, z); + if (old > 0) + { + if (material == Material::lava) + { + fizz(level, x, y, z); + } + else + { + Tile::tiles[old]->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + } + } + } + level->setTileAndData(x, y, z, id, neighbor); + } +} + +int LiquidTileDynamic::getSlopeDistance(Level *level, int x, int y, int z, int pass, int from) +{ + int lowest = 1000; + for (int d = 0; d < 4; d++) + { + if (d == 0 && from == 1) continue; + if (d == 1 && from == 0) continue; + if (d == 2 && from == 3) continue; + if (d == 3 && from == 2) continue; + + int xx = x; + int yy = y; + int zz = z; + + if (d == 0) xx--; + if (d == 1) xx++; + if (d == 2) zz--; + if (d == 3) zz++; + + if (isWaterBlocking(level, xx, yy, zz)) + { + continue; + } else if (level->getMaterial(xx, yy, zz) == material && level->getData(xx, yy, zz) == 0) + { + continue; + } + else + { + if (isWaterBlocking(level, xx, yy - 1, zz)) + { + if (pass < 4) + { + int v = getSlopeDistance(level, xx, yy, zz, pass + 1, d); + if (v < lowest) lowest = v; + } + } + else + { + return pass; + } + } + } + return lowest; + +} + +bool *LiquidTileDynamic::getSpread(Level *level, int x, int y, int z) +{ + for (int d = 0; d < 4; d++) + { + dist[d] = 1000; + int xx = x; + int yy = y; + int zz = z; + + if (d == 0) xx--; + if (d == 1) xx++; + if (d == 2) zz--; + if (d == 3) zz++; + if (isWaterBlocking(level, xx, yy, zz)) + { + continue; + } + else if (level->getMaterial(xx, yy, zz) == material && level->getData(xx, yy, zz) == 0) + { + continue; + } + + { + if (isWaterBlocking(level, xx, yy - 1, zz)) + { + dist[d] = getSlopeDistance(level, xx, yy, zz, 1, d); + } + else + { + dist[d] = 0; + } + } + } + + int lowest = dist[0]; + for (int d = 1; d < 4; d++) + { + if (dist[d] < lowest) lowest = dist[d]; + } + + + for (int d = 0; d < 4; d++) + { + result[d] = (dist[d] == lowest); + } + return result; + +} + +bool LiquidTileDynamic::isWaterBlocking(Level *level, int x, int y, int z) +{ + int t = level->getTile(x, y, z); + if (t == Tile::door_wood_Id || t == Tile::door_iron_Id || t == Tile::sign_Id || t == Tile::ladder_Id || t == Tile::reeds_Id) + { + return true; + } + if (t == 0) return false; + Material *m = Tile::tiles[t]->material; + if (m == Material::portal) return true; + if (m->blocksMotion()) return true; + return false; +} + +int LiquidTileDynamic::getHighest(Level *level, int x, int y, int z, int current) +{ + int d = getDepth(level, x, y, z); + if (d < 0) return current; + if (d == 0) maxCount++; + if (d >= 8) + { + d = 0; + } + return current < 0 || d < current ? d : current; +} + +bool LiquidTileDynamic::canSpreadTo(Level *level, int x, int y, int z) +{ + // 4J added - don't try and spread out of our restricted map. If we don't do this check then tiles at the edge of the world will try and spread outside as the outside tiles report that they contain + // only air. The fact that this successfully spreads then updates the neighbours of the tile outside of the map, one of which is the original tile just inside the map, which gets set back to being + // dynamic, and added to the pending ticks array. + int xc = x >> 4; + int zc = z >> 4; + int ix = xc + (level->chunkSourceXZSize/2); + int iz = zc + (level->chunkSourceXZSize/2); + if( ( ix < 0 ) || ( ix >= level->chunkSourceXZSize ) ) return false; + if( ( iz < 0 ) || ( iz >= level->chunkSourceXZSize ) ) return false; + + Material *target = level->getMaterial(x, y, z); + if (target == material) return false; + if (target == Material::lava) return false; + return !isWaterBlocking(level, x, y, z); +} + +void LiquidTileDynamic::onPlace(Level *level, int x, int y, int z) +{ + LiquidTile::onPlace(level, x, y, z); + if (level->getTile(x, y, z) == id) + { + level->addToTickNextTick(x, y, z, id, getTickDelay()); + } +} diff --git a/Minecraft.World/LiquidTileDynamic.h b/Minecraft.World/LiquidTileDynamic.h new file mode 100644 index 00000000..51bbf181 --- /dev/null +++ b/Minecraft.World/LiquidTileDynamic.h @@ -0,0 +1,54 @@ +#pragma once +#include "LiquidTile.h" + +class Random; + +class LiquidTileDynamic : public LiquidTile +{ + friend class Tile; +private: + // 4J Added + typedef struct _LiquidTickData + { + Level *level; + int x, y, z; + Random *random; + _LiquidTickData(Level *level, int x, int y, int z, Random *random) + : level(level), x(x), y(y), z(z), random(random) + { + } + } LiquidTickData; + deque m_tilesToTick; // For an iterative version of instatick + bool m_iterativeInstatick; +protected: + LiquidTileDynamic(int id, Material *material); + virtual ~LiquidTileDynamic(); +private: + void setStatic(Level *level, int x, int y, int z); + int maxCount; +public: + virtual bool isPathfindable(LevelSource *level, int x, int y, int z); + +private: + // 4J Added + void iterativeTick(Level *level, int x, int y, int z, Random *random); + void mainTick(Level *level, int x, int y, int z, Random *random); +public: + void tick(Level *level, int x, int y, int z, Random *random); +private: + void trySpreadTo(Level *level, int x, int y, int z, int neighbor); + + bool *result; + int *dist; + +private: + int getSlopeDistance(Level *level, int x, int y, int z, int pass, int from); + bool *getSpread(Level *level, int x, int y, int z); + bool isWaterBlocking(Level *level, int x, int y, int z); +protected: + int getHighest(Level *level, int x, int y, int z, int current); +private: + bool canSpreadTo(Level *level, int x, int y, int z); +public: + void onPlace(Level *level, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/LiquidTileStatic.cpp b/Minecraft.World/LiquidTileStatic.cpp new file mode 100644 index 00000000..1d82d3dd --- /dev/null +++ b/Minecraft.World/LiquidTileStatic.cpp @@ -0,0 +1,85 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "LiquidTileStatic.h" +#include "FireTile.h" + +LiquidTileStatic::LiquidTileStatic(int id, Material *material) : LiquidTile(id, material) +{ + setTicking(false); + if (material == Material::lava) setTicking(true); +} + +bool LiquidTileStatic::isPathfindable(LevelSource *level, int x, int y, int z) +{ + return material != Material::lava; +} + +void LiquidTileStatic::neighborChanged(Level *level, int x, int y, int z, int type) +{ + LiquidTile::neighborChanged(level, x, y, z, type); + if (level->getTile(x, y, z) == id) + { + setDynamic(level, x, y, z); + } +} + +void LiquidTileStatic::setDynamic(Level *level, int x, int y, int z) +{ + int d = level->getData(x, y, z); + level->noNeighborUpdate = true; + level->setTileAndDataNoUpdate(x, y, z, id - 1, d); + level->setTilesDirty(x, y, z, x, y, z); + level->addToTickNextTick(x, y, z, id - 1, getTickDelay()); + level->noNeighborUpdate = false; +} + +void LiquidTileStatic::tick(Level *level, int x, int y, int z, Random *random) +{ + if (material == Material::lava) + { + int h = random->nextInt(3); + for (int i = 0; i < h; i++) + { + x += random->nextInt(3) - 1; + y++; + z += random->nextInt(3) - 1; + int t = level->getTile(x, y, z); + if (t == 0) + { + if (isFlammable(level, x - 1, y, z) || + isFlammable(level, x + 1, y, z) || + isFlammable(level, x, y, z - 1) || + isFlammable(level, x, y, z + 1) || + isFlammable(level, x, y - 1, z) || + isFlammable(level, x, y + 1, z)) + { + level->setTile(x, y, z, Tile::fire_Id); + return; + } + } + else if (Tile::tiles[t]->material->blocksMotion()) + { + return; + } + + } + if (h == 0) + { + int ox = x; + int oz = z; + for (int i = 0; i< 3; i++) + { + x = ox + random->nextInt(3) - 1; + z = oz + random->nextInt(3) - 1; + if (level->isEmptyTile(x, y + 1, z) && isFlammable(level, x, y, z)) { + level->setTile(x, y + 1, z, Tile::fire_Id); + } + } + } + } +} + +bool LiquidTileStatic::isFlammable(Level *level, int x, int y, int z) +{ + return level->getMaterial(x, y, z)->isFlammable(); +} diff --git a/Minecraft.World/LiquidTileStatic.h b/Minecraft.World/LiquidTileStatic.h new file mode 100644 index 00000000..031eef62 --- /dev/null +++ b/Minecraft.World/LiquidTileStatic.h @@ -0,0 +1,20 @@ +#pragma once +#include "LiquidTile.h" + +class Random; + +class LiquidTileStatic : public LiquidTile +{ + friend class Tile; +protected: + LiquidTileStatic(int id, Material *material); +public: + virtual bool isPathfindable(LevelSource *level, int x, int y, int z); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); +private: + virtual void setDynamic(Level *level, int x, int y, int z); +public: + virtual void tick(Level *level, int x, int y, int z, Random *random); +private: + bool isFlammable(Level *level, int x, int y, int z); +}; diff --git a/Minecraft.World/ListTag.h b/Minecraft.World/ListTag.h new file mode 100644 index 00000000..9deeb22d --- /dev/null +++ b/Minecraft.World/ListTag.h @@ -0,0 +1,144 @@ +#pragma once +using namespace std; +#include "Tag.h" + +template class ListTag : public Tag +{ +private: + vector list; + byte type; + +public: + ListTag() : Tag(L"") {} + ListTag(const wstring &name) : Tag(name) {} + + void write(DataOutput *dos) + { + if (list.size() > 0) type = (list[0])->getId(); + else type = 1; + + dos->writeByte(type); + dos->writeInt((int)list.size()); + + AUTO_VAR(itEnd, list.end()); + for (AUTO_VAR(it, list.begin()); it != itEnd; it++) + (*it)->write(dos); + } + void load(DataInput *dis) + { + type = dis->readByte(); + int size = dis->readInt(); + + list.clear(); + for (int i = 0; i < size; i++) + { + Tag *tag = Tag::newTag(type, L""); + tag->load(dis); + list.push_back(tag); + } + } + + byte getId() { return TAG_List; } + + wstring toString() + { + static wchar_t buf[64]; + swprintf(buf,64,L"%d entries of type %ls",list.size(),Tag::getTagName(type)); + return wstring( buf ); + } + + void print(char *prefix, ostream out) + { + Tag::print(prefix, out); + + out << prefix << "{" << endl; + + char *newPrefix = new char[ strlen(prefix) + 4 ]; + strcpy( newPrefix, prefix); + strcat( newPrefix, " "); + AUTO_VAR(itEnd, list.end()); + for (AUTO_VAR(it, list.begin()); it != itEnd; it++) + (*it)->print(newPrefix, out); + delete[] newPrefix; + out << prefix << "}" << endl; + } + + void add(T *tag) + { + type = tag->getId(); + list.push_back(tag); + } + + T *get(int index) + { + return (T *) list[index]; + } + + int size() + { + return (int)list.size(); + } + + virtual ~ListTag() + { + AUTO_VAR(itEnd, list.end()); + for (AUTO_VAR(it, list.begin()); it != itEnd; it++) + { + delete *it; + } + } + + Tag *copy() + { + ListTag *res = new ListTag(getName()); + res->type = type; + AUTO_VAR(itEnd, list.end()); + for (AUTO_VAR(it, list.begin()); it != itEnd; it++) + { + T *copy = (T *) (*it)->copy(); + res->list.push_back(copy); + } + return res; + } + +#if 0 + bool equals(Object obj) + { + if (Tag::equals(obj)) + { + ListTag *o = (ListTag *) obj; + if (type == o->type) + { + bool equal = false; + if(list.size() == o->list.size()) + { + equal = true; + AUTO_VAR(itEnd, list.end()); + // 4J Stu - Pretty inefficient method, but I think we can live with it give how often it will happen, and the small sizes of the data sets + for (AUTO_VAR(it, list.begin()); it != itEnd; it++) + { + bool thisMatches = false; + for(AUTO_VAR(it2, o->list.begin()); it != o->list.end(); ++it2) + { + if((*it)->equals(*it2)) + { + thisMatches = true; + break; + } + } + if(!thisMatches) + { + equal = false; + break; + } + } + } + + //return list->equals(o->list); + return equal; + } + } + return false; + } +#endif +}; \ No newline at end of file diff --git a/Minecraft.World/LockedChestTile.cpp b/Minecraft.World/LockedChestTile.cpp new file mode 100644 index 00000000..774fb929 --- /dev/null +++ b/Minecraft.World/LockedChestTile.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "LockedChestTile.h" + +LockedChestTile::LockedChestTile(int id) : Tile(id, Material::wood) +{ +} + +bool LockedChestTile::mayPlace(Level *level, int x, int y, int z) +{ + return true; +} + +void LockedChestTile::tick(Level *level, int x, int y, int z, Random *random) +{ + level->setTile(x,y,z,0); +} + +void LockedChestTile::registerIcons(IconRegister *iconRegister) +{ + // None +} \ No newline at end of file diff --git a/Minecraft.World/LockedChestTile.h b/Minecraft.World/LockedChestTile.h new file mode 100644 index 00000000..c093f85c --- /dev/null +++ b/Minecraft.World/LockedChestTile.h @@ -0,0 +1,14 @@ +#pragma once +#include "Tile.h" + +class Random; + +class LockedChestTile : public Tile +{ + friend class Tile; +protected: + LockedChestTile(int id); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual void tick(Level *level, int x, int y, int z, Random *random); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/LoginPacket.cpp b/Minecraft.World/LoginPacket.cpp new file mode 100644 index 00000000..bfe69226 --- /dev/null +++ b/Minecraft.World/LoginPacket.cpp @@ -0,0 +1,183 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.player.h" +#include "PacketListener.h" +#include "LoginPacket.h" +#include "LevelType.h" + + + +LoginPacket::LoginPacket() +{ + this->userName = L""; + this->clientVersion = 0; + this->seed = 0; + this->dimension = 0; + this->gameType = 0; + this->mapHeight = 0; + this->maxPlayers = 0; + + this->difficulty = 1; + + this->m_offlineXuid = INVALID_XUID; + this->m_onlineXuid = INVALID_XUID; + m_friendsOnlyUGC = false; + m_ugcPlayersVersion = 0; + m_multiplayerInstanceId = 0; + m_playerIndex = 0; + m_playerSkinId = 0; + m_playerCapeId = 0; + m_isGuest = false; + m_newSeaLevel = false; + m_pLevelType = NULL; + m_uiGamePrivileges = 0; + m_xzSize = LEVEL_MAX_WIDTH; + m_hellScale = HELL_LEVEL_MAX_SCALE; +} + +// Client -> Server +LoginPacket::LoginPacket(const wstring& userName, int clientVersion, PlayerUID offlineXuid, PlayerUID onlineXuid, bool friendsOnlyUGC, DWORD ugcPlayersVersion, DWORD skinId, DWORD capeId, bool isGuest) +{ + this->userName = userName; + this->clientVersion = clientVersion; + this->seed = 0; + this->dimension = 0; + this->gameType = 0; + this->mapHeight = 0; + this->maxPlayers = 0; + + this->difficulty = 1; + + this->m_offlineXuid = offlineXuid; + this->m_onlineXuid = onlineXuid; + m_friendsOnlyUGC = friendsOnlyUGC; + m_ugcPlayersVersion = ugcPlayersVersion; + m_multiplayerInstanceId = 0; + m_playerIndex = 0; + m_playerSkinId = skinId; + m_playerCapeId = capeId; + m_isGuest = isGuest; + m_newSeaLevel = false; + m_pLevelType = NULL; + m_uiGamePrivileges = 0; + m_xzSize = LEVEL_MAX_WIDTH; + m_hellScale = HELL_LEVEL_MAX_SCALE; +} + +// Server -> Client +LoginPacket::LoginPacket(const wstring& userName, int clientVersion, LevelType *pLevelType, __int64 seed, int gameType, char dimension, BYTE mapHeight, BYTE maxPlayers, char difficulty, INT multiplayerInstanceId, BYTE playerIndex, bool newSeaLevel, unsigned int uiGamePrivileges, int xzSize, int hellScale) +{ + this->userName = userName; + this->clientVersion = clientVersion; + this->seed = seed; + this->dimension = dimension; + this->gameType = gameType; + this->mapHeight = mapHeight; + this->maxPlayers = maxPlayers; + + this->difficulty = difficulty; + + this->m_offlineXuid = INVALID_XUID; + this->m_onlineXuid = INVALID_XUID; + m_friendsOnlyUGC = false; + m_ugcPlayersVersion = 0; + m_multiplayerInstanceId = multiplayerInstanceId; + this->m_playerIndex = playerIndex; + m_playerSkinId = 0; + m_playerCapeId = 0; + m_isGuest = false; + m_newSeaLevel = newSeaLevel; + this->m_pLevelType=pLevelType; + m_uiGamePrivileges = uiGamePrivileges; + m_xzSize = xzSize; + m_hellScale = hellScale; +} + +void LoginPacket::read(DataInputStream *dis) //throws IOException +{ + clientVersion = dis->readInt(); + userName = readUtf(dis, Player::MAX_NAME_LENGTH); + wstring typeName = readUtf(dis, 16); + m_pLevelType = LevelType::getLevelType(typeName); + if (m_pLevelType == NULL) + { + m_pLevelType = LevelType::lvl_normal; + } + seed = dis->readLong(); + gameType = dis->readInt(); + dimension = dis->readByte(); + mapHeight = dis->readByte(); + maxPlayers = dis->readByte(); + m_offlineXuid = dis->readPlayerUID(); + m_onlineXuid = dis->readPlayerUID(); + m_friendsOnlyUGC = dis->readBoolean(); + m_ugcPlayersVersion = dis->readInt(); + difficulty = dis->readByte(); + m_multiplayerInstanceId = dis->readInt(); + m_playerIndex = dis->readByte(); + INT skinId = dis->readInt(); + m_playerSkinId = *(DWORD *)&skinId; + INT capeId = dis->readInt(); + m_playerCapeId = *(DWORD *)&capeId; + m_isGuest = dis->readBoolean(); + m_newSeaLevel = dis->readBoolean(); + m_uiGamePrivileges = dis->readInt(); +#ifdef _LARGE_WORLDS + m_xzSize = dis->readShort(); + m_hellScale = dis->read(); +#endif + app.DebugPrintf("LoginPacket::read - Difficulty = %d\n",difficulty); + +} + +void LoginPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(clientVersion); + writeUtf(userName, dos); + if (m_pLevelType == NULL) + { + writeUtf(L"", dos); + } + else + { + writeUtf(m_pLevelType->getGeneratorName(), dos); + } + dos->writeLong(seed); + dos->writeInt(gameType); + dos->writeByte(dimension); + dos->writeByte(mapHeight); + dos->writeByte(maxPlayers); + dos->writePlayerUID(m_offlineXuid); + dos->writePlayerUID(m_onlineXuid); + dos->writeBoolean(m_friendsOnlyUGC); + dos->writeInt(m_ugcPlayersVersion); + dos->writeByte(difficulty); + dos->writeInt(m_multiplayerInstanceId); + dos->writeByte(m_playerIndex); + dos->writeInt(m_playerSkinId); + dos->writeInt(m_playerCapeId); + dos->writeBoolean(m_isGuest); + dos->writeBoolean(m_newSeaLevel); + dos->writeInt(m_uiGamePrivileges); +#ifdef _LARGE_WORLDS + dos->writeShort(m_xzSize); + dos->write(m_hellScale); +#endif +} + +void LoginPacket::handle(PacketListener *listener) +{ + listener->handleLogin(shared_from_this()); +} + +int LoginPacket::getEstimatedSize() +{ + int length=0; + if (m_pLevelType != NULL) + { + length = (int)m_pLevelType->getGeneratorName().length(); + } + + return (int)(sizeof(int) + userName.length() + 4 + 6 + sizeof(__int64) + sizeof(char) + sizeof(int) + (2*sizeof(PlayerUID)) +1 + sizeof(char) + sizeof(BYTE) + sizeof(bool) + sizeof(bool) + length + sizeof(unsigned int)); +} diff --git a/Minecraft.World/LoginPacket.h b/Minecraft.World/LoginPacket.h new file mode 100644 index 00000000..09041135 --- /dev/null +++ b/Minecraft.World/LoginPacket.h @@ -0,0 +1,45 @@ +#pragma once +using namespace std; + +#include "Packet.h" +class LevelType; + +class LoginPacket : public Packet, public enable_shared_from_this +{ +public: + int clientVersion; + wstring userName; + __int64 seed; + char dimension; + PlayerUID m_offlineXuid, m_onlineXuid; // 4J Added + char difficulty; // 4J Added + bool m_friendsOnlyUGC; // 4J Added + DWORD m_ugcPlayersVersion; // 4J Added + INT m_multiplayerInstanceId; //4J Added for sentient + BYTE m_playerIndex; // 4J Added + DWORD m_playerSkinId, m_playerCapeId; // 4J Added + bool m_isGuest; // 4J Added + bool m_newSeaLevel; // 4J Added + LevelType *m_pLevelType; + unsigned int m_uiGamePrivileges; + int m_xzSize; // 4J Added + int m_hellScale; // 4J Added + + // 1.8.2 + int gameType; + BYTE mapHeight; + BYTE maxPlayers; + + LoginPacket(); + LoginPacket(const wstring& userName, int clientVersion, LevelType *pLevelType, __int64 seed, int gameType, char dimension, BYTE mapHeight, BYTE maxPlayers, char difficulty, INT m_multiplayerInstanceId, BYTE playerIndex, bool newSeaLevel, unsigned int uiGamePrivileges, int xzSize, int hellScale); // Server -> Client + LoginPacket(const wstring& userName, int clientVersion, PlayerUID offlineXuid, PlayerUID onlineXuid, bool friendsOnlyUGC, DWORD ugcPlayersVersion, DWORD skinId, DWORD capeId, bool isGuest); // Client -> Server + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new LoginPacket()); } + virtual int getId() { return 1; } +}; diff --git a/Minecraft.World/LongTag.h b/Minecraft.World/LongTag.h new file mode 100644 index 00000000..2dcd3819 --- /dev/null +++ b/Minecraft.World/LongTag.h @@ -0,0 +1,36 @@ +#pragma once +#include "Tag.h" + +class LongTag : public Tag +{ +public: + __int64 data; + LongTag(const wstring &name) : Tag(name) {} + LongTag(const wstring &name, __int64 data) : Tag(name) {this->data = data; } + + void write(DataOutput *dos) { dos->writeLong(data); } + void load(DataInput *dis) { data = dis->readLong(); } + + byte getId() { return TAG_Long; } + wstring toString() + { + static wchar_t buf[32]; + swprintf(buf,32,L"%I64d",data); + return wstring(buf); + } + + Tag *copy() + { + return new LongTag(getName(), data); + } + + bool equals(Tag *obj) + { + if (Tag::equals(obj)) + { + LongTag *o = (LongTag *) obj; + return data == o->data; + } + return false; + } +}; \ No newline at end of file diff --git a/Minecraft.World/LookAtPlayerGoal.cpp b/Minecraft.World/LookAtPlayerGoal.cpp new file mode 100644 index 00000000..f2dc10c3 --- /dev/null +++ b/Minecraft.World/LookAtPlayerGoal.cpp @@ -0,0 +1,57 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "LookAtPlayerGoal.h" + +LookAtPlayerGoal::LookAtPlayerGoal(Mob *mob, const type_info& lookAtType, float lookDistance) : lookAtType(lookAtType) +{ + this->mob = mob; + this->lookDistance = lookDistance; + this->probability = 0.02f; + setRequiredControlFlags(Control::LookControlFlag); + + lookTime = 0; +} + +LookAtPlayerGoal::LookAtPlayerGoal(Mob *mob, const type_info& lookAtType, float lookDistance, float probability) : lookAtType(lookAtType) +{ + this->mob = mob; + this->lookDistance = lookDistance; + this->probability = probability; + setRequiredControlFlags(Control::LookControlFlag); + + lookTime = 0; +} + +bool LookAtPlayerGoal::canUse() +{ + if (mob->getRandom()->nextFloat() >= probability) return false; + if (lookAtType == typeid(Player)) lookAt = mob->level->getNearestPlayer(mob->shared_from_this(), lookDistance); + else lookAt = weak_ptr(mob->level->getClosestEntityOfClass(lookAtType, mob->bb->grow(lookDistance, 3, lookDistance), mob->shared_from_this())); + return lookAt.lock() != NULL; +} + +bool LookAtPlayerGoal::canContinueToUse() +{ + if (lookAt.lock() == NULL || !lookAt.lock()->isAlive()) return false; + if (mob->distanceToSqr(lookAt.lock()) > lookDistance * lookDistance) return false; + return lookTime > 0; +} + +void LookAtPlayerGoal::start() +{ + lookTime = 40 + mob->getRandom()->nextInt(40); +} + +void LookAtPlayerGoal::stop() +{ + lookAt = weak_ptr(); +} + +void LookAtPlayerGoal::tick() +{ + mob->getLookControl()->setLookAt(lookAt.lock()->x, lookAt.lock()->y + lookAt.lock()->getHeadHeight(), lookAt.lock()->z, 10, mob->getMaxHeadXRot()); + --lookTime; +} \ No newline at end of file diff --git a/Minecraft.World/LookAtPlayerGoal.h b/Minecraft.World/LookAtPlayerGoal.h new file mode 100644 index 00000000..bfc524c8 --- /dev/null +++ b/Minecraft.World/LookAtPlayerGoal.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Goal.h" + +class Mob; +class Level; + +class LookAtPlayerGoal : public Goal +{ +private: + Mob *mob; // Owner of this goal + +protected: + weak_ptr lookAt; + +private: + float lookDistance; + int lookTime; + float probability; + const type_info& lookAtType; + +public: + LookAtPlayerGoal(Mob *mob, const type_info& lookAtType, float lookDistance); + LookAtPlayerGoal(Mob *mob, const type_info& lookAtType, float lookDistance, float probability); + virtual ~LookAtPlayerGoal() {} + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + virtual void stop(); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/LookAtTradingPlayerGoal.cpp b/Minecraft.World/LookAtTradingPlayerGoal.cpp new file mode 100644 index 00000000..db1e25a2 --- /dev/null +++ b/Minecraft.World/LookAtTradingPlayerGoal.cpp @@ -0,0 +1,19 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.npc.h" +#include "LookAtTradingPlayerGoal.h" + +LookAtTradingPlayerGoal::LookAtTradingPlayerGoal(Villager *villager) : LookAtPlayerGoal((Mob *)villager, typeid(Player), 8) +{ + this->villager = villager; +} + +bool LookAtTradingPlayerGoal::canUse() +{ + if (villager->isTrading()) + { + lookAt = weak_ptr(dynamic_pointer_cast(villager->getTradingPlayer())); + return true; + } + return false; +} \ No newline at end of file diff --git a/Minecraft.World/LookAtTradingPlayerGoal.h b/Minecraft.World/LookAtTradingPlayerGoal.h new file mode 100644 index 00000000..bebc96fd --- /dev/null +++ b/Minecraft.World/LookAtTradingPlayerGoal.h @@ -0,0 +1,16 @@ +#pragma once + +#include "LookAtPlayerGoal.h" + +class Villager; + +class LookAtTradingPlayerGoal : public LookAtPlayerGoal +{ +private: + Villager *villager; // This is the owner of this goal + +public: + LookAtTradingPlayerGoal(Villager *villager); + + virtual bool canUse(); +}; \ No newline at end of file diff --git a/Minecraft.World/LookControl.cpp b/Minecraft.World/LookControl.cpp new file mode 100644 index 00000000..829c3e4a --- /dev/null +++ b/Minecraft.World/LookControl.cpp @@ -0,0 +1,117 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.phys.h" +#include "LookControl.h" + +LookControl::LookControl(Mob *mob) +{ + yMax = xMax = 0.0f; + hasWanted = false; + wantedX = wantedY = wantedZ = 0.0; + + this->mob = mob; +} + +void LookControl::setLookAt(shared_ptr target, float yMax, float xMax) +{ + this->wantedX = target->x; + shared_ptr targetMob = dynamic_pointer_cast(target); + if (targetMob != NULL) this->wantedY = target->y + targetMob->getHeadHeight(); + else this->wantedY = (target->bb->y0 + target->bb->y1) / 2; + this->wantedZ = target->z; + this->yMax = yMax; + this->xMax = xMax; + hasWanted = true; +} + +void LookControl::setLookAt(double x, double y, double z, float yMax, float xMax) +{ + this->wantedX = x; + this->wantedY = y; + this->wantedZ = z; + this->yMax = yMax; + this->xMax = xMax; + hasWanted = true; +} + +void LookControl::tick() +{ + mob->xRot = 0; + + if (hasWanted) + { + hasWanted = false; + + double xd = wantedX - mob->x; + double yd = wantedY - (mob->y + mob->getHeadHeight()); + double zd = wantedZ - mob->z; + double sd = sqrt(xd * xd + zd * zd); + + float yRotD = (float) (atan2(zd, xd) * 180 / PI) - 90; + float xRotD = (float) -(atan2(yd, sd) * 180 / PI); + mob->xRot = rotlerp(mob->xRot, xRotD, xMax); + mob->yHeadRot = rotlerp(mob->yHeadRot, yRotD, yMax); + } + else + { + mob->yHeadRot = rotlerp(mob->yHeadRot, mob->yBodyRot, 10); + } + + float headDiffBody = Mth::wrapDegrees(mob->yHeadRot - mob->yBodyRot); + + if (!mob->getNavigation()->isDone()) + { + // head clamped to body + if (headDiffBody < -75) mob->yHeadRot = mob->yBodyRot - 75; + if (headDiffBody > 75) mob->yHeadRot = mob->yBodyRot + 75; + } +} + +float LookControl::rotlerp(float a, float b, float max) +{ + float diff = b - a; + while (diff < -180) + diff += 360; + while (diff >= 180) + diff -= 360; + if (diff > max) + { + diff = max; + } + if (diff < -max) + { + diff = -max; + } + return a + diff; +} + +bool LookControl::isHasWanted() +{ + return hasWanted; +} + +float LookControl::getYMax() +{ + return yMax; +} + +float LookControl::getXMax() +{ + return xMax; +} + +double LookControl::getWantedX() +{ + return wantedX; +} + +double LookControl::getWantedY() +{ + return wantedY; +} + +double LookControl::getWantedZ() +{ + return wantedZ; +} \ No newline at end of file diff --git a/Minecraft.World/LookControl.h b/Minecraft.World/LookControl.h new file mode 100644 index 00000000..add64e4a --- /dev/null +++ b/Minecraft.World/LookControl.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Control.h" + +class Mob; + +class LookControl : public Control +{ +private: + Mob *mob; + float yMax, xMax; + bool hasWanted; + + double wantedX, wantedY, wantedZ; + +public: + LookControl(Mob *mob); + + void setLookAt(shared_ptr target, float yMax, float xMax); + void setLookAt(double x, double y, double z, float yMax, float xMax); + virtual void tick(); + +private: + float rotlerp(float a, float b, float max); + +public: + bool isHasWanted(); + float getYMax(); + float getXMax(); + double getWantedX(); + double getWantedY(); + double getWantedZ(); +}; \ No newline at end of file diff --git a/Minecraft.World/LootBonusEnchantment.cpp b/Minecraft.World/LootBonusEnchantment.cpp new file mode 100644 index 00000000..9150fe44 --- /dev/null +++ b/Minecraft.World/LootBonusEnchantment.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "LootBonusEnchantment.h" + +LootBonusEnchantment::LootBonusEnchantment(int id, int frequency, const EnchantmentCategory *cat) : Enchantment(id, frequency, cat) +{ + setDescriptionId(IDS_ENCHANTMENT_LOOT_BONUS); + if (cat == EnchantmentCategory::digger) + { + setDescriptionId(IDS_ENCHANTMENT_LOOT_BONUS_DIGGER); + } +} + +int LootBonusEnchantment::getMinCost(int level) +{ + return 15 + (level - 1) * 9; +} + +int LootBonusEnchantment::getMaxCost(int level) +{ + return Enchantment::getMinCost(level) + 50; +} + +int LootBonusEnchantment::getMaxLevel() +{ + return 3; +} + +bool LootBonusEnchantment::isCompatibleWith(Enchantment *other) const +{ + return Enchantment::isCompatibleWith(other) && other->id != untouching->id; +} \ No newline at end of file diff --git a/Minecraft.World/LootBonusEnchantment.h b/Minecraft.World/LootBonusEnchantment.h new file mode 100644 index 00000000..134d2dce --- /dev/null +++ b/Minecraft.World/LootBonusEnchantment.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Enchantment.h" + +class LootBonusEnchantment : public Enchantment +{ +public: + LootBonusEnchantment(int id, int frequency, const EnchantmentCategory *cat); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); + virtual bool isCompatibleWith(Enchantment *other) const; +}; \ No newline at end of file diff --git a/Minecraft.World/MakeLoveGoal.cpp b/Minecraft.World/MakeLoveGoal.cpp new file mode 100644 index 00000000..89f09f1d --- /dev/null +++ b/Minecraft.World/MakeLoveGoal.cpp @@ -0,0 +1,103 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.village.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.npc.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "MakeLoveGoal.h" + +MakeLoveGoal::MakeLoveGoal(Villager *villager) +{ + village = weak_ptr(); + partner = weak_ptr(); + loveMakingTime = 0; + + this->villager = villager; + level = villager->level; + setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag); +} + +bool MakeLoveGoal::canUse() +{ + + if (villager->getAge() != 0) return false; + if (villager->getRandom()->nextInt(500) != 0) return false; + + village = level->villages->getClosestVillage(Mth::floor(villager->x), Mth::floor(villager->y), Mth::floor(villager->z), 0); + if (village.lock() == NULL) return false; + if (!villageNeedsMoreVillagers()) return false; + + shared_ptr mate = level->getClosestEntityOfClass(typeid(Villager), villager->bb->grow(8, 3, 8), villager->shared_from_this()); + if (mate == NULL) return false; + + partner = weak_ptr(dynamic_pointer_cast(mate)); + if (partner.lock()->getAge() != 0) return false; + + return true; +} + +void MakeLoveGoal::start() +{ + loveMakingTime = 300; + villager->setInLove(true); +} + +void MakeLoveGoal::stop() +{ + village = weak_ptr(); + partner = weak_ptr(); + villager->setInLove(false); +} + +bool MakeLoveGoal::canContinueToUse() +{ + return partner.lock() != NULL && loveMakingTime >= 0 && villageNeedsMoreVillagers() && villager->getAge() == 0; +} + +void MakeLoveGoal::tick() +{ + --loveMakingTime; + villager->getLookControl()->setLookAt(partner.lock(), 10, 30); + + if (villager->distanceToSqr(partner.lock()) > 1.5 * 1.5) + { + villager->getNavigation()->moveTo(partner.lock(), 0.25f); + } + else + { + if (loveMakingTime == 0 && partner.lock()->isInLove()) breed(); + } + + if (villager->getRandom()->nextInt(35) == 0) level->broadcastEntityEvent(villager->shared_from_this(), EntityEvent::LOVE_HEARTS); +} + +bool MakeLoveGoal::villageNeedsMoreVillagers() +{ + shared_ptr _village = village.lock(); + if( _village == NULL ) return false; + + int idealSize = (int) ((float) _village->getDoorCount() * 0.35); + // System.out.println("idealSize: " + idealSize + " pop: " + + // village.getPopulationSize()); + return _village->getPopulationSize() < idealSize; +} + +void MakeLoveGoal::breed() +{ + // 4J Stu - This sets a timer that stops these villagers from trying to breed again + // We should do this even if breeding fails due to vilalger count to stop them continually trying to breed + partner.lock()->setAge(5 * 60 * 20); + villager->setAge(5 * 60 * 20); + // 4J - added limit to number of animals that can be bred + if(level->canCreateMore( eTYPE_VILLAGER, Level::eSpawnType_Breed) ) + { + shared_ptr child = shared_ptr( new Villager(level) ); + child->setAge(-20 * 60 * 20); + child->setProfession(villager->getRandom()->nextInt(Villager::PROFESSION_MAX)); + child->moveTo(villager->x, villager->y, villager->z, 0, 0); + level->addEntity(child); + level->broadcastEntityEvent(child, EntityEvent::LOVE_HEARTS); + } +} diff --git a/Minecraft.World/MakeLoveGoal.h b/Minecraft.World/MakeLoveGoal.h new file mode 100644 index 00000000..1e148daa --- /dev/null +++ b/Minecraft.World/MakeLoveGoal.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Goal.h" + +class Villager; +class Village; + +class MakeLoveGoal : public Goal +{ +private: + Villager *villager; // Owner of this goal + weak_ptr partner; + Level *level; + int loveMakingTime; + weak_ptr village; + +public: + MakeLoveGoal(Villager *villager); + + bool canUse(); + void start(); + void stop(); + bool canContinueToUse(); + void tick(); + +private: + bool villageNeedsMoreVillagers(); + void breed(); + +public: + // 4J Added override to update ai elements when loading entity from schematics + virtual void setLevel(Level *level) { this->level = level; } +}; \ No newline at end of file diff --git a/Minecraft.World/MapItem.cpp b/Minecraft.World/MapItem.cpp new file mode 100644 index 00000000..abf4fb9a --- /dev/null +++ b/Minecraft.World/MapItem.cpp @@ -0,0 +1,347 @@ +#include "stdafx.h" +#include "net.minecraft.network.packet.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.chunk.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.level.saveddata.h" +#include "net.minecraft.world.level.storage.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "MapItem.h" +#include "net.minecraft.world.inventory.h" +#include "JavaMath.h" + +MapItem::MapItem(int id) : ComplexItem(id) +{ + this->setMaxStackSize(1); +} + +shared_ptr MapItem::getSavedData(short idNum, Level *level) +{ + std::wstring id = wstring( L"map_" ) + _toString(idNum); + shared_ptr mapItemSavedData = dynamic_pointer_cast(level->getSavedData(typeid(MapItemSavedData), id)); + + if (mapItemSavedData == NULL) + { + // 4J Stu - This call comes from ClientConnection, but i don't see why we should be trying to work out + // the id again when it's passed as a param. In any case that won't work with the new map setup + //int aux = level->getFreeAuxValueFor(L"map"); + int aux = idNum; + + id = wstring( L"map_" ) + _toString(aux); + mapItemSavedData = shared_ptr( new MapItemSavedData(id) ); + + level->setSavedData(id, (shared_ptr ) mapItemSavedData); + } + + return mapItemSavedData; +} + +shared_ptr MapItem::getSavedData(shared_ptr itemInstance, Level *level) +{ + MemSect(31); + std::wstring id = wstring( L"map_" ) + _toString(itemInstance->getAuxValue() ); + MemSect(0); + shared_ptr mapItemSavedData = dynamic_pointer_cast( level->getSavedData(typeid(MapItemSavedData), id ) ); + + bool newData = false; + if (mapItemSavedData == NULL) + { + // 4J Stu - I don't see why we should be trying to work out the id again when it's passed as a param. + // In any case that won't work with the new map setup + //itemInstance->setAuxValue(level->getFreeAuxValueFor(L"map")); + + id = wstring( L"map_" ) + _toString(itemInstance->getAuxValue() ); + mapItemSavedData = shared_ptr( new MapItemSavedData(id) ); + + newData = true; + } + + mapItemSavedData->scale = 3; +#ifndef _LARGE_WORLDS + // 4J-PB - for Xbox maps, we'll centre them on the origin of the world, since we can fit the whole world in our map + mapItemSavedData->x = 0; + mapItemSavedData->z = 0; +#endif + + if( newData ) + { +#ifdef _LARGE_WORLDS + int scale = MapItemSavedData::MAP_SIZE * 2 * (1 << mapItemSavedData->scale); + mapItemSavedData->x = Math::round((float) level->getLevelData()->getXSpawn() / scale) * scale; + mapItemSavedData->z = Math::round(level->getLevelData()->getZSpawn() / scale) * scale; +#endif + mapItemSavedData->dimension = (byte) level->dimension->id; + + mapItemSavedData->setDirty(); + + level->setSavedData(id, (shared_ptr ) mapItemSavedData); + } + + return mapItemSavedData; +} + +void MapItem::update(Level *level, shared_ptr player, shared_ptr data) +{ + if (level->dimension->id != data->dimension) + { + // Wrong dimension, abort + return; + } + + int w = MapItem::IMAGE_WIDTH; + int h = MapItem::IMAGE_HEIGHT; + + int scale = 1 << data->scale; + + int xo = data->x; + int zo = data->z; + + int xp = Mth::floor(player->x - xo) / scale + w / 2; + int zp = Mth::floor(player->z - zo) / scale + h / 2; + + int rad = 128 / scale; + if (level->dimension->hasCeiling) + { + rad /= 2; + } + data->step++; + + for (int x = xp - rad + 1; x < xp + rad; x++) + { + if ((x & 15) != (data->step & 15)) continue; + + int yd0 = 255; + int yd1 = 0; + + double ho = 0; + for (int z = zp - rad - 1; z < zp + rad; z++) + { + if (x < 0 || z < -1 || x >= w || z >= h) continue; + + int xd = x - xp; + int zd = z - zp; + + bool ditherBlack = xd * xd + zd * zd > (rad - 2) * (rad - 2); + + int xx = (xo / scale + x - w / 2) * scale; + int zz = (zo / scale + z - h / 2) * scale; + + int r = 0; + int g = 0; + int b = 0; + + + int count[256]; + memset( count,0,sizeof(int)*256); + + LevelChunk *lc = level->getChunkAt(xx, zz); + if(lc->isEmpty()) continue; + int xso = ((xx)) & 15; + int zso = ((zz)) & 15; + int liquidDepth = 0; + + double hh = 0; + if (level->dimension->hasCeiling) + { + int ss = xx + zz * 231871; + ss = ss * ss * 31287121 + ss * 11; + if (((ss >> 20) & 1) == 0) count[Tile::dirt_Id] += 10; + else count[Tile::rock_Id] += 10; + hh = 100; + } + else + { + for (int xs = 0; xs < scale; xs++) + { + for (int zs = 0; zs < scale; zs++) + { + int yy = lc->getHeightmap(xs + xso, zs + zso) + 1; + int t = 0; + if (yy > 1) + { + bool ok = false; + do + { + ok = true; + t = lc->getTile(xs + xso, yy - 1, zs + zso); + if (t == 0) ok = false; + else if (yy > 0 && t > 0 && Tile::tiles[t]->material->color == MaterialColor::none) + { + ok = false; + } + + if (!ok) + { + yy--; + if (yy <= 0) break; + t = lc->getTile(xs + xso, yy - 1, zs + zso); + } + + } while (yy > 0 && !ok); + + if (yy > 0 && t != 0 && Tile::tiles[t]->material->isLiquid()) + { + int y = yy - 1; + int below = 0; + do + { + below = lc->getTile(xs + xso, y--, zs + zso); + liquidDepth++; + } while (y > 0 && below != 0 && Tile::tiles[below]->material->isLiquid()); + } + } + hh += yy / (double) (scale * scale); + + count[t]++; + } + } + } + liquidDepth /= scale * scale; + r /= scale * scale; + g /= scale * scale; + b /= scale * scale; + + int best = 0; + int tBest = 0; + for (int j = 0; j < 256; j++) + { + if (count[j] > best) + { + tBest = j; + best = count[j]; + } + } + + double diff = ((hh - ho) * 4 / (scale + 4)) + (((x + z) & 1) - 0.5) * 0.4; + int br = 1; + if (diff > +0.6) br = 2; + if (diff < -0.6) br = 0; + + int col = 0; + if (tBest > 0) + { + MaterialColor *mc = Tile::tiles[tBest]->material->color; + if (mc == MaterialColor::water) + { + diff = (liquidDepth * 0.1) + ((x + z) & 1) * 0.2; + br = 1; + if (diff < 0.5) br = 2; + if (diff > 0.9) br = 0; + } + col = mc->id; + } + + ho = hh; + + if (z < 0) continue; + if (xd * xd + zd * zd >= rad * rad) continue; + if (ditherBlack && ((x + z) & 1) == 0) + { + continue; + } + byte oldColor = data->colors[x + z * w]; + byte newColor = (byte) (col * 4 + br); + if (oldColor != newColor) + { + if (yd0 > z) yd0 = z; + if (yd1 < z) yd1 = z; + data->colors[x + z * w] = newColor; + } + } + if (yd0 <= yd1) + { + data->setDirty(x, yd0, yd1); + } + } +} + +void MapItem::inventoryTick(shared_ptr itemInstance, Level *level, shared_ptr owner, int slot, bool selected) +{ + if (level->isClientSide) return; + + shared_ptr data = getSavedData(itemInstance, level); + if (dynamic_pointer_cast(owner) != NULL) + { + shared_ptr player = dynamic_pointer_cast(owner); + + // 4J Stu - If the player has a map that belongs to another player, then merge the data over and change this map id to the owners id + int ownersAuxValue = level->getAuxValueForMap(player->getXuid(), data->dimension, data->x, data->z, data->scale); + if(ownersAuxValue != itemInstance->getAuxValue() ) + { + shared_ptr ownersData = getSavedData(ownersAuxValue,level); + + ownersData->x = data->x; + ownersData->z = data->z; + ownersData->scale = data->scale; + ownersData->dimension = data->dimension; + + itemInstance->setAuxValue( ownersAuxValue ); + ownersData->tickCarriedBy(player, itemInstance ); + ownersData->mergeInMapData(data); + player->inventoryMenu->broadcastChanges(); + + data = ownersData; + } + else + { + data->tickCarriedBy(player, itemInstance); + } + } + + if (selected) + { + update(level, owner, data); + } +} + +void MapItem::onCraftedBy(shared_ptr itemInstance, Level *level, shared_ptr player) +{ + wchar_t buf[64]; + + int mapScale = 3; +#ifdef _LARGE_WORLDS + int scale = MapItemSavedData::MAP_SIZE * 2 * (1 << mapScale); + int centreXC = (int) (Math::round(player->x / scale) * scale); + int centreZC = (int) (Math::round(player->z / scale) * scale); +#else + // 4J-PB - for Xbox maps, we'll centre them on the origin of the world, since we can fit the whole world in our map + int centreXC = 0; + int centreZC = 0; +#endif + + itemInstance->setAuxValue(level->getAuxValueForMap(player->getXuid(), player->dimension, centreXC, centreZC, mapScale)); + + swprintf(buf,64,L"map_%d", itemInstance->getAuxValue()); + std::wstring id = wstring(buf); + + shared_ptr data = getSavedData(itemInstance->getAuxValue(), level); + // 4J Stu - We only have one map per player per dimension, so don't reset the one that they have + // when a new one is created + if( data == NULL ) + { + data = shared_ptr( new MapItemSavedData(id) ); + } + level->setSavedData(id, (shared_ptr ) data); + + data->scale = mapScale; + // 4J-PB - for Xbox maps, we'll centre them on the origin of the world, since we can fit the whole world in our map + data->x = centreXC; + data->z = centreZC; + data->dimension = (byte) level->dimension->id; + data->setDirty(); +} + +shared_ptr MapItem::getUpdatePacket(shared_ptr itemInstance, Level *level, shared_ptr player) +{ + charArray data = MapItem::getSavedData(itemInstance, level)->getUpdatePacket(itemInstance, level, player); + + if (data.data == NULL || data.length == 0) return nullptr; + + shared_ptr retval = shared_ptr(new ComplexItemDataPacket((short) Item::map->id, (short) itemInstance->getAuxValue(), data)); + delete data.data; + return retval; +} diff --git a/Minecraft.World/MapItem.h b/Minecraft.World/MapItem.h new file mode 100644 index 00000000..dc16339d --- /dev/null +++ b/Minecraft.World/MapItem.h @@ -0,0 +1,23 @@ +#pragma once +using namespace std; + +#include "ComplexItem.h" + +class MapItemSavedData; + +class MapItem : public ComplexItem +{ +public: + static const int IMAGE_WIDTH = 128; + static const int IMAGE_HEIGHT = 128; + +public: // 4J Stu - Was protected in Java, but then we can't access it where we need it + MapItem(int id); + + static shared_ptr getSavedData(short idNum, Level *level); + shared_ptr getSavedData(shared_ptr itemInstance, Level *level); + void update(Level *level, shared_ptr player, shared_ptr data); + virtual void inventoryTick(shared_ptr itemInstance, Level *level, shared_ptr owner, int slot, bool selected); + virtual void onCraftedBy(shared_ptr itemInstance, Level *level, shared_ptr player); + shared_ptr getUpdatePacket(shared_ptr itemInstance, Level *level, shared_ptr player); +}; diff --git a/Minecraft.World/MapItemSavedData.cpp b/Minecraft.World/MapItemSavedData.cpp new file mode 100644 index 00000000..d69a7f53 --- /dev/null +++ b/Minecraft.World/MapItemSavedData.cpp @@ -0,0 +1,573 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "MapItemSavedData.h" +#include "..\Minecraft.Client\PlayerList.h" +#include "..\Minecraft.Client\MinecraftServer.h" +#include "..\Minecraft.Client\ServerPlayer.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.storage.h" +#include "..\Minecraft.Client\PlayerConnection.h" + +const int MapItemSavedData::END_PORTAL_DECORATION_KEY = -1; + +// 4J added entityId param +MapItemSavedData::MapDecoration::MapDecoration(char img, char x, char y, char rot, int entityId, bool visible) +{ + this->img = img; + this->x = x; + this->y = y; + this->rot = rot; + this->entityId = entityId; + this->visible = visible; +} + +MapItemSavedData::HoldingPlayer::HoldingPlayer(shared_ptr player, const MapItemSavedData *parent) : parent( parent ), player( player ) +{ + // inited outside of ctor + rowsDirtyMin = intArray(MapItem::IMAGE_WIDTH); + rowsDirtyMax = intArray(MapItem::IMAGE_WIDTH); + + tick = 0; + sendPosTick = 0; + + // java ctor + //this->player = player; + for (unsigned int i = 0; i < rowsDirtyMin.length; i++) + { + rowsDirtyMin[i] = 0; + rowsDirtyMax[i] = MapItem::IMAGE_HEIGHT - 1; + } +} + +MapItemSavedData::HoldingPlayer::~HoldingPlayer() +{ + delete rowsDirtyMin.data; + delete rowsDirtyMax.data; + delete lastSentDecorations.data; +} + +charArray MapItemSavedData::HoldingPlayer::nextUpdatePacket(shared_ptr itemInstance) +{ + if (--sendPosTick < 0) + { + sendPosTick = 4; + + unsigned int playerDecorationsSize = (int)parent->decorations.size(); + unsigned int nonPlayerDecorationsSize = (int)parent->nonPlayerDecorations.size(); + charArray data = charArray( (playerDecorationsSize + nonPlayerDecorationsSize ) * DEC_PACKET_BYTES + 1); + data[0] = 1; + for (unsigned int i = 0; i < parent->decorations.size(); i++) + { + MapDecoration *md = parent->decorations.at(i); +#ifdef _LARGE_WORLDS + data[i * DEC_PACKET_BYTES + 1] = (char) (md->img); + data[i * DEC_PACKET_BYTES + 8] = (char) (md->rot & 0xF); +#else + data[i * DEC_PACKET_BYTES + 1] = (char) ((md->img << 4) | (md->rot & 0xF)); +#endif + data[i * DEC_PACKET_BYTES + 2] = md->x; + data[i * DEC_PACKET_BYTES + 3] = md->y; + data[i * DEC_PACKET_BYTES + 4] = md->entityId & 0xFF; + data[i * DEC_PACKET_BYTES + 5] = (md->entityId>>8) & 0xFF; + data[i * DEC_PACKET_BYTES + 6] = (md->entityId>>16) & 0xFF; + data[i * DEC_PACKET_BYTES + 7] = (md->entityId>>24) & 0x7F; + data[i * DEC_PACKET_BYTES + 7] |= md->visible ? 0x80 : 0x0; + } + unsigned int dataIndex = playerDecorationsSize; + for(AUTO_VAR(it, parent->nonPlayerDecorations.begin()); it != parent->nonPlayerDecorations.end(); ++it) + { + MapDecoration *md = it->second; +#ifdef _LARGE_WORLDS + data[dataIndex * DEC_PACKET_BYTES + 1] = (char) (md->img); + data[dataIndex * DEC_PACKET_BYTES + 8] = (char) (md->rot & 0xF); +#else + data[dataIndex * DEC_PACKET_BYTES + 1] = (char) ((md->img << 4) | (md->rot & 0xF)); +#endif + data[dataIndex * DEC_PACKET_BYTES + 2] = md->x; + data[dataIndex * DEC_PACKET_BYTES + 3] = md->y; + data[dataIndex * DEC_PACKET_BYTES + 4] = md->entityId & 0xFF; + data[dataIndex * DEC_PACKET_BYTES + 5] = (md->entityId>>8) & 0xFF; + data[dataIndex * DEC_PACKET_BYTES + 6] = (md->entityId>>16) & 0xFF; + data[dataIndex * DEC_PACKET_BYTES + 7] = (md->entityId>>24) & 0x7F; + data[dataIndex * DEC_PACKET_BYTES + 7] |= md->visible ? 0x80 : 0x0; + + ++dataIndex; + } + bool thesame = !itemInstance->isFramed(); + if (lastSentDecorations.data == NULL || lastSentDecorations.length != data.length) + { + thesame = false; + } + else + { + for (unsigned int i = 0; i < data.length; i++) + { + if ( data[i] != lastSentDecorations[i]) + { + thesame = false; + break; + } + } + } + + if (!thesame) + { + if( lastSentDecorations.data != NULL ) + { + delete[] lastSentDecorations.data; + } + // Make a copy of data, as the calling function presumes it can destroy the returned data + lastSentDecorations = charArray(data.length); + memcpy(lastSentDecorations.data, data.data, data.length); + return data; + } + delete data.data; + } + shared_ptr servPlayer = dynamic_pointer_cast(player); + for (int d = 0; d < 10; d++) + { + int column = (tick * 11) % (MapItem::IMAGE_WIDTH); + tick++; + + if (rowsDirtyMin[column] >= 0) + { + int len = rowsDirtyMax[column] - rowsDirtyMin[column] + 1; + int min = rowsDirtyMin[column]; + + charArray data = charArray(len + 3); + data[0] = 0; + data[1] = (char) column; + data[2] = (char) min; + for (unsigned int y = 0; y < data.length - 3; y++) + { + data[y + 3] = parent->colors[(y + min) * MapItem::IMAGE_WIDTH + column]; + } + rowsDirtyMax[column] = -1; + rowsDirtyMin[column] = -1; + return data; + } + } + return charArray(); +} + + +MapItemSavedData::MapItemSavedData(const wstring& id) : SavedData( id ) +{ + x = z = 0; + dimension = 0; + scale = 0; + colors = byteArray( MapItem::IMAGE_WIDTH * MapItem::IMAGE_HEIGHT ); + step = 0; +} + +MapItemSavedData::~MapItemSavedData() +{ + delete colors.data; + for( unsigned int i = 0; i < decorations.size(); i++ ) + { + delete decorations[i]; + } +} + +void MapItemSavedData::load(CompoundTag *tag) +{ + dimension = tag->getByte(L"dimension"); + x = tag->getInt(L"xCenter"); + z = tag->getInt(L"zCenter"); + scale = tag->getByte(L"scale"); + if (scale < 0) scale = 0; + if (scale > 4) scale = 4; + + int width = tag->getShort(L"width"); + int height = tag->getShort(L"height"); + if (width == MapItem::IMAGE_WIDTH && height == MapItem::IMAGE_HEIGHT) + { + colors = tag->getByteArray(L"colors"); + } + else + { + byteArray newColors = tag->getByteArray(L"colors"); + //4J + if(colors.data != NULL) + { + delete[] colors.data; + } + //End4J + colors = byteArray(MapItem::IMAGE_WIDTH * MapItem::IMAGE_HEIGHT); + int xo = (MapItem::IMAGE_WIDTH - width) / 2; + int yo = (MapItem::IMAGE_HEIGHT - height) / 2; + for (int y = 0; y < height; y++) + { + int yt = y + yo; + if (yt < 0 && yt >= MapItem::IMAGE_HEIGHT) continue; + for (int x = 0; x < width; x++) + { + int xt = x + xo; + if (xt < 0 && xt >= MapItem::IMAGE_WIDTH) continue; + colors[xt + yt * MapItem::IMAGE_WIDTH] = newColors[x + y * width]; + } + } + } +} + +void MapItemSavedData::save(CompoundTag *tag) +{ + tag->putByte(L"dimension", dimension); + tag->putInt(L"xCenter", x); + tag->putInt(L"zCenter", z); + tag->putByte(L"scale", scale); + tag->putShort(L"width", (short) MapItem::IMAGE_WIDTH); + tag->putShort(L"height", (short) MapItem::IMAGE_HEIGHT); + tag->putByteArray(L"colors", colors); +} + +void MapItemSavedData::tickCarriedBy(shared_ptr player, shared_ptr item) +{ + if (carriedByPlayers.find(player) == carriedByPlayers.end()) + { + shared_ptr hp = shared_ptr( new HoldingPlayer(player, this ) ); + carriedByPlayers.insert( playerHoldingPlayerMapType::value_type(player, hp) ); + carriedBy.push_back(hp); + } + + for( unsigned int i = 0; i < decorations.size(); i++ ) + { + delete decorations[i]; + } + decorations.clear(); + + // 4J Stu - Put this block back in if you want to display entity positions on a map (see below) +#if 0 + nonPlayerDecorations.clear(); +#endif + bool addedPlayers = false; + for (AUTO_VAR(it, carriedBy.begin()); it != carriedBy.end(); ) + { + shared_ptr hp = *it; + + // 4J Stu - Players in the same dimension as an item frame with a map need to be sent this data, so don't remove them + if (hp->player->removed ) //|| (!hp->player->inventory->contains(item) && !item->isFramed() )) + { + AUTO_VAR(it2, carriedByPlayers.find( (shared_ptr ) hp->player )); + if( it2 != carriedByPlayers.end() ) + { + carriedByPlayers.erase( it2 ); + } + it = carriedBy.erase( std::find(carriedBy.begin(), carriedBy.end(), hp) ); + } + else + { + ++it; + + Level *playerLevel = hp->player->level; + if(!playerLevel->isClientSide && hp->player->dimension == 0 && (playerLevel->getLevelData()->getHasStrongholdEndPortal() || playerLevel->getLevelData()->getHasStronghold() ) ) + { + bool atLeastOnePlayerInTheEnd = false; + PlayerList *players = MinecraftServer::getInstance()->getPlayerList(); + for(AUTO_VAR(it3, players->players.begin()); it3 != players->players.end(); ++it3) + { + shared_ptr serverPlayer = *it3; + if(serverPlayer->dimension == 1) + { + atLeastOnePlayerInTheEnd = true; + break; + } + } + + AUTO_VAR(currentPortalDecoration, nonPlayerDecorations.find( END_PORTAL_DECORATION_KEY )); + if( currentPortalDecoration == nonPlayerDecorations.end() && atLeastOnePlayerInTheEnd) + { + float origX = 0.0f; + float origZ = 0.0f; + + if(playerLevel->getLevelData()->getHasStrongholdEndPortal()) + { + origX = playerLevel->getLevelData()->getXStrongholdEndPortal(); + origZ = playerLevel->getLevelData()->getZStrongholdEndPortal(); + } + else + { + origX = playerLevel->getLevelData()->getXStronghold()<<4; + origZ = playerLevel->getLevelData()->getZStronghold()<<4; + } + + float xd = (float) ( origX - x ) / (1 << scale); + float yd = (float) ( origZ - z ) / (1 << scale); + char x = (char) (xd * 2 + 0.5); + char y = (char) (yd * 2 + 0.5); + int size = MAP_SIZE - 1; +#ifdef _LARGE_WORLDS + if (xd < -size || yd < -size || xd > size || yd > size) + { + + if (xd <= -size) x = (byte) (size * 2 + 2.5); + if (yd <= -size) y = (byte) (size * 2 + 2.5); + if (xd >= size) x = (byte) (size * 2 + 1); + if (yd >= size) y = (byte) (size * 2 + 1); + } +#endif + //decorations.push_back(new MapDecoration(4, x, y, 0)); + nonPlayerDecorations.insert( unordered_map::value_type( END_PORTAL_DECORATION_KEY, new MapDecoration(4, x, y, 0, END_PORTAL_DECORATION_KEY, true) ) ); + } + else if ( currentPortalDecoration != nonPlayerDecorations.end() && !atLeastOnePlayerInTheEnd ) + { + delete currentPortalDecoration->second; + nonPlayerDecorations.erase( currentPortalDecoration ); + } + } + + if (item->isFramed()) + { + //addDecoration(1, player.level, "frame-" + item.getFrame().entityId, item.getFrame().xTile, item.getFrame().zTile, item.getFrame().dir * 90); + + if( nonPlayerDecorations.find( item->getFrame()->entityId ) == nonPlayerDecorations.end() ) + { + float xd = (float) ( item->getFrame()->xTile - x ) / (1 << scale); + float yd = (float) ( item->getFrame()->zTile - z ) / (1 << scale); + char x = (char) (xd * 2 + 0.5); + char y = (char) (yd * 2 + 0.5); + int size = MAP_SIZE - 1; + char rot = (char) ( (item->getFrame()->dir * 90) * 16 / 360); + if (dimension < 0) + { + int s = step / 10; + rot = (char) ((s * s * 34187121 + s * 121) >> 15 & 15); + } +#ifdef _LARGE_WORLDS + if (xd < -size || yd < -size || xd > size || yd > size) + { + + if (xd <= -size) x = (byte) (size * 2 + 2.5); + if (yd <= -size) y = (byte) (size * 2 + 2.5); + if (xd >= size) x = (byte) (size * 2 + 1); + if (yd >= size) y = (byte) (size * 2 + 1); + } +#endif + //decorations.push_back(new MapDecoration(7, x, y, 0)); + nonPlayerDecorations.insert( unordered_map::value_type( item->getFrame()->entityId, new MapDecoration(12, x, y, rot, item->getFrame()->entityId, true) ) ); + } + } + + // 4J Stu - Put this block back in if you want to display entity positions on a map (see above as well) +#if 0 + for(AUTO_VAR(it,playerLevel->entities.begin()); it != playerLevel->entities.end(); ++it) + { + shared_ptr ent = *it; + + if((ent->GetType() & eTYPE_ENEMY) == 0) continue; + + float xd = (float) ( ent->x - x ) / (1 << scale); + float yd = (float) ( ent->z - z ) / (1 << scale); + char x = (char) (xd * 2 + 0.5); + char y = (char) (yd * 2 + 0.5); + int size = MAP_SIZE - 1; + char rot = 0; + if (dimension < 0) + { + int s = step / 10; + rot = (char) ((s * s * 34187121 + s * 121) >> 15 & 15); + } +#ifdef _LARGE_WORLDS + if (xd < -size || yd < -size || xd > size || yd > size) + { + + if (xd <= -size) x = (byte) (size * 2 + 2.5); + if (yd <= -size) y = (byte) (size * 2 + 2.5); + if (xd >= size) x = (byte) (size * 2 + 1); + if (yd >= size) y = (byte) (size * 2 + 1); + } +#endif + //decorations.push_back(new MapDecoration(7, x, y, 0)); + nonPlayerDecorations.insert( unordered_map::value_type( ent->entityId, new MapDecoration(4, x, y, rot, ent->entityId, true) ) ); + } +#endif + + // 4J-PB - display all the players in the map + // For the xbox, x and z are 0 + if (hp->player->dimension == this->dimension && !addedPlayers) + { + addedPlayers = true; + + PlayerList *players = MinecraftServer::getInstance()->getPlayerList(); + for(AUTO_VAR(it3, players->players.begin()); it3 != players->players.end(); ++it3) + { + shared_ptr decorationPlayer = *it3; + if(decorationPlayer!=NULL && decorationPlayer->dimension == this->dimension) + { + float xd = (float) (decorationPlayer->x - x) / (1 << scale); + float yd = (float) (decorationPlayer->z - z) / (1 << scale); + char x = (char) (xd * 2); + char y = (char) (yd * 2); + int size = MAP_SIZE; // - 1; + char rot; + char imgIndex; + +#ifdef _LARGE_WORLDS + if (xd > -size && yd > -size && xd <= size && yd <= size) + +#endif + { + rot = (char) (decorationPlayer->yRot * 16 / 360 + 0.5); + if (dimension < 0) + { + int s = step / 10; + rot = (char) ((s * s * 34187121 + s * 121) >> 15 & 15); + } + + // 4J Stu - As we have added new icons for players on a new row below + // other icons used in Java we need to move our index to the next row + imgIndex = (int)decorationPlayer->getPlayerIndex(); + if(imgIndex>3) imgIndex += 4; + } +#ifdef _LARGE_WORLDS + else //if (abs(xd) < MAP_SIZE * 5 && abs(yd) < MAP_SIZE * 5) + { + // 4J Stu - As we have added new icons for players on a new row below + // other icons used in Java we need to move our index to the next row + imgIndex = (int)decorationPlayer->getPlayerIndex(); + if(imgIndex>3) imgIndex += 4; + imgIndex += 16; // Add 16 to indicate that it's on the next texture + + rot = 0; + size--; // Added to match the old adjusted size + if (xd <= -size) x = (byte) (size * 2 + 2.5); + if (yd <= -size) y = (byte) (size * 2 + 2.5); + if (xd >= size) x = (byte) (size * 2 + 1); + if (yd >= size) y = (byte) (size * 2 + 1); + } +#endif + + MemSect(45); + decorations.push_back(new MapDecoration(imgIndex, x, y, rot, decorationPlayer->entityId, (decorationPlayer == hp->player || decorationPlayer->canShowOnMaps()) )); + MemSect(0); + } + } + } + +// float xd = (float) (hp->player->x - x) / (1 << scale); +// float yd = (float) (hp->player->z - z) / (1 << scale); +// int ww = 64; +// int hh = 64; +// if (xd >= -ww && yd >= -hh && xd <= ww && yd <= hh) +// { +// char img = 0; +// char x = (char) (xd * 2 + 0.5); +// char y = (char) (yd * 2 + 0.5); +// char rot = (char) (player->yRot * 16 / 360 + 0.5); +// if (dimension < 0) +// { +// int s = step / 10; +// rot = (char) ((s * s * 34187121 + s * 121) >> 15 & 15); +// } +// if (hp->player->dimension == this->dimension) +// { +// decorations.push_back(new MapDecoration(img, x, y, rot)); +// } +// } + } + } +} + +charArray MapItemSavedData::getUpdatePacket(shared_ptr itemInstance, Level *level, shared_ptr player) +{ + AUTO_VAR(it, carriedByPlayers.find(player)); + if (it == carriedByPlayers.end() ) return charArray(); + + shared_ptr hp = it->second; + return hp->nextUpdatePacket(itemInstance); +} + +void MapItemSavedData::setDirty(int x, int y0, int y1) +{ + SavedData::setDirty(); + + AUTO_VAR(itEnd, carriedBy.end()); + for (AUTO_VAR(it, carriedBy.begin()); it != itEnd; it++) + { + shared_ptr hp = *it; //carriedBy.at(i); + if (hp->rowsDirtyMin[x] < 0 || hp->rowsDirtyMin[x] > y0) hp->rowsDirtyMin[x] = y0; + if (hp->rowsDirtyMax[x] < 0 || hp->rowsDirtyMax[x] < y1) hp->rowsDirtyMax[x] = y1; + } +} + +void MapItemSavedData::handleComplexItemData(charArray &data) +{ + if (data[0] == 0) + { + int xx = data[1] & 0xff; + int yy = data[2] & 0xff; + for (unsigned int y = 0; y < data.length - 3; y++) + { + colors[(y + yy) * MapItem::IMAGE_WIDTH + xx] = data[y + 3]; + } + setDirty(); + + } + else if (data[0] == 1) + { + for( unsigned int i = 0; i < decorations.size(); i++ ) + { + delete decorations[i]; + } + decorations.clear(); + for (unsigned int i = 0; i < (data.length - 1) / DEC_PACKET_BYTES; i++) + { +#ifdef _LARGE_WORLDS + char img = data[i * DEC_PACKET_BYTES + 1]; + char rot = data[i * DEC_PACKET_BYTES + 8]; +#else + // 4J-PB - this gives the wrong result + char img = (char) ( (((int)data[i * DEC_PACKET_BYTES + 1])&0xF0) >> 4); + char rot = (char) (data[i * DEC_PACKET_BYTES + 1] & 0xF); +#endif + char x = data[i * DEC_PACKET_BYTES + 2]; + char y = data[i * DEC_PACKET_BYTES + 3]; + int entityId = (((int)data[i * DEC_PACKET_BYTES + 4])&0xFF) | ( (((int)data[i * DEC_PACKET_BYTES + 5])&0xFF)<<8) | ((((int)data[i * DEC_PACKET_BYTES + 6])&0xFF)<<16) | ((((int)data[i * DEC_PACKET_BYTES + 7])&0x7F)<<24); + bool visible = (data[i * DEC_PACKET_BYTES + 7] & 0x80) != 0; + decorations.push_back(new MapDecoration(img, x, y, rot, entityId, visible)); + } + } +} + +// 4J Added +// We only have one map per player per dimension, so if they pickup someone elses map we merge their map data with ours +// so that we can see everything that they discovered but still only have one map data ourself +void MapItemSavedData::mergeInMapData(shared_ptr dataToAdd) +{ + int w = MapItem::IMAGE_WIDTH; + int h = MapItem::IMAGE_HEIGHT; + + for (int x = 0; x < w; x++) + { + int yd0 = 255; + int yd1 = 0; + + for (int z = 0; z < h; z++) + { + byte oldColor = colors[x + z * w]; + byte newColor = dataToAdd->colors[x + z * w]; + if (oldColor == 0 && oldColor != newColor) + { + if (yd0 > z) yd0 = z; + if (yd1 < z) yd1 = z; + colors[x + z * w] = newColor; + } + } + if (yd0 <= yd1) + { + setDirty(x, yd0, yd1); + } + } +} + +void MapItemSavedData::removeItemFrameDecoration(shared_ptr item) +{ + AUTO_VAR(frameDecoration, nonPlayerDecorations.find( item->getFrame()->entityId ) ); + if ( frameDecoration != nonPlayerDecorations.end() ) + { + delete frameDecoration->second; + nonPlayerDecorations.erase( frameDecoration ); + } +} diff --git a/Minecraft.World/MapItemSavedData.h b/Minecraft.World/MapItemSavedData.h new file mode 100644 index 00000000..fcbe5e30 --- /dev/null +++ b/Minecraft.World/MapItemSavedData.h @@ -0,0 +1,89 @@ +#pragma once + +#include "Player.h" +#include "SavedData.h" + +class MapItemSavedData : public SavedData +{ +public: + static const int MAP_SIZE = 64; + static const int MAX_SCALE = 4; + +#ifdef _LARGE_WORLDS + static const int DEC_PACKET_BYTES = 8; +#else + static const int DEC_PACKET_BYTES = 7; +#endif + + class MapDecoration + { + public: + char img, x, y, rot; + int entityId; // 4J Added + bool visible; + + MapDecoration(char img, char x, char y, char rot, int entityId, bool visible); // 4J added entityId, visible param + }; + + class HoldingPlayer + { + public: + const shared_ptr player; + intArray rowsDirtyMin; + intArray rowsDirtyMax; + + private: + int tick; + int sendPosTick; + charArray lastSentDecorations; + + protected: + const MapItemSavedData *parent; + + public: + // 4J Stu - Had to add a reference to the MapItemSavedData object that created us as we try to access it's member variables + HoldingPlayer(shared_ptr player, const MapItemSavedData *parent); + ~HoldingPlayer(); + charArray nextUpdatePacket(shared_ptr itemInstance); + }; + +public: + int x, z; + char dimension; + byte scale; + byteArray colors; + int step; + vector > carriedBy; + +private: + typedef unordered_map , shared_ptr , PlayerKeyHash, PlayerKeyEq> playerHoldingPlayerMapType; + playerHoldingPlayerMapType carriedByPlayers; + +public: + vector decorations; + +private: + // 4J Stu added + unordered_map nonPlayerDecorations; + static const int END_PORTAL_DECORATION_KEY; + + +public: + MapItemSavedData(const wstring& id); + ~MapItemSavedData(); + + virtual void load(CompoundTag *tag); + virtual void save(CompoundTag *tag); + + void tickCarriedBy(shared_ptr player, shared_ptr item); + + charArray getUpdatePacket(shared_ptr itemInstance, Level *level, shared_ptr player); + + using SavedData::setDirty; + void setDirty(int x, int y0, int y1); + void handleComplexItemData(charArray &data); + + // 4J Stu Added + void mergeInMapData(shared_ptr dataToAdd); + void removeItemFrameDecoration(shared_ptr item); +}; diff --git a/Minecraft.World/Material.cpp b/Minecraft.World/Material.cpp new file mode 100644 index 00000000..9e201ad5 --- /dev/null +++ b/Minecraft.World/Material.cpp @@ -0,0 +1,192 @@ +#include "stdafx.h" +#include "Material.h" +#include "DecorationMaterial.h" +#include "GasMaterial.h" +#include "LiquidMaterial.h" +#include "PortalMaterial.h" +#include "WebMaterial.h"// 4J added, Java version just does a local alteration when instantiating the Material for webs to get the same thing + +Material *Material::air = NULL; +Material *Material::grass = NULL; +Material *Material::dirt = NULL; +Material *Material::wood = NULL; +Material *Material::stone = NULL; +Material *Material::metal = NULL; +Material *Material::heavyMetal = NULL; +Material *Material::water = NULL; +Material *Material::lava = NULL; +Material *Material::leaves = NULL; +Material *Material::plant = NULL; +Material *Material::replaceable_plant = NULL; +Material *Material::sponge = NULL; +Material *Material::cloth = NULL; +Material *Material::fire = NULL; +Material *Material::sand = NULL; +Material *Material::decoration = NULL; +Material *Material::clothDecoration = NULL; +Material *Material::glass = NULL; +Material *Material::buildable_glass = NULL; +Material *Material::explosive = NULL; +Material *Material::coral = NULL; +Material *Material::ice = NULL; +Material *Material::topSnow = NULL; +Material *Material::snow = NULL; +Material *Material::cactus = NULL; +Material *Material::clay = NULL; +Material *Material::vegetable = NULL; +Material *Material::egg = NULL; +Material *Material::portal = NULL; +Material *Material::cake = NULL; +Material *Material::piston = NULL; +Material *Material::web = NULL; + +void Material::staticCtor() +{ + Material::air = new GasMaterial(MaterialColor::none); + Material::grass = new Material(MaterialColor::grass); + Material::dirt = new Material(MaterialColor::dirt); + Material::wood = (new Material(MaterialColor::wood))->flammable(); + Material::stone = (new Material(MaterialColor::stone))->notAlwaysDestroyable(); + Material::metal = (new Material(MaterialColor::metal))->notAlwaysDestroyable(); + Material::heavyMetal = (new Material(MaterialColor::metal))->notAlwaysDestroyable()->notPushable(); + Material::water = (new LiquidMaterial(MaterialColor::water))->destroyOnPush(); + Material::lava = (new LiquidMaterial(MaterialColor::fire))->destroyOnPush(); + Material::leaves = (new Material(MaterialColor::plant))->flammable()->neverBuildable()->destroyOnPush(); + Material::plant = (new DecorationMaterial(MaterialColor::plant))->destroyOnPush(); + Material::replaceable_plant = (new DecorationMaterial(MaterialColor::plant))->flammable()->destroyOnPush()->replaceable(); + Material::sponge = new Material(MaterialColor::cloth); + Material::cloth = (new Material(MaterialColor::cloth))->flammable(); + Material::fire = (new GasMaterial(MaterialColor::none))->destroyOnPush(); + Material::sand = new Material(MaterialColor::sand); + Material::decoration = (new DecorationMaterial(MaterialColor::none))->destroyOnPush(); + Material::clothDecoration = (new DecorationMaterial(MaterialColor::cloth))->flammable(); + Material::glass = (new Material(MaterialColor::none))->neverBuildable()->makeDestroyedByHand(); + Material::buildable_glass = (new Material(MaterialColor::none))->makeDestroyedByHand(); + Material::explosive = (new Material(MaterialColor::fire))->flammable()->neverBuildable(); + Material::coral = (new Material(MaterialColor::plant))->destroyOnPush(); + Material::ice = (new Material(MaterialColor::ice))->neverBuildable()->makeDestroyedByHand(); + Material::topSnow = (new DecorationMaterial(MaterialColor::snow))->replaceable()->neverBuildable()->notAlwaysDestroyable()->destroyOnPush(); + Material::snow = (new Material(MaterialColor::snow))->notAlwaysDestroyable(); + Material::cactus = (new Material(MaterialColor::plant))->neverBuildable()->destroyOnPush(); + Material::clay = (new Material(MaterialColor::clay)); + Material::vegetable = (new Material(MaterialColor::plant))->destroyOnPush(); + Material::egg = ( new Material(MaterialColor::plant))->destroyOnPush(); + Material::portal = (new PortalMaterial(MaterialColor::none))->notPushable(); + Material::cake = (new Material(MaterialColor::none))->destroyOnPush(); + // 4J added WebMaterial, Java version just does a local alteration when instantiating the Material for webs to get the same thing + Material::web = (new WebMaterial(MaterialColor::cloth))->notAlwaysDestroyable()->destroyOnPush(); + Material::piston = (new Material(MaterialColor::stone))->notPushable(); +} + +Material::Material(MaterialColor *color) +{ + this->color = color; + + // 4J Stu - Default inits + _flammable = false; + _replaceable = false; + _neverBuildable = false; + _isAlwaysDestroyable = true; + pushReaction = 0; + destroyedByHand = false; +} + +bool Material::isLiquid() +{ + return false; +} + +bool Material::letsWaterThrough() +{ + return (!isLiquid() && !isSolid()); +} + +bool Material::isSolid() +{ + return true; +} + +bool Material::blocksLight() +{ + return true; +} + +bool Material::blocksMotion() +{ + return true; +} + +Material *Material::neverBuildable() +{ + this->_neverBuildable = true; + return this; +} + +Material *Material::notAlwaysDestroyable() +{ + this->_isAlwaysDestroyable = false; + return this; +} + +Material *Material::flammable() +{ + this->_flammable = true; + return this; +} + +bool Material::isFlammable() +{ + return _flammable; +} + +Material *Material::replaceable() +{ + this->_replaceable = true; + return this; +} + +bool Material::isReplaceable() +{ + return _replaceable; +} + +bool Material::isSolidBlocking() +{ + if (_neverBuildable) return false; + return blocksMotion(); +} + +bool Material::isAlwaysDestroyable() +{ + // these materials will always drop resources when destroyed, regardless + // of player's equipment + return _isAlwaysDestroyable; +} + +int Material::getPushReaction() +{ + return pushReaction; +} + +Material *Material::makeDestroyedByHand() +{ + this->destroyedByHand = true; + return this; +} + +bool Material::isDestroyedByHand() +{ + return destroyedByHand; +} + +Material *Material::destroyOnPush() +{ + pushReaction = PUSH_DESTROY; + return this; +} + +Material *Material::notPushable() +{ + pushReaction = PUSH_BLOCK; + return this; +} \ No newline at end of file diff --git a/Minecraft.World/Material.h b/Minecraft.World/Material.h new file mode 100644 index 00000000..6e0571cf --- /dev/null +++ b/Minecraft.World/Material.h @@ -0,0 +1,92 @@ +#pragma once +#include "MaterialColor.h" + +class ChunkRebuildData; + +class Material +{ + friend class ChunkRebuildData; +public: + static Material *air; + static Material *grass; + static Material *dirt; + static Material *wood; + static Material *stone; + static Material *metal; + static Material *heavyMetal; + static Material *water; + static Material *lava; + static Material *leaves; + static Material *plant; + static Material *replaceable_plant; + static Material *sponge; + static Material *cloth; + static Material *fire; + static Material *sand; + static Material *decoration; + static Material *clothDecoration; + static Material *glass; + static Material *buildable_glass; + static Material *explosive; + static Material *coral; + static Material *ice; + static Material *topSnow; + static Material *snow; + static Material *cactus; + static Material *clay; + static Material *vegetable; + static Material *egg; + static Material *portal; + static Material *cake; + static Material *web; + static Material *piston; + + static const int PUSH_NORMAL = 0; + static const int PUSH_DESTROY = 1; + static const int PUSH_BLOCK = 2; // not pushable + + static void staticCtor(); + +private: + bool _flammable, _replaceable, _neverBuildable; + +public: + MaterialColor *color; +private: + bool _isAlwaysDestroyable; + int pushReaction; + bool destroyedByHand; +public: + + Material(MaterialColor *color); + virtual bool isLiquid() ; + virtual bool letsWaterThrough(); + virtual bool isSolid(); + virtual bool blocksLight(); + virtual bool blocksMotion(); + +private: + virtual Material *neverBuildable(); +protected: + virtual Material *notAlwaysDestroyable(); + virtual Material *flammable(); + +public: + virtual bool isFlammable(); + virtual Material *replaceable(); + virtual bool isReplaceable(); + virtual bool isSolidBlocking(); + virtual bool isAlwaysDestroyable(); + virtual int getPushReaction(); + +protected: + Material *makeDestroyedByHand(); + +public: + bool isDestroyedByHand(); + +protected: + Material *destroyOnPush(); + Material *notPushable(); +}; + diff --git a/Minecraft.World/MaterialColor.cpp b/Minecraft.World/MaterialColor.cpp new file mode 100644 index 00000000..d14748b2 --- /dev/null +++ b/Minecraft.World/MaterialColor.cpp @@ -0,0 +1,46 @@ +#include "stdafx.h" +#include "MaterialColor.h" + +MaterialColor **MaterialColor::colors; + +MaterialColor *MaterialColor::none = NULL; +MaterialColor *MaterialColor::grass = NULL; +MaterialColor *MaterialColor::sand = NULL; +MaterialColor *MaterialColor::cloth = NULL; +MaterialColor *MaterialColor::fire = NULL; +MaterialColor *MaterialColor::ice = NULL; +MaterialColor *MaterialColor::metal = NULL; +MaterialColor *MaterialColor::plant = NULL; +MaterialColor *MaterialColor::snow = NULL; +MaterialColor *MaterialColor::clay = NULL; +MaterialColor *MaterialColor::dirt = NULL; +MaterialColor *MaterialColor::stone = NULL; +MaterialColor *MaterialColor::water = NULL; +MaterialColor *MaterialColor::wood = NULL; + +void MaterialColor::staticCtor() +{ + MaterialColor::colors = new MaterialColor *[16]; + + MaterialColor::none = new MaterialColor(0, eMinecraftColour_Material_None); + MaterialColor::grass = new MaterialColor(1, eMinecraftColour_Material_Grass); + MaterialColor::sand = new MaterialColor(2, eMinecraftColour_Material_Sand); + MaterialColor::cloth = new MaterialColor(3, eMinecraftColour_Material_Cloth); + MaterialColor::fire = new MaterialColor(4, eMinecraftColour_Material_Fire); + MaterialColor::ice = new MaterialColor(5, eMinecraftColour_Material_Ice); + MaterialColor::metal = new MaterialColor(6, eMinecraftColour_Material_Metal); + MaterialColor::plant = new MaterialColor(7, eMinecraftColour_Material_Plant); + MaterialColor::snow = new MaterialColor(8, eMinecraftColour_Material_Snow); + MaterialColor::clay = new MaterialColor(9, eMinecraftColour_Material_Clay); + MaterialColor::dirt = new MaterialColor(10, eMinecraftColour_Material_Dirt); + MaterialColor::stone = new MaterialColor(11, eMinecraftColour_Material_Stone); + MaterialColor::water = new MaterialColor(12, eMinecraftColour_Material_Water); + MaterialColor::wood = new MaterialColor(13, eMinecraftColour_Material_Wood); +} + +MaterialColor::MaterialColor(int id, eMinecraftColour col) +{ + this->id = id; + this->col = col; + colors[id] = this; +} \ No newline at end of file diff --git a/Minecraft.World/MaterialColor.h b/Minecraft.World/MaterialColor.h new file mode 100644 index 00000000..70d2be89 --- /dev/null +++ b/Minecraft.World/MaterialColor.h @@ -0,0 +1,31 @@ +#pragma once + +class MaterialColor +{ +public: + static MaterialColor **colors; + + static MaterialColor *none; + static MaterialColor *grass; + static MaterialColor *sand; + static MaterialColor *cloth; + static MaterialColor *fire; + static MaterialColor *ice; + static MaterialColor *metal; + static MaterialColor *plant; + static MaterialColor *snow; + static MaterialColor *clay; + static MaterialColor *dirt; + static MaterialColor *stone; + static MaterialColor *water; + static MaterialColor *wood; + + static void staticCtor(); + +public: + eMinecraftColour col; + int id; + +private: + MaterialColor(int id, eMinecraftColour col); +}; diff --git a/Minecraft.World/McRegionChunkStorage.cpp b/Minecraft.World/McRegionChunkStorage.cpp new file mode 100644 index 00000000..a11cb1c1 --- /dev/null +++ b/Minecraft.World/McRegionChunkStorage.cpp @@ -0,0 +1,460 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "ConsoleSaveFileIO.h" +#include "LevelData.h" +#include "McRegionChunkStorage.h" + +CRITICAL_SECTION McRegionChunkStorage::cs_memory; + +std::deque McRegionChunkStorage::s_chunkDataQueue; +int McRegionChunkStorage::s_runningThreadCount = 0; +C4JThread *McRegionChunkStorage::s_saveThreads[3]; + + +McRegionChunkStorage::McRegionChunkStorage(ConsoleSaveFile *saveFile, const wstring &prefix) : m_prefix( prefix ) +{ + m_saveFile = saveFile; + + // Make sure that if there are any files for regions to be created, that they are created in the order that suits us for making the initial level save work fast + if( prefix == L"" ) + { + m_saveFile->createFile(ConsoleSavePath(L"DIM-1r.-1.-1.mcr")); + m_saveFile->createFile(ConsoleSavePath(L"DIM-1r.0.-1.mcr")); + m_saveFile->createFile(ConsoleSavePath(L"DIM-1r.0.0.mcr")); + m_saveFile->createFile(ConsoleSavePath(L"DIM-1r.-1.0.mcr")); + m_saveFile->createFile(ConsoleSavePath(L"DIM1/r.-1.-1.mcr")); + m_saveFile->createFile(ConsoleSavePath(L"DIM1/r.0.-1.mcr")); + m_saveFile->createFile(ConsoleSavePath(L"DIM1/r.0.0.mcr")); + m_saveFile->createFile(ConsoleSavePath(L"DIM1/r.-1.0.mcr")); + m_saveFile->createFile(ConsoleSavePath(L"r.-1.-1.mcr")); + m_saveFile->createFile(ConsoleSavePath(L"r.0.-1.mcr")); + m_saveFile->createFile(ConsoleSavePath(L"r.0.0.mcr")); + m_saveFile->createFile(ConsoleSavePath(L"r.-1.0.mcr")); + } + + +#ifdef SPLIT_SAVES + ConsoleSavePath currentFile = ConsoleSavePath( m_prefix + wstring( L"entities.dat" ) ); + + if(m_saveFile->doesFileExist(currentFile)) + { + ConsoleSaveFileInputStream fis = ConsoleSaveFileInputStream( m_saveFile, currentFile ); + DataInputStream dis(&fis); + + int count = dis.readInt(); + + for(int i = 0; i < count; ++i) + { + __int64 index = dis.readLong(); + CompoundTag *tag = NbtIo::read(&dis); + + ByteArrayOutputStream bos; + DataOutputStream dos(&bos); + NbtIo::write(tag, &dos); + delete tag; + + byteArray savedData(bos.size()); + memcpy(savedData.data, bos.buf.data, bos.size()); + + m_entityData[index] = savedData; + } + } +#endif +} + +McRegionChunkStorage::~McRegionChunkStorage() +{ + for(AUTO_VAR(it,m_entityData.begin()); it != m_entityData.end(); ++it) + { + delete it->second.data; + } +} + +LevelChunk *McRegionChunkStorage::load(Level *level, int x, int z) +{ + DataInputStream *regionChunkInputStream = RegionFileCache::getChunkDataInputStream(m_saveFile, m_prefix, x, z); + +#ifdef SPLIT_SAVES + // If we can't find the chunk in the save file, then we should remove any entities we might have for that chunk + if(regionChunkInputStream == NULL) + { + __int64 index = ((__int64)(x) << 32) | (((__int64)(z))&0x00000000FFFFFFFF); + + AUTO_VAR(it, m_entityData.find(index)); + if(it != m_entityData.end()) + { + delete it->second.data; + m_entityData.erase(it); + } + } +#endif + + LevelChunk *levelChunk = NULL; + + if(m_saveFile->getOriginalSaveVersion() >= SAVE_FILE_VERSION_COMPRESSED_CHUNK_STORAGE) + { + if (regionChunkInputStream != NULL) + { + MemSect(9); + levelChunk = OldChunkStorage::load(level, regionChunkInputStream); + loadEntities(level, levelChunk); + MemSect(0); + regionChunkInputStream->deleteChildStream(); + delete regionChunkInputStream; + } + } + else + { + CompoundTag *chunkData; + if (regionChunkInputStream != NULL) + { + MemSect(8); + chunkData = NbtIo::read((DataInput *)regionChunkInputStream); + MemSect(0); + } else + { + return NULL; + } + + regionChunkInputStream->deleteChildStream(); + delete regionChunkInputStream; + + if (!chunkData->contains(L"Level")) + { + char buf[256]; + sprintf(buf,"Chunk file at %d, %d is missing level data, skipping\n",x, z); + app.DebugPrintf(buf); + delete chunkData; + return NULL; + } + if (!chunkData->getCompound(L"Level")->contains(L"Blocks")) + { + char buf[256]; + sprintf(buf,"Chunk file at %d, %d is missing block data, skipping\n",x, z); + app.DebugPrintf(buf); + delete chunkData; + return NULL; + } + MemSect(9); + levelChunk = OldChunkStorage::load(level, chunkData->getCompound(L"Level")); + MemSect(0); + if (!levelChunk->isAt(x, z)) + { + char buf[256]; + sprintf(buf,"Chunk file at %d, %d is in the wrong location; relocating. Expected %d, %d, got %d, %d\n", + x, z, x, z, levelChunk->x, levelChunk->z); + app.DebugPrintf(buf); + delete levelChunk; + delete chunkData; + return NULL; + + // 4J Stu - We delete the data within OldChunkStorage::load, so we can never reload from it + //chunkData->putInt(L"xPos", x); + //chunkData->putInt(L"zPos", z); + //MemSect(10); + //levelChunk = OldChunkStorage::load(level, chunkData->getCompound(L"Level")); + //MemSect(0); + } +#ifdef SPLIT_SAVES + loadEntities(level, levelChunk); +#endif + delete chunkData; + } + return levelChunk; + +} + +void McRegionChunkStorage::save(Level *level, LevelChunk *levelChunk) +{ + level->checkSession(); + + // 4J - removed try/catch +// try { + + // Note - have added use of a critical section round sections of code that do a lot of memory alloc/free operations. This is because + // when we are running saves on multiple threads these sections have a lot of contention and thrash the memory system's critical sections + // Better to let each thread have its turn at a higher level of granularity. + MemSect(30); + PIXBeginNamedEvent(0,"Getting output stream\n"); + DataOutputStream *output = RegionFileCache::getChunkDataOutputStream(m_saveFile, m_prefix, levelChunk->x, levelChunk->z); + PIXEndNamedEvent(); + + if(m_saveFile->getOriginalSaveVersion() >= SAVE_FILE_VERSION_COMPRESSED_CHUNK_STORAGE) + { + PIXBeginNamedEvent(0,"Writing chunk data"); + OldChunkStorage::save(levelChunk, level, output); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Updating chunk queue"); + EnterCriticalSection(&cs_memory); + s_chunkDataQueue.push_back(output); + LeaveCriticalSection(&cs_memory); + PIXEndNamedEvent(); + } + else + { + EnterCriticalSection(&cs_memory); + PIXBeginNamedEvent(0,"Creating tags\n"); + CompoundTag *tag = new CompoundTag(); + CompoundTag *levelData = new CompoundTag(); + tag->put(L"Level", levelData); + OldChunkStorage::save(levelChunk, level, levelData); + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"NbtIo writing\n"); + NbtIo::write(tag, output); + PIXEndNamedEvent(); + LeaveCriticalSection(&cs_memory); + PIXBeginNamedEvent(0,"Output closing\n"); + output->close(); + PIXEndNamedEvent(); + + + // 4J Stu - getChunkDataOutputStream makes a new DataOutputStream that points to a new ChunkBuffer( ByteArrayOutputStream ) + // We should clean these up when we are done + EnterCriticalSection(&cs_memory); + PIXBeginNamedEvent(0,"Cleaning up\n"); + output->deleteChildStream(); + delete output; + delete tag; + LeaveCriticalSection(&cs_memory); + PIXEndNamedEvent(); + } + MemSect(0); + + LevelData *levelInfo = level->getLevelData(); + + // 4J Stu - Override this with our save file size to stop all the RegionFileCache lookups + //levelInfo->setSizeOnDisk(levelInfo->getSizeOnDisk() + RegionFileCache::getSizeDelta(m_saveFile, m_prefix, levelChunk->x, levelChunk->z)); + levelInfo->setSizeOnDisk( this->m_saveFile->getSizeOnDisk() ); +// } catch (Exception e) { +// e.printStackTrace(); +// } +} + +void McRegionChunkStorage::saveEntities(Level *level, LevelChunk *levelChunk) +{ +#ifdef SPLIT_SAVES + PIXBeginNamedEvent(0,"Saving entities"); + __int64 index = ((__int64)(levelChunk->x) << 32) | (((__int64)(levelChunk->z))&0x00000000FFFFFFFF); + + delete m_entityData[index].data; + + CompoundTag *newTag = new CompoundTag(); + bool savedEntities = OldChunkStorage::saveEntities(levelChunk, level, newTag); + + if(savedEntities) + { + ByteArrayOutputStream bos; + DataOutputStream dos(&bos); + NbtIo::write(newTag, &dos); + + byteArray savedData(bos.size()); + memcpy(savedData.data, bos.buf.data, bos.size()); + + m_entityData[index] = savedData; + } + else + { + AUTO_VAR(it, m_entityData.find(index)); + if(it != m_entityData.end()) + { + m_entityData.erase(it); + } + } + delete newTag; + PIXEndNamedEvent(); +#endif +} + +void McRegionChunkStorage::loadEntities(Level *level, LevelChunk *levelChunk) +{ +#ifdef SPLIT_SAVES + __int64 index = ((__int64)(levelChunk->x) << 32) | (((__int64)(levelChunk->z))&0x00000000FFFFFFFF); + + AUTO_VAR(it, m_entityData.find(index)); + if(it != m_entityData.end()) + { + ByteArrayInputStream bais(it->second); + DataInputStream dis(&bais); + CompoundTag *tag = NbtIo::read(&dis); + OldChunkStorage::loadEntities(levelChunk, level, tag); + bais.reset(); + delete tag; + } +#endif +} + +void McRegionChunkStorage::tick() +{ + m_saveFile->tick(); +} + +void McRegionChunkStorage::flush() +{ +#ifdef SPLIT_SAVES + PIXBeginNamedEvent(0, "Flushing entity data"); + ConsoleSavePath currentFile = ConsoleSavePath( m_prefix + wstring( L"entities.dat" ) ); + ConsoleSaveFileOutputStream fos = ConsoleSaveFileOutputStream( m_saveFile, currentFile ); + BufferedOutputStream bos(&fos, 1024*1024); + DataOutputStream dos(&bos); + + PIXBeginNamedEvent(0,"Writing to stream"); + dos.writeInt(m_entityData.size()); + + for(AUTO_VAR(it,m_entityData.begin()); it != m_entityData.end(); ++it) + { + dos.writeLong(it->first); + dos.write(it->second,0,it->second.length); + } + bos.flush(); + PIXEndNamedEvent(); + PIXEndNamedEvent(); +#endif +} + + +void McRegionChunkStorage::staticCtor() +{ + InitializeCriticalSectionAndSpinCount(&cs_memory,5120); + + for(unsigned int i = 0; i < 3; ++i) + { + char threadName[256]; + sprintf(threadName,"McRegion Save thread %d\n",i); + SetThreadName(0, threadName); + + //saveThreads[j] = CreateThread(NULL,0,runSaveThreadProc,&threadData[j],CREATE_SUSPENDED,&threadId[j]); + s_saveThreads[i] = new C4JThread(runSaveThreadProc,NULL,threadName); + + + //app.DebugPrintf("Created new thread: %s\n",threadName); + + // Threads 1,3 and 5 are generally idle so use them + if(i == 0) s_saveThreads[i]->SetProcessor(CPU_CORE_SAVE_THREAD_A); + else if(i == 1) + { + s_saveThreads[i]->SetProcessor(CPU_CORE_SAVE_THREAD_B); +#ifdef __ORBIS__ + s_saveThreads[i]->SetPriority(THREAD_PRIORITY_BELOW_NORMAL); // On Orbis, this core is also used for Matching 2, and that priority of that seems to be always at default no matter what we set it to. Prioritise this below Matching 2. +#endif + } + else if(i == 2) s_saveThreads[i]->SetProcessor(CPU_CORE_SAVE_THREAD_C); + + //ResumeThread( saveThreads[j] ); + s_saveThreads[i]->Run(); + } +} + +int McRegionChunkStorage::runSaveThreadProc(LPVOID lpParam) +{ + Compression::CreateNewThreadStorage(); + + bool running = true; + size_t lastQueueSize = 0; + + DataOutputStream *dos = NULL; + while(running) + { + if( TryEnterCriticalSection(&cs_memory) ) + { + lastQueueSize = s_chunkDataQueue.size(); + if(lastQueueSize > 0) + { + dos = s_chunkDataQueue.front(); + s_chunkDataQueue.pop_front(); + } + s_runningThreadCount++; + LeaveCriticalSection(&cs_memory); + + if(dos) + { + PIXBeginNamedEvent(0,"Saving chunk"); + //app.DebugPrintf("Compressing chunk data (%d left)\n", lastQueueSize - 1); + dos->close(); + dos->deleteChildStream(); + PIXEndNamedEvent(); + } + delete dos; + dos = NULL; + + EnterCriticalSection(&cs_memory); + s_runningThreadCount--; + LeaveCriticalSection(&cs_memory); + } + + // If there was more than one thing in the queue last time we checked, then we want to spin round again soon + // Otherwise wait a bit longer + if( (lastQueueSize -1) > 0) Sleep(1); // Sleep 1 to yield + else Sleep(100); + } + + Compression::ReleaseThreadStorage(); + + return 0; +} + +void McRegionChunkStorage::WaitForAll() +{ + WaitForAllSaves(); +} + +void McRegionChunkStorage::WaitIfTooManyQueuedChunks() +{ + WaitForSaves(); +} + +// Static +void McRegionChunkStorage::WaitForAllSaves() +{ + // Wait for there to be no more tasks to be processed... + EnterCriticalSection(&cs_memory); + size_t queueSize = s_chunkDataQueue.size(); + LeaveCriticalSection(&cs_memory); + + while(queueSize > 0) + { + Sleep(10); + + EnterCriticalSection(&cs_memory); + queueSize = s_chunkDataQueue.size(); + LeaveCriticalSection(&cs_memory); + } + + // And then wait for there to be no running threads that are processing these tasks + EnterCriticalSection(&cs_memory); + int runningThreadCount = s_runningThreadCount; + LeaveCriticalSection(&cs_memory); + + while(runningThreadCount > 0) + { + Sleep(10); + + EnterCriticalSection(&cs_memory); + runningThreadCount = s_runningThreadCount; + LeaveCriticalSection(&cs_memory); + } +} + +// Static +void McRegionChunkStorage::WaitForSaves() +{ + static const int MAX_QUEUE_SIZE = 12; + static const int DESIRED_QUEUE_SIZE = 6; + + // Wait for the queue to reduce to a level where we should add more elements + EnterCriticalSection(&cs_memory); + size_t queueSize = s_chunkDataQueue.size(); + LeaveCriticalSection(&cs_memory); + + if( queueSize > MAX_QUEUE_SIZE ) + { + while( queueSize > DESIRED_QUEUE_SIZE ) + { + Sleep(10); + + EnterCriticalSection(&cs_memory); + queueSize = s_chunkDataQueue.size(); + LeaveCriticalSection(&cs_memory); + } + } +} diff --git a/Minecraft.World/McRegionChunkStorage.h b/Minecraft.World/McRegionChunkStorage.h new file mode 100644 index 00000000..50dabd52 --- /dev/null +++ b/Minecraft.World/McRegionChunkStorage.h @@ -0,0 +1,43 @@ +#pragma once +using namespace std; + +#include "ChunkStorage.h" +#include "LevelChunk.h" +#include "RegionFileCache.h" +#include "com.mojang.nbt.h" +#include "OldChunkStorage.h" + +class ConsoleSaveFile; + +class McRegionChunkStorage : public ChunkStorage +{ +private: + const wstring m_prefix; + ConsoleSaveFile *m_saveFile; + static CRITICAL_SECTION cs_memory; + + unordered_map<__int64, byteArray> m_entityData; + + static std::deque s_chunkDataQueue; + static int s_runningThreadCount; + static C4JThread *s_saveThreads[3]; + +public: + McRegionChunkStorage(ConsoleSaveFile *saveFile, const wstring &prefix); + ~McRegionChunkStorage(); + static void staticCtor(); + + virtual LevelChunk *load(Level *level, int x, int z); + virtual void save(Level *level, LevelChunk *levelChunk); + virtual void saveEntities(Level *level, LevelChunk *levelChunk); + virtual void loadEntities(Level *level, LevelChunk *levelChunk); + virtual void tick(); + virtual void flush(); + virtual void WaitForAll(); // 4J Added + virtual void WaitIfTooManyQueuedChunks(); // 4J Added + +private: + static void WaitForAllSaves(); + static void WaitForSaves(); + static int runSaveThreadProc(LPVOID lpParam); +}; diff --git a/Minecraft.World/McRegionLevelStorage.cpp b/Minecraft.World/McRegionLevelStorage.cpp new file mode 100644 index 00000000..687ee048 --- /dev/null +++ b/Minecraft.World/McRegionLevelStorage.cpp @@ -0,0 +1,103 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.chunk.storage.h" +#include "net.minecraft.world.level.dimension.h" +#include "ConsoleSaveFileIO.h" +#include "LevelData.h" +#include "File.h" +#include "McRegionLevelStorage.h" + +McRegionLevelStorage::McRegionLevelStorage(ConsoleSaveFile *saveFile, File dir, const wstring& levelName, bool createPlayerDir) + : DirectoryLevelStorage(saveFile, dir, levelName, createPlayerDir) +{ + RegionFileCache::clear(); +} + +McRegionLevelStorage::~McRegionLevelStorage() +{ + // Make sure cache is clear, as the DirectoryLevelStorage destructor is going to be deleting the underlying ConsoleSaveFile + // reference so we don't want the RegionFileCache to still be referencing it either + RegionFileCache::clear(); +} + +ChunkStorage *McRegionLevelStorage::createChunkStorage(Dimension *dimension) +{ + //File folder = getFolder(); + + if (dynamic_cast(dimension) != NULL) + { + + if(app.GetResetNether()) + { +#ifdef SPLIT_SAVES + vector *netherFiles = m_saveFile->getRegionFilesByDimension(1); + if(netherFiles!=NULL) + { + DWORD bytesWritten = 0; + for(AUTO_VAR(it, netherFiles->begin()); it != netherFiles->end(); ++it) + { + m_saveFile->zeroFile(*it, (*it)->getFileSize(), &bytesWritten); + } + delete netherFiles; + } +#else + vector *netherFiles = m_saveFile->getFilesWithPrefix(LevelStorage::NETHER_FOLDER); + if(netherFiles!=NULL) + { + for(AUTO_VAR(it, netherFiles->begin()); it != netherFiles->end(); ++it) + { + m_saveFile->deleteFile(*it); + } + delete netherFiles; + } +#endif + resetNetherPlayerPositions(); + } + + return new McRegionChunkStorage(m_saveFile, LevelStorage::NETHER_FOLDER); + } + + if (dynamic_cast(dimension)) + { + //File dir2 = new File(folder, LevelStorage.ENDER_FOLDER); + //dir2.mkdirs(); + //return new ThreadedMcRegionChunkStorage(dir2); + + // 4J-PB - save version 0 at this point means it's a create new world + int iSaveVersion=m_saveFile->getSaveVersion(); + + if((iSaveVersion!=0) && (iSaveVersion < SAVE_FILE_VERSION_NEW_END)) + { + // For versions before TU9 (TU7 and 8) we generate a part of The End, but we want to scrap it if it exists so that it is replaced with the TU9+ version + app.DebugPrintf("Loaded save version number is: %d, required to keep The End is: %d\n",m_saveFile->getSaveVersion(), SAVE_FILE_VERSION_NEW_END); + + vector *endFiles = m_saveFile->getFilesWithPrefix(LevelStorage::ENDER_FOLDER); + + // 4J-PB - There will be no End in early saves + if(endFiles!=NULL) + { + for(AUTO_VAR(it, endFiles->begin()); it != endFiles->end(); ++it) + { + m_saveFile->deleteFile(*it); + } + delete endFiles; + } + } + return new McRegionChunkStorage(m_saveFile, LevelStorage::ENDER_FOLDER); + } + + return new McRegionChunkStorage(m_saveFile, L""); +} + +void McRegionLevelStorage::saveLevelData(LevelData *levelData, vector > *players) +{ + levelData->setVersion(MCREGION_VERSION_ID); + MemSect(38); + DirectoryLevelStorage::saveLevelData(levelData, players); + MemSect(0); +} + +void McRegionLevelStorage::closeAll() +{ + RegionFileCache::clear(); +} \ No newline at end of file diff --git a/Minecraft.World/McRegionLevelStorage.h b/Minecraft.World/McRegionLevelStorage.h new file mode 100644 index 00000000..abaa17d1 --- /dev/null +++ b/Minecraft.World/McRegionLevelStorage.h @@ -0,0 +1,22 @@ +#pragma once +using namespace std; + +#include "DirectoryLevelStorage.h" + +class McRegionLevelStorage : public DirectoryLevelStorage +{ +// private static final Logger logger = Logger.getLogger("Minecraft"); + + friend class McRegionLevelStorageSource; // 4J Jev, needs access to protected members. + +protected: + static const int MCREGION_VERSION_ID = 0x4abc; + +public: + McRegionLevelStorage(ConsoleSaveFile *saveFile, File dir, const wstring& levelName, bool createPlayerDir); + ~McRegionLevelStorage(); + + virtual ChunkStorage *createChunkStorage(Dimension *dimension); + virtual void saveLevelData(LevelData *levelData, vector > *players); + virtual void closeAll(); +}; \ No newline at end of file diff --git a/Minecraft.World/McRegionLevelStorageSource.cpp b/Minecraft.World/McRegionLevelStorageSource.cpp new file mode 100644 index 00000000..0652ad84 --- /dev/null +++ b/Minecraft.World/McRegionLevelStorageSource.cpp @@ -0,0 +1,365 @@ +#include "stdafx.h" +#include "JavaMath.h" +#include "BasicTypeContainers.h" +#if 0 +// 4J - not required anymore +#include "Matcher.h" +#endif +#include "ProgressListener.h" +#include "net.minecraft.world.level.chunk.storage.h" +#include "net.minecraft.world.level.chunk.h" +#include "LevelSummary.h" +#include "McRegionLevelStorage.h" +#include "File.h" +#include "LevelData.h" +#include "McRegionLevelStorageSource.h" + +#include "ConsoleSaveFileIO.h" + +#if 0 +// 4J - not required anymore +// These were Pattern class objects, using the c++0x regex class instead +const std::tr1::wregex McRegionLevelStorageSource::FolderFilter::chunkFolderPattern = std::tr1::wregex(L"[0-9a-z]|([0-9a-z][0-9a-z])"); +const std::tr1::wregex McRegionLevelStorageSource::ChunkFilter::chunkFilePattern = std::tr1::wregex(L"c\\.(-?[0-9a-z]+)\\.(-?[0-9a-z]+)\\.dat"); +#endif + +McRegionLevelStorageSource::McRegionLevelStorageSource(File dir) : DirectoryLevelStorageSource(dir) +{ +} + +wstring McRegionLevelStorageSource::getName() +{ + return L"Scaevolus' McRegion"; +} + +vector *McRegionLevelStorageSource::getLevelList() +{ + // 4J Stu - We don't need to do directory lookups with the xbox save files + vector *levels = new vector; +#if 0 + vector *subFolders = baseDir.listFiles(); + File *file; + AUTO_VAR(itEnd, subFolders->end()); + for (AUTO_VAR(it, subFolders->begin()); it != itEnd; it++) + { + file = *it; //subFolders->at(i); + + if (file->isDirectory()) + { + continue; + } + + wstring levelId = file->getName(); + + LevelData *levelData = getDataTagFor(levelId); + if (levelData != NULL) + { + bool requiresConversion = levelData->getVersion() != McRegionLevelStorage::MCREGION_VERSION_ID; + wstring levelName = levelData->getLevelName(); + + if (levelName.empty()) // 4J Jev TODO: levelName can't be NULL? if (levelName == NULL || isEmpty(levelName)) + { + levelName = levelId; + } + // long size = getLevelSize(folder); + long size = 0; + levels->push_back(new LevelSummary(levelId, levelName, levelData->getLastPlayed(), size, requiresConversion, levelData->isHardcore())); + } + } +#endif + return levels; +} + +void McRegionLevelStorageSource::clearAll() +{ +} + +shared_ptr McRegionLevelStorageSource::selectLevel(ConsoleSaveFile *saveFile, const wstring& levelId, bool createPlayerDir) +{ + // return new LevelStorageProfilerDecorator(new McRegionLevelStorage(baseDir, levelId, createPlayerDir)); + return shared_ptr(new McRegionLevelStorage(saveFile, baseDir, levelId, createPlayerDir)); +} + +bool McRegionLevelStorageSource::isConvertible(ConsoleSaveFile *saveFile, const wstring& levelId) +{ + // check if there is old file format level data + LevelData *levelData = getDataTagFor(saveFile, levelId); + if (levelData == NULL || levelData->getVersion() != 0) + { + delete levelData; + return false; + } + delete levelData; + + return true; +} + +bool McRegionLevelStorageSource::requiresConversion(ConsoleSaveFile *saveFile, const wstring& levelId) +{ + LevelData *levelData = getDataTagFor(saveFile, levelId); + if (levelData == NULL || levelData->getVersion() != 0) + { + delete levelData; + return false; + } + delete levelData; + + return true; +} + +bool McRegionLevelStorageSource::convertLevel(ConsoleSaveFile *saveFile, const wstring& levelId, ProgressListener *progress) +{ + assert(false); + // I removed this while updating the saves to use the single save file + // Will we ever use this convertLevel function anyway? The main issue is the check + // for the hellFolder.exists() which would require a slight change to the way our + // save files are structured +#if 0 + progress->progressStagePercentage(0); + + vector *normalRegions = new vector; + vector *normalBaseFolders = new vector; + vector *netherRegions = new vector; + vector *netherBaseFolders = new vector; + ArrayList enderRegions = new ArrayList(); + ArrayList enderBaseFolders = new ArrayList(); + + //File baseFolder = File(baseDir, levelId); + //File netherFolder = File(baseFolder, LevelStorage::HELL_FOLDER); + //File enderFolder = new File(baseFolder, LevelStorage.ENDER_FOLDER); + ConsoleSaveFile saveFile = ConsoleSaveFile( levelId ); + + // System.out.println("Scanning folders..."); 4J Jev, TODO how do we println ? + + // find normal world + addRegions(baseFolder, normalRegions, normalBaseFolders); + + // find hell world + if (netherFolder.exists()) + { + addRegions(netherFolder, netherRegions, netherBaseFolders); + } + if (enderFolder.exists()) + { + addRegions(enderFolder, enderRegions, enderBaseFolders); + } + + int totalCount = normalRegions->size() + netherRegions->size() + enderRegions.size() + normalBaseFolders->size() + netherBaseFolders->size() + enderBaseFolders.size(); + + // System.out.println("Total conversion count is " + totalCount); 4J Jev, TODO + + // convert normal world + convertRegions(baseFolder, normalRegions, 0, totalCount, progress); + // convert hell world + convertRegions(netherFolder, netherRegions, normalRegions->size(), totalCount, progress); + // convert hell world + convertRegions(enderFolder, enderRegions, normalRegions.size() + netherRegions.size(), totalCount, progress); + + LevelData *levelData = getDataTagFor(levelId); + levelData->setVersion(McRegionLevelStorage::MCREGION_VERSION_ID); + + LevelStorage *levelStorage = selectLevel(levelId, false); + levelStorage->saveLevelData(levelData); + + // erase old files + eraseFolders(normalBaseFolders, normalRegions->size() + netherRegions->size(), totalCount, progress); + if (netherFolder.exists()) + { + eraseFolders(netherBaseFolders, normalRegions->size() + netherRegions->size() + normalBaseFolders->size(), totalCount, progress); + } +#endif + return true; +} + +#if 0 +// 4J - not required anymore +void McRegionLevelStorageSource::addRegions(File &baseFolder, vector *dest, vector *firstLevelFolders) +{ + FolderFilter folderFilter; + ChunkFilter chunkFilter; + + File *folder1; + vector *folderLevel1 = baseFolder.listFiles((FileFilter *) &folderFilter); + AUTO_VAR(itEnd, folderLevel1->end()); + for (AUTO_VAR(it, folderLevel1->begin()); it != itEnd; it++) + { + folder1 = *it; //folderLevel1->at(i1); + + // keep this for the clean-up process later on + firstLevelFolders->push_back(folder1); + + File *folder2; + vector *folderLevel2 = folder1->listFiles(&folderFilter); + AUTO_VAR(itEnd2, folderLevel2->end()); + for (AUTO_VAR(it2, folderLevel2->begin()); it2 != itEnd; it2++) + { + folder2 = *it2; //folderLevel2->at(i2); + + vector *chunkFiles = folder2->listFiles((FileFilter *) &chunkFilter); + + File *chunk; + AUTO_VAR(itEndFile, chunkFiles->end()); + for (AUTO_VAR(itFile, chunkFiles->begin()); itFile != itEndFile; itFile++) + { + chunk = *itFile; //chunkFiles->at(i3); + + dest->push_back(new ChunkFile(chunk)); + } + } + } +} +#endif + +void McRegionLevelStorageSource::convertRegions(File &baseFolder, vector *chunkFiles, int currentCount, int totalCount, ProgressListener *progress) +{ + assert( false ); + + // 4J Stu - Removed, see comment in convertLevel above +#if 0 + //Collections::sort(chunkFiles); + std::sort( chunkFiles->begin(), chunkFiles->end() ); + + byteArray buffer = byteArray(4096); + + ChunkFile *chunkFile; + AUTO_VAR(itEnd, chunkFiles->end()); + for (AUTO_VAR(it, chunkFiles->begin()); it != itEnd; it++) + { + chunkFile = *it; //chunkFiles->at(i1); + + // Matcher matcher = ChunkFilter.chunkFilePattern.matcher(chunkFile.getName()); + // if (!matcher.matches()) { + // continue; + // } + // int x = Integer.parseInt(matcher.group(1), 36); + // int z = Integer.parseInt(matcher.group(2), 36); + + int x = chunkFile->getX(); + int z = chunkFile->getZ(); + + RegionFile *region = RegionFileCache::getRegionFile(baseFolder, x, z); + if (!region->hasChunk(x & 31, z & 31)) + { + FileInputStream fis = new BufferedInputStream(FileInputStream(*chunkFile->getFile())); + DataInputStream istream = DataInputStream(&fis); // 4J - was new GZIPInputStream as well + + DataOutputStream *out = region->getChunkDataOutputStream(x & 31, z & 31); + + int length = 0; + while ( (length = istream.read(buffer)) != -1 ) + { + out->write(buffer, 0, length); + } + + out->close(); + istream.close(); + + // 4J Stu - getChunkDataOutputStream makes a new DataOutputStream that points to a new ChunkBuffer( ByteArrayOutputStream ) + // We should clean these up when we are done + out->deleteChildStream(); + delete out; + } + + currentCount++; + int percent = (int) Math::round(100.0 * (double) currentCount / (double) totalCount); + progress->progressStagePercentage(percent); + } + RegionFileCache::clear(); +#endif + +} + +void McRegionLevelStorageSource::eraseFolders(vector *folders, int currentCount, int totalCount, ProgressListener *progress) +{ + File *folder; + AUTO_VAR(itEnd, folders->end()); + for (AUTO_VAR(it, folders->begin()); it != itEnd; it++) + { + folder = *it; //folders->at(i); + + vector *files = folder->listFiles(); + deleteRecursive(files); + folder->_delete(); + + currentCount++; + int percent = (int) Math::round(100.0 * (double) currentCount / (double) totalCount); + progress->progressStagePercentage(percent); + } +} + +#if 0 +// 4J - not required anymore +bool McRegionLevelStorageSource::FolderFilter::accept(File *file) +{ + if (file->isDirectory()) + { + Matcher matcher( chunkFolderPattern, file->getName() ); + return matcher.matches(); + } + return false; +} + + +bool McRegionLevelStorageSource::ChunkFilter::accept(File *dir, const wstring& name) +{ + Matcher matcher( chunkFilePattern, name ); + return matcher.matches(); +} + + +McRegionLevelStorageSource::ChunkFile::ChunkFile(File *file) +{ + this->file = file; + + Matcher matcher( ChunkFilter::chunkFilePattern, file->getName() ); + if (matcher.matches()) + { + x = Integer::parseInt(matcher.group(1), 36); + z = Integer::parseInt(matcher.group(2), 36); + } + else + { + x = 0; + z = 0; + } +} + +//Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. +int McRegionLevelStorageSource::ChunkFile::compareTo(ChunkFile *rhs) +{ + // sort chunk files so that they are placed according to their + // region position + int rx = x >> 5; + int rhsrx = rhs->x >> 5; + if (rx == rhsrx) + { + int rz = z >> 5; + int rhsrz = rhs->z >> 5; + return rz - rhsrz; + } + + return rx - rhsrx; +} + +// 4J Stu Added so we can use std::sort instead of the java Collections::sort +// a < b +bool McRegionLevelStorageSource::ChunkFile::operator<( ChunkFile *b ) +{ + return compareTo( b ) < 0; +} + +File *McRegionLevelStorageSource::ChunkFile::getFile() +{ + return (File *) file; +} + +int McRegionLevelStorageSource::ChunkFile::getX() +{ + return x; +} + +int McRegionLevelStorageSource::ChunkFile::getZ() +{ + return z; +} +#endif \ No newline at end of file diff --git a/Minecraft.World/McRegionLevelStorageSource.h b/Minecraft.World/McRegionLevelStorageSource.h new file mode 100644 index 00000000..d1a4bf30 --- /dev/null +++ b/Minecraft.World/McRegionLevelStorageSource.h @@ -0,0 +1,68 @@ +#pragma once +using namespace std; + +#include "DirectoryLevelStorageSource.h" +#include "FileFilter.h" +#include "FilenameFilter.h" + +class ProgressListener; +class LevelStorage; + +class McRegionLevelStorageSource : public DirectoryLevelStorageSource +{ +public: + class ChunkFile; + + McRegionLevelStorageSource(File dir); + virtual wstring getName(); + virtual vector *getLevelList(); + virtual void clearAll(); + virtual shared_ptr selectLevel(ConsoleSaveFile *saveFile, const wstring& levelId, bool createPlayerDir); + virtual bool isConvertible(ConsoleSaveFile *saveFile, const wstring& levelId); + virtual bool requiresConversion(ConsoleSaveFile *saveFile, const wstring& levelId); + virtual bool convertLevel(ConsoleSaveFile *saveFile, const wstring& levelId, ProgressListener *progress); + +private: +#if 0 + // 4J - not required anymore + void addRegions(File &baseFolder, vector *dest, vector *firstLevelFolders); +#endif + void convertRegions(File &baseFolder, vector *chunkFiles, int currentCount, int totalCount, ProgressListener *progress); + void eraseFolders(vector *folders, int currentCount, int totalCount, ProgressListener *progress); + +public: +#if 0 + // 4J - not required anymore + static class FolderFilter : public FileFilter + { + public: + static const std::tr1::wregex chunkFolderPattern; // was Pattern + bool accept(File *file); + }; + + static class ChunkFilter : public FilenameFilter + { + public: + static const std::tr1::wregex chunkFilePattern; // was Pattern + bool accept(File *dir, const wstring& name); + }; + + static class ChunkFile // implements Comparable + { + private: + /* const */ File *file; + /* const */ int x; + /* const */ int z; + + public: + ChunkFile(File *file); + int compareTo(ChunkFile *rhs); + File *getFile(); + int getX(); + int getZ(); + + // a < b + bool operator<( ChunkFile *b ); + }; +#endif +}; diff --git a/Minecraft.World/MegaTreeFeature.cpp b/Minecraft.World/MegaTreeFeature.cpp new file mode 100644 index 00000000..77e06e30 --- /dev/null +++ b/Minecraft.World/MegaTreeFeature.cpp @@ -0,0 +1,201 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "MegaTreeFeature.h" + +MegaTreeFeature::MegaTreeFeature(bool doUpdate, int baseHeight, int trunkType, int leafType) : Feature(doUpdate), baseHeight(baseHeight), trunkType(trunkType), leafType(leafType) +{ +} + +bool MegaTreeFeature::place(Level *level, Random *random, int x, int y, int z) +{ + int treeHeight = random->nextInt(3) + baseHeight; + + bool free = true; + if (y < 1 || y + treeHeight + 1 > Level::maxBuildHeight) return false; + + // 4J Stu Added to stop tree features generating areas previously place by game rule generation + if(app.getLevelGenerationOptions() != NULL) + { + PIXBeginNamedEvent(0, "MegaTreeFeature Checking intersects"); + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + bool intersects = levelGenOptions->checkIntersects(x - 2, y - 1, z - 2, x + 2, y + treeHeight, z + 2); + PIXEndNamedEvent(); + if(intersects) + { + //app.DebugPrintf("Skipping reeds feature generation as it overlaps a game rule structure\n"); + return false; + } + } + + for (int yy = y; yy <= y + 1 + treeHeight; yy++) + { + int r = 2; + if (yy == y) r = 1; + if (yy >= y + 1 + treeHeight - 2) r = 2; + for (int xx = x - r; xx <= x + r && free; xx++) + { + for (int zz = z - r; zz <= z + r && free; zz++) + { + if (yy >= 0 && yy < Level::maxBuildHeight) + { + int tt = level->getTile(xx, yy, zz); + if (tt != 0 && tt != Tile::leaves_Id && tt != Tile::grass_Id && tt != Tile::dirt_Id && tt != Tile::treeTrunk_Id && tt != Tile::sapling_Id) free = false; + } + else + { + free = false; + } + } + } + } + + if (!free) return false; + + int belowTile = level->getTile(x, y - 1, z); + if ((belowTile != Tile::grass_Id && belowTile != Tile::dirt_Id) || y >= Level::maxBuildHeight - treeHeight - 1) return false; + + level->setTileNoUpdate(x, y - 1, z, Tile::dirt_Id); + level->setTileNoUpdate(x + 1, y - 1, z, Tile::dirt_Id); + level->setTileNoUpdate(x, y - 1, z + 1, Tile::dirt_Id); + level->setTileNoUpdate(x + 1, y - 1, z + 1, Tile::dirt_Id); + + PIXBeginNamedEvent(0,"MegaTree placing leaves, %d, %d, %d", x, z, y+treeHeight); + placeLeaves(level, x, z, y + treeHeight, 2, random); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"MegaTree placing branches"); + int branchHeight = y + treeHeight - 2 - random->nextInt(4); + while (branchHeight > y + treeHeight / 2) + { + float angle = random->nextFloat() * PI * 2.0f; + int bx = x + (int) (0.5f + Mth::cos(angle) * 4.0f); + int bz = z + (int) (0.5f + Mth::sin(angle) * 4.0f); + placeLeaves(level, bx, bz, branchHeight, 0, random); + + for (int b = 0; b < 5; b++) + { + bx = x + (int) (1.5f + Mth::cos(angle) * b); + bz = z + (int) (1.5f + Mth::sin(angle) * b); + placeBlock(level, bx, branchHeight - 3 + b / 2, bz, Tile::treeTrunk_Id, trunkType); + } + + branchHeight -= 2 + random->nextInt(4); + } + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0, "MegaTree placing vines"); + for (int hh = 0; hh < treeHeight; hh++) + { + int t = level->getTile(x, y + hh, z); + if (t == 0 || t == Tile::leaves_Id) + { + placeBlock(level, x, y + hh, z, Tile::treeTrunk_Id, trunkType); + if (hh > 0) + { + if (random->nextInt(3) > 0 && level->isEmptyTile(x - 1, y + hh, z)) + { + placeBlock(level, x - 1, y + hh, z, Tile::vine_Id, VineTile::VINE_EAST); + } + if (random->nextInt(3) > 0 && level->isEmptyTile(x, y + hh, z - 1)) + { + placeBlock(level, x, y + hh, z - 1, Tile::vine_Id, VineTile::VINE_SOUTH); + } + } + } + if (hh < (treeHeight - 1)) + { + t = level->getTile(x + 1, y + hh, z); + if (t == 0 || t == Tile::leaves_Id) + { + placeBlock(level, x + 1, y + hh, z, Tile::treeTrunk_Id, trunkType); + if (hh > 0) + { + if (random->nextInt(3) > 0 && level->isEmptyTile(x + 2, y + hh, z)) + { + placeBlock(level, x + 2, y + hh, z, Tile::vine_Id, VineTile::VINE_WEST); + } + if (random->nextInt(3) > 0 && level->isEmptyTile(x + 1, y + hh, z - 1)) + { + placeBlock(level, x + 1, y + hh, z - 1, Tile::vine_Id, VineTile::VINE_SOUTH); + } + } + } + t = level->getTile(x + 1, y + hh, z + 1); + if (t == 0 || t == Tile::leaves_Id) + { + placeBlock(level, x + 1, y + hh, z + 1, Tile::treeTrunk_Id, trunkType); + if (hh > 0) + { + if (random->nextInt(3) > 0 && level->isEmptyTile(x + 2, y + hh, z + 1)) + { + placeBlock(level, x + 2, y + hh, z + 1, Tile::vine_Id, VineTile::VINE_WEST); + } + if (random->nextInt(3) > 0 && level->isEmptyTile(x + 1, y + hh, z + 2)) + { + placeBlock(level, x + 1, y + hh, z + 2, Tile::vine_Id, VineTile::VINE_NORTH); + } + } + } + t = level->getTile(x, y + hh, z + 1); + if (t == 0 || t == Tile::leaves_Id) + { + placeBlock(level, x, y + hh, z + 1, Tile::treeTrunk_Id, trunkType); + if (hh > 0) + { + if (random->nextInt(3) > 0 && level->isEmptyTile(x - 1, y + hh, z + 1)) + { + placeBlock(level, x - 1, y + hh, z + 1, Tile::vine_Id, VineTile::VINE_EAST); + } + if (random->nextInt(3) > 0 && level->isEmptyTile(x, y + hh, z + 2)) + { + placeBlock(level, x, y + hh, z + 2, Tile::vine_Id, VineTile::VINE_NORTH); + } + } + } + } + } + PIXEndNamedEvent(); + + return true; +} + +void MegaTreeFeature::placeLeaves(Level *level, int x, int z, int topPosition, int baseRadius, Random *random) +{ + int grassHeight = 2; + // 4J Stu - Generate from top down so that we don't have to keep adjusting the heightmaps + for (int yy = topPosition; yy >= topPosition - grassHeight; yy--) + { + int yo = yy - (topPosition); + int radius = baseRadius + 1 - yo; + for (int xx = x - radius; xx <= x + radius + 1; xx++) + { + int xo = xx - (x); + for (int zz = z - radius; zz <= z + radius + 1; zz++) + { + int zo = zz - (z); + if ((xo < 0 && zo < 0) && (xo * xo + zo * zo) > (radius * radius)) + { + continue; + } + if ((xo > 0 || zo > 0) && (xo * xo + zo * zo) > ((radius + 1) * (radius + 1))) + { + continue; + } + if (random->nextInt(4) == 0 && (xo * xo + zo * zo) > ((radius - 1) * (radius - 1))) + { + continue; + } + PIXBeginNamedEvent(0,"Getting tile"); + int t = level->getTile(xx, yy, zz); + PIXEndNamedEvent(); + if (!Tile::solid[t]) + { + PIXBeginNamedEvent(0,"Placing block"); + placeBlock(level, xx, yy, zz, Tile::leaves_Id, leafType); + PIXEndNamedEvent(); + } + } + } + } +} \ No newline at end of file diff --git a/Minecraft.World/MegaTreeFeature.h b/Minecraft.World/MegaTreeFeature.h new file mode 100644 index 00000000..9d02a4cd --- /dev/null +++ b/Minecraft.World/MegaTreeFeature.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Feature.h" + +class MegaTreeFeature : public Feature +{ +private: + const int baseHeight; + const int trunkType; + const int leafType; + +public: + MegaTreeFeature(bool doUpdate, int baseHeight, int trunkType, int leafType); + + bool place(Level *level, Random *random, int x, int y, int z); + +private: + void placeLeaves(Level *level, int x, int z, int topPosition, int baseRadius, Random *random); +}; \ No newline at end of file diff --git a/Minecraft.World/MeleeAttackGoal.cpp b/Minecraft.World/MeleeAttackGoal.cpp new file mode 100644 index 00000000..f3aa6455 --- /dev/null +++ b/Minecraft.World/MeleeAttackGoal.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.sensing.h" +#include "net.minecraft.world.phys.h" +#include "MeleeAttackGoal.h" + +void MeleeAttackGoal::_init(Mob *mob, float speed, bool trackTarget) +{ + this->attackType = eTYPE_NOTSET; + this->mob = mob; + this->level = mob->level; + this->speed = speed; + this->trackTarget = trackTarget; + setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag); + + + attackTime = 0; + path = NULL; + timeToRecalcPath = 0; +} + +MeleeAttackGoal::MeleeAttackGoal(Mob *mob, eINSTANCEOF attackType, float speed, bool trackTarget) +{ + _init(mob, speed, trackTarget); + this->attackType = attackType; +} + +MeleeAttackGoal::MeleeAttackGoal(Mob *mob, float speed, bool trackTarget) +{ + _init(mob,speed,trackTarget); +} + +MeleeAttackGoal::~MeleeAttackGoal() +{ + if(path != NULL) delete path; +} + +bool MeleeAttackGoal::canUse() +{ + shared_ptr bestTarget = mob->getTarget(); + if (bestTarget == NULL) return false; + if(!bestTarget->isAlive()) return false; + if (attackType != eTYPE_NOTSET && (attackType & bestTarget->GetType()) != attackType) return false; + target = weak_ptr(bestTarget); + delete path; + path = mob->getNavigation()->createPath(target.lock()); + return path != NULL; +} + +bool MeleeAttackGoal::canContinueToUse() +{ + shared_ptr bestTarget = mob->getTarget(); + if (bestTarget == NULL) return false; + if (target.lock() == NULL || !target.lock()->isAlive()) return false; + if (!trackTarget) return !mob->getNavigation()->isDone(); + if (!mob->isWithinRestriction(Mth::floor(target.lock()->x), Mth::floor(target.lock()->y), Mth::floor(target.lock()->z))) return false; + return true; +} + +void MeleeAttackGoal::start() +{ + mob->getNavigation()->moveTo(path, speed); + path = NULL; + timeToRecalcPath = 0; +} + +void MeleeAttackGoal::stop() +{ + target = weak_ptr(); + mob->getNavigation()->stop(); +} + +void MeleeAttackGoal::tick() +{ + mob->getLookControl()->setLookAt(target.lock(), 30, 30); + if (trackTarget || mob->getSensing()->canSee(target.lock())) + { + if (--timeToRecalcPath <= 0) + { + timeToRecalcPath = 4 + mob->getRandom()->nextInt(7); + mob->getNavigation()->moveTo(target.lock(), speed); + } + } + + attackTime = max(attackTime - 1, 0); + + double meleeRadiusSqr = (mob->bbWidth * 2) * (mob->bbWidth * 2); + if (mob->distanceToSqr(target.lock()->x, target.lock()->bb->y0, target.lock()->z) > meleeRadiusSqr) return; + if (attackTime > 0) return; + attackTime = 20; + mob->doHurtTarget(target.lock()); +} diff --git a/Minecraft.World/MeleeAttackGoal.h b/Minecraft.World/MeleeAttackGoal.h new file mode 100644 index 00000000..c8f5e19d --- /dev/null +++ b/Minecraft.World/MeleeAttackGoal.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Goal.h" + +class Level; +class Mob; +class Path; + +class MeleeAttackGoal : public Goal +{ +private: + Level *level; + Mob *mob; // Owner of this goal + weak_ptr target; + + int attackTime; + float speed; + bool trackTarget; + Path *path; + eINSTANCEOF attackType; + int timeToRecalcPath; + + void _init(Mob *mob, float speed, bool trackTarget); + +public: + MeleeAttackGoal(Mob *mob, eINSTANCEOF attackType, float speed, bool trackTarget); + MeleeAttackGoal(Mob *mob, float speed, bool trackTarget); + ~MeleeAttackGoal(); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + virtual void stop(); + virtual void tick(); + + // 4J Added override to update ai elements when loading entity from schematics + virtual void setLevel(Level *level) { this->level = level; } +}; \ No newline at end of file diff --git a/Minecraft.World/MelonTile.cpp b/Minecraft.World/MelonTile.cpp new file mode 100644 index 00000000..881eadc2 --- /dev/null +++ b/Minecraft.World/MelonTile.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include "MelonTile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.h" +#include "Facing.h" + +const wstring MelonTile::TEX = L"melon_side"; +const wstring MelonTile::TEX_TOP = L"melon_top"; + +MelonTile::MelonTile(int id) : Tile(id, Material::vegetable) +{ + iconTop = NULL; +} + +Icon *MelonTile::getTexture(int face, int data) +{ + if (face == Facing::UP || face == Facing::DOWN) return iconTop; + return icon; +} + +int MelonTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::melon->id; +} + +int MelonTile::getResourceCount(Random *random) +{ + return 3 + random->nextInt(5); +} + +int MelonTile::getResourceCountForLootBonus(int bonusLevel, Random *random) +{ + int total = getResourceCount(random) + random->nextInt(1 + bonusLevel); + if (total > 9) + { + total = 9; + } + return total; +} + +void MelonTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(TEX); + iconTop = iconRegister->registerIcon(TEX_TOP); +} \ No newline at end of file diff --git a/Minecraft.World/MelonTile.h b/Minecraft.World/MelonTile.h new file mode 100644 index 00000000..029cb1cb --- /dev/null +++ b/Minecraft.World/MelonTile.h @@ -0,0 +1,24 @@ +#pragma once +#include "Tile.h" + +class ChunkRebuildData; +class MelonTile : public Tile +{ + friend class ChunkRebuildData; +private: + static const wstring TEX; + static const wstring TEX_TOP; + + Icon *iconTop; + + // 4J Stu - I don't know why this is protected in Java +//protected: +public: + MelonTile(int id); +public: + virtual Icon *getTexture(int face, int data); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int getResourceCount(Random *random); + virtual int getResourceCountForLootBonus(int bonusLevel, Random *random); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/MemoryChunkStorage.cpp b/Minecraft.World/MemoryChunkStorage.cpp new file mode 100644 index 00000000..249efdd9 --- /dev/null +++ b/Minecraft.World/MemoryChunkStorage.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.chunk.h" +#include "MemoryChunkStorage.h" + +LevelChunk *MemoryChunkStorage::load(Level *level, int x, int z) //throws IOException +{ + return NULL; +} + +void MemoryChunkStorage::save(Level *level, LevelChunk *levelChunk) //throws IOException +{ +} + +void MemoryChunkStorage::saveEntities(Level *level, LevelChunk *levelChunk) //throws IOException +{ +} + +void MemoryChunkStorage::tick() +{ +} + +void MemoryChunkStorage::flush() +{ +} \ No newline at end of file diff --git a/Minecraft.World/MemoryChunkStorage.h b/Minecraft.World/MemoryChunkStorage.h new file mode 100644 index 00000000..ca32d0ac --- /dev/null +++ b/Minecraft.World/MemoryChunkStorage.h @@ -0,0 +1,14 @@ +#pragma once +using namespace std; + +#include "ChunkSource.h" + +class MemoryChunkStorage : public ChunkStorage +{ +public: + virtual LevelChunk *load(Level *level, int x, int z); + virtual void save(Level *level, LevelChunk *levelChunk); + virtual void saveEntities(Level *level, LevelChunk *levelChunk); + virtual void tick(); + virtual void flush(); +}; \ No newline at end of file diff --git a/Minecraft.World/MemoryLevelStorage.cpp b/Minecraft.World/MemoryLevelStorage.cpp new file mode 100644 index 00000000..5a1d43bb --- /dev/null +++ b/Minecraft.World/MemoryLevelStorage.cpp @@ -0,0 +1,63 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.chunk.storage.h" +#include "net.minecraft.world.level.dimension.h" +#include "com.mojang.nbt.h" +#include "MemoryLevelStorage.h" + +#include "ConsoleSaveFileIO.h" + +MemoryLevelStorage::MemoryLevelStorage() +{ +} + +LevelData *MemoryLevelStorage::prepareLevel() +{ + return NULL; +} + +void MemoryLevelStorage::checkSession() +{ +} + +ChunkStorage *MemoryLevelStorage::createChunkStorage(Dimension *dimension) +{ + return new MemoryChunkStorage(); +} + +void MemoryLevelStorage::saveLevelData(LevelData *levelData, vector > *players) +{ +} + +void MemoryLevelStorage::saveLevelData(LevelData *levelData) +{ +} + +PlayerIO *MemoryLevelStorage::getPlayerIO() +{ + return this; +} + +void MemoryLevelStorage::closeAll() +{ +} + +void MemoryLevelStorage::save(shared_ptr player) +{ +} + +bool MemoryLevelStorage::load(shared_ptr player) +{ + return false; +} + +CompoundTag *MemoryLevelStorage::loadPlayerDataTag(const wstring& playerName) +{ + return NULL; +} + +ConsoleSavePath MemoryLevelStorage::getDataFile(const wstring& id) +{ + return ConsoleSaveFile(wstring(L"")); +} \ No newline at end of file diff --git a/Minecraft.World/MemoryLevelStorage.h b/Minecraft.World/MemoryLevelStorage.h new file mode 100644 index 00000000..21ea784c --- /dev/null +++ b/Minecraft.World/MemoryLevelStorage.h @@ -0,0 +1,31 @@ +#pragma once +using namespace std; + +#include "LevelStorage.h" +#include "PlayerIO.h" + +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.chunk.storage.h" +#include "net.minecraft.world.level.dimension.h" +#include "com.mojang.nbt.h" + +#include "ConsoleSaveFile.h" + +class MemoryLevelStorage : public LevelStorage, public PlayerIO +{ +public: + MemoryLevelStorage(); + virtual LevelData *prepareLevel(); + virtual void checkSession(); + virtual ChunkStorage *createChunkStorage(Dimension *dimension); + virtual void saveLevelData(LevelData *levelData, vector > *players); + virtual void saveLevelData(LevelData *levelData); + virtual PlayerIO *getPlayerIO(); + virtual void closeAll(); + virtual void save(shared_ptr player); + virtual bool load(shared_ptr player); + virtual CompoundTag *loadPlayerDataTag(const wstring& playerName); + virtual ConsoleSavePath getDataFile(const wstring& id); +}; \ No newline at end of file diff --git a/Minecraft.World/MemoryLevelStorageSource.cpp b/Minecraft.World/MemoryLevelStorageSource.cpp new file mode 100644 index 00000000..db0cfa58 --- /dev/null +++ b/Minecraft.World/MemoryLevelStorageSource.cpp @@ -0,0 +1,61 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.chunk.storage.h" +#include "MemoryLevelStorage.h" +#include "LevelSummary.h" +#include "MemoryLevelStorageSource.h" + +MemoryLevelStorageSource::MemoryLevelStorageSource() +{ +} + +wstring MemoryLevelStorageSource::getName() +{ + return L"Memory Storage"; +} + +shared_ptr MemoryLevelStorageSource::selectLevel(const wstring& levelId, bool createPlayerDir) +{ + return shared_ptr () new MemoryLevelStorage()); +} + +vector *MemoryLevelStorageSource::getLevelList() +{ + return new vector; +} + +void MemoryLevelStorageSource::clearAll() +{ +} + +LevelData *MemoryLevelStorageSource::getDataTagFor(const wstring& levelId) +{ + return NULL; +} + +bool MemoryLevelStorageSource::isNewLevelIdAcceptable(const wstring& levelId) +{ + return true; +} + +void MemoryLevelStorageSource::deleteLevel(const wstring& levelId) +{ +} + +void MemoryLevelStorageSource::renameLevel(const wstring& levelId, const wstring& newLevelName) +{ +} + +bool MemoryLevelStorageSource::isConvertible(const wstring& levelId) +{ + return false; +} + +bool MemoryLevelStorageSource::requiresConversion(const wstring& levelId) +{ + return false; +} + +bool MemoryLevelStorageSource::convertLevel(const wstring& levelId, ProgressListener *progress) +{ + return false; +} \ No newline at end of file diff --git a/Minecraft.World/MemoryLevelStorageSource.h b/Minecraft.World/MemoryLevelStorageSource.h new file mode 100644 index 00000000..c53c0463 --- /dev/null +++ b/Minecraft.World/MemoryLevelStorageSource.h @@ -0,0 +1,21 @@ +#pragma once +using namespace std; + +#include "LevelStorageSource.h" + +class MemoryLevelStorageSource : public LevelStorageSource +{ +public: + MemoryLevelStorageSource(); + wstring getName(); + shared_ptr selectLevel(const wstring& levelId, bool createPlayerDir); + vector *getLevelList(); + void clearAll(); + LevelData *getDataTagFor(const wstring& levelId); + bool isNewLevelIdAcceptable(const wstring& levelId); + void deleteLevel(const wstring& levelId); + void renameLevel(const wstring& levelId, const wstring& newLevelName); + bool isConvertible(const wstring& levelId); + bool requiresConversion(const wstring& levelId); + bool convertLevel(const wstring& levelId, ProgressListener *progress); +}; \ No newline at end of file diff --git a/Minecraft.World/MenuBackup.cpp b/Minecraft.World/MenuBackup.cpp new file mode 100644 index 00000000..6d09f3e3 --- /dev/null +++ b/Minecraft.World/MenuBackup.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.player.h" +#include "AbstractContainerMenu.h" +#include "Slot.h" +#include "MenuBackup.h" + +MenuBackup::MenuBackup(shared_ptr inventory, AbstractContainerMenu *menu) +{ + backups = new unordered_map(); + + this->inventory = inventory; + this->menu = menu; +} + +void MenuBackup::save(short changeUid) +{ + ItemInstanceArray *backup = new ItemInstanceArray( (int)menu->slots->size() + 1 ); + (*backup)[0] = ItemInstance::clone(inventory->getCarried()); + for (unsigned int i = 0; i < menu->slots->size(); i++) + { + (*backup)[i + 1] = ItemInstance::clone(menu->slots->at(i)->getItem()); + } + // TODO Is unordered_map use correct? + // Was backups.put(changeUid, backup); + (*backups)[changeUid] = backup; +} + +// Cannot use delete as function name as it is a reserved keyword +void MenuBackup::deleteBackup(short changeUid) +{ + // TODO Is the unordered_map use correct? + // 4J Was backups.remove(changeUid); + backups->erase(changeUid); +} + +void MenuBackup::rollback(short changeUid) +{ + ItemInstanceArray *backup = backups->at(changeUid); + backups->clear(); + inventory->setCarried( (*backup)[0] ); + for (unsigned int i = 0; i < menu->slots->size(); i++) + { + menu->slots->at(i)->set( (*backup)[i + 1] ); + } + +} \ No newline at end of file diff --git a/Minecraft.World/MenuBackup.h b/Minecraft.World/MenuBackup.h new file mode 100644 index 00000000..2de750b1 --- /dev/null +++ b/Minecraft.World/MenuBackup.h @@ -0,0 +1,21 @@ +#pragma once + +class AbstractContainerMenu; +class Inventory; + +class MenuBackup +{ +private: + unordered_map *backups; + shared_ptr inventory; + AbstractContainerMenu *menu; + +public: + MenuBackup(shared_ptr inventory, AbstractContainerMenu *menu); + + void save(short changeUid); + + // Cannot use delete as function name as it is a reserved keyword + void deleteBackup(short changeUid); + void rollback(short changeUid); +}; \ No newline at end of file diff --git a/Minecraft.World/Merchant.h b/Minecraft.World/Merchant.h new file mode 100644 index 00000000..8b4b7a69 --- /dev/null +++ b/Minecraft.World/Merchant.h @@ -0,0 +1,17 @@ +#pragma once + +class MerchantRecipeList; +class MerchantRecipe; +class Player; + +class Merchant +{ +public: + virtual void setTradingPlayer(shared_ptr player) = 0; + virtual shared_ptr getTradingPlayer() = 0; + virtual MerchantRecipeList *getOffers(shared_ptr forPlayer) = 0; + virtual void overrideOffers(MerchantRecipeList *recipeList) = 0; + virtual void notifyTrade(MerchantRecipe *activeRecipe) = 0; + virtual void notifyTradeUpdated(shared_ptr item) = 0; + virtual int getDisplayName() = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/MerchantContainer.cpp b/Minecraft.World/MerchantContainer.cpp new file mode 100644 index 00000000..cadbe4e3 --- /dev/null +++ b/Minecraft.World/MerchantContainer.cpp @@ -0,0 +1,183 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.trading.h" +#include "MerchantMenu.h" +#include "MerchantContainer.h" + +MerchantContainer::MerchantContainer(shared_ptr player, shared_ptr villager) +{ + this->player = player; + merchant = villager; + items = ItemInstanceArray(3); + items[0] = nullptr; + items[1] = nullptr; + items[2] = nullptr; + activeRecipe = NULL; + selectionHint = 0; +} + +MerchantContainer::~MerchantContainer() +{ + delete [] items.data; +} + +unsigned int MerchantContainer::getContainerSize() +{ + return items.length; +} + +shared_ptr MerchantContainer::getItem(unsigned int slot) +{ + return items[slot]; +} + +shared_ptr MerchantContainer::removeItem(unsigned int slot, int count) +{ + if (items[slot] != NULL) + { + if (slot == MerchantMenu::RESULT_SLOT) + { + shared_ptr item = items[slot]; + items[slot] = nullptr; + return item; + } + if (items[slot]->count <= count) + { + shared_ptr item = items[slot]; + items[slot] = nullptr; + if (isPaymentSlot(slot)) + { + updateSellItem(); + } + return item; + } + else + { + shared_ptr i = items[slot]->remove(count); + if (items[slot]->count == 0) items[slot] = nullptr; + if (isPaymentSlot(slot)) + { + updateSellItem(); + } + return i; + } + } + return nullptr; +} + +bool MerchantContainer::isPaymentSlot(int slot) +{ + return slot == MerchantMenu::PAYMENT1_SLOT || slot == MerchantMenu::PAYMENT2_SLOT; +} + +shared_ptr MerchantContainer::removeItemNoUpdate(int slot) +{ + if (items[slot] != NULL) + { + shared_ptr item = items[slot]; + items[slot] = nullptr; + return item; + } + return nullptr; +} + +void MerchantContainer::setItem(unsigned int slot, shared_ptr item) +{ + items[slot] = item; + if (item != NULL && item->count > getMaxStackSize()) item->count = getMaxStackSize(); + if (isPaymentSlot(slot)) + { + updateSellItem(); + } +} + +int MerchantContainer::getName() +{ + return merchant->getDisplayName(); +} + +int MerchantContainer::getMaxStackSize() +{ + return Container::LARGE_MAX_STACK_SIZE; +} + +bool MerchantContainer::stillValid(shared_ptr player) +{ + return merchant->getTradingPlayer() == player; +} + +void MerchantContainer::startOpen() +{ +} + +void MerchantContainer::stopOpen() +{ +} + +void MerchantContainer::setChanged() +{ + updateSellItem(); +} + +void MerchantContainer::updateSellItem() +{ + activeRecipe = NULL; + + shared_ptr buyItem1 = items[MerchantMenu::PAYMENT1_SLOT]; + shared_ptr buyItem2 = items[MerchantMenu::PAYMENT2_SLOT]; + + if (buyItem1 == NULL) + { + buyItem1 = buyItem2; + buyItem2 = nullptr; + } + + if (buyItem1 == NULL) + { + setItem(MerchantMenu::RESULT_SLOT, nullptr); + } + else + { + MerchantRecipeList *offers = merchant->getOffers(player); + if (offers != NULL) + { + MerchantRecipe *recipeFor = offers->getRecipeFor(buyItem1, buyItem2, selectionHint); + if (recipeFor != NULL && !recipeFor->isDeprecated()) + { + activeRecipe = recipeFor; + setItem(MerchantMenu::RESULT_SLOT, recipeFor->getSellItem()->copy()); + } + else if (buyItem2 != NULL) + { + // try to switch + recipeFor = offers->getRecipeFor(buyItem2, buyItem1, selectionHint); + if (recipeFor != NULL && !recipeFor->isDeprecated()) + { + activeRecipe = recipeFor; + setItem(MerchantMenu::RESULT_SLOT, recipeFor->getSellItem()->copy()); + } + else + { + setItem(MerchantMenu::RESULT_SLOT, nullptr); + } + + } + else + { + setItem(MerchantMenu::RESULT_SLOT, nullptr); + } + } + } + + merchant->notifyTradeUpdated(getItem(MerchantMenu::RESULT_SLOT)); +} + +MerchantRecipe *MerchantContainer::getActiveRecipe() +{ + return activeRecipe; +} + +void MerchantContainer::setSelectionHint(int selectionHint) +{ + this->selectionHint = selectionHint; + updateSellItem(); +} \ No newline at end of file diff --git a/Minecraft.World/MerchantContainer.h b/Minecraft.World/MerchantContainer.h new file mode 100644 index 00000000..efba13d6 --- /dev/null +++ b/Minecraft.World/MerchantContainer.h @@ -0,0 +1,42 @@ +#pragma once + +#include "Container.h" +#include "ArrayWithLength.h" + +class Merchant; +class Player; +class MerchantRecipe; + +class MerchantContainer : public Container +{ +private: + shared_ptr merchant; + ItemInstanceArray items; + shared_ptr player; + MerchantRecipe *activeRecipe; + int selectionHint; + +public: + MerchantContainer(shared_ptr player, shared_ptr villager); + ~MerchantContainer(); + + unsigned int getContainerSize(); + shared_ptr getItem(unsigned int slot); + shared_ptr removeItem(unsigned int slot, int count); + +private: + bool isPaymentSlot(int slot); + +public: + shared_ptr removeItemNoUpdate(int slot); + void setItem(unsigned int slot, shared_ptr item); + int getName(); + int getMaxStackSize(); + bool stillValid(shared_ptr player); + void startOpen(); + void stopOpen(); + void setChanged(); + void updateSellItem(); + MerchantRecipe *getActiveRecipe(); + void setSelectionHint(int selectionHint); +}; \ No newline at end of file diff --git a/Minecraft.World/MerchantMenu.cpp b/Minecraft.World/MerchantMenu.cpp new file mode 100644 index 00000000..a6dfca1f --- /dev/null +++ b/Minecraft.World/MerchantMenu.cpp @@ -0,0 +1,150 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.inventory.h" +#include "net.minecraft.world.item.trading.h" +#include "net.minecraft.world.level.h" +#include "MerchantMenu.h" + +MerchantMenu::MerchantMenu(shared_ptr inventory, shared_ptr merchant, Level *level) +{ + trader = merchant; + this->level = level; + + tradeContainer = shared_ptr( new MerchantContainer(dynamic_pointer_cast(inventory->player->shared_from_this()), merchant) ); + addSlot(new Slot(tradeContainer, PAYMENT1_SLOT, SELLSLOT1_X, ROW2_Y)); + addSlot(new Slot(tradeContainer, PAYMENT2_SLOT, SELLSLOT2_X, ROW2_Y)); + addSlot(new MerchantResultSlot(inventory->player, merchant, tradeContainer, RESULT_SLOT, BUYSLOT_X, ROW2_Y)); + + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x + y * 9 + 9, 8 + x * 18, 84 + y * 18)); + } + } + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x, 8 + x * 18, 142)); + } +} + +shared_ptr MerchantMenu::getTradeContainer() +{ + return tradeContainer; +} + +void MerchantMenu::addSlotListener(ContainerListener *listener) +{ + AbstractContainerMenu::addSlotListener(listener); +} + +void MerchantMenu::broadcastChanges() +{ + AbstractContainerMenu::broadcastChanges(); +} + +// 4J used to take a shared_ptr but wasn't using it, so removed to simplify things +void MerchantMenu::slotsChanged() +{ + tradeContainer->updateSellItem(); + AbstractContainerMenu::slotsChanged(); +} + +void MerchantMenu::setSelectionHint(int hint) +{ + tradeContainer->setSelectionHint(hint); +} + +void MerchantMenu::setData(int id, int value) +{ +} + +bool MerchantMenu::stillValid(shared_ptr player) +{ + return trader->getTradingPlayer() == player; +} + +shared_ptr MerchantMenu::quickMoveStack(shared_ptr player, int slotIndex) +{ + shared_ptr clicked = nullptr; + Slot *slot = NULL; + + if(slotIndex < slots->size()) slot = slots->at(slotIndex); + if (slot != NULL && slot->hasItem()) + { + shared_ptr stack = slot->getItem(); + clicked = stack->copy(); + + if (slotIndex == RESULT_SLOT) + { + if (!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, true)) + { + return nullptr; + } + slot->onQuickCraft(stack, clicked); + } + else if (slotIndex == PAYMENT1_SLOT || slotIndex == PAYMENT2_SLOT) + { + if (!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + } + else if (slotIndex >= INV_SLOT_START && slotIndex < INV_SLOT_END) + { + if (!moveItemStackTo(stack, USE_ROW_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + } + else if (slotIndex >= USE_ROW_SLOT_START && slotIndex < USE_ROW_SLOT_END) + { + if (!moveItemStackTo(stack, INV_SLOT_START, INV_SLOT_END, false)) + { + return nullptr; + } + } + if (stack->count == 0) + { + slot->set(nullptr); + } + else + { + slot->setChanged(); + } + if (stack->count == clicked->count) + { + return nullptr; + } + else + { + slot->onTake(player, stack); + } + } + return clicked; +} + +void MerchantMenu::removed(shared_ptr player) +{ + AbstractContainerMenu::removed(player); + trader->setTradingPlayer(nullptr); + + AbstractContainerMenu::removed(player); + if (level->isClientSide) return; + + shared_ptr item = tradeContainer->removeItemNoUpdate(PAYMENT1_SLOT); + if (item) + { + player->drop(item); + } + item = tradeContainer->removeItemNoUpdate(PAYMENT2_SLOT); + if (item != NULL) + { + player->drop(item); + } +} + +shared_ptr MerchantMenu::getMerchant() +{ + return trader; +} \ No newline at end of file diff --git a/Minecraft.World/MerchantMenu.h b/Minecraft.World/MerchantMenu.h new file mode 100644 index 00000000..14a5fa1d --- /dev/null +++ b/Minecraft.World/MerchantMenu.h @@ -0,0 +1,46 @@ +#pragma once + +#include "AbstractContainerMenu.h" + +class MerchantContainer; + +class MerchantMenu : public AbstractContainerMenu +{ +public: + static const int PAYMENT1_SLOT = 0; + static const int PAYMENT2_SLOT = 1; + static const int RESULT_SLOT = 2; + + static const int INV_SLOT_START = RESULT_SLOT + 1; + static const int INV_SLOT_END = INV_SLOT_START + 9 * 3; + static const int USE_ROW_SLOT_START = INV_SLOT_END; + static const int USE_ROW_SLOT_END = USE_ROW_SLOT_START + 9; + + static const int SELLSLOT1_X = 36; + static const int SELLSLOT2_X = SELLSLOT1_X + 26; + static const int BUYSLOT_X = 120; + + static const int ROW1_Y = 24; + static const int ROW2_Y = 53; + + +private: + shared_ptr trader; + shared_ptr tradeContainer; + Level *level; + +public: + MerchantMenu(shared_ptr inventory, shared_ptr merchant, Level *level); + + shared_ptr getTradeContainer(); + void addSlotListener(ContainerListener *listener); + void broadcastChanges(); + void slotsChanged(); // 4J used to take a shared_ptr but wasn't using it, so removed to simplify things + void setSelectionHint(int hint); + void setData(int id, int value); + bool stillValid(shared_ptr player); + shared_ptr quickMoveStack(shared_ptr player, int slotIndex); + void removed(shared_ptr player); + + shared_ptr getMerchant(); // 4J Added +}; \ No newline at end of file diff --git a/Minecraft.World/MerchantRecipe.cpp b/Minecraft.World/MerchantRecipe.cpp new file mode 100644 index 00000000..b772b0c7 --- /dev/null +++ b/Minecraft.World/MerchantRecipe.cpp @@ -0,0 +1,146 @@ +#include "stdafx.h" + +#include "MerchantRecipe.h" + +void MerchantRecipe::_init(shared_ptr buyA, shared_ptr buyB, shared_ptr sell) +{ + this->buyA = buyA; + this->buyB = buyB; + this->sell = sell; + uses = 0; + maxUses = 7; +} + +MerchantRecipe::MerchantRecipe(CompoundTag *tag) +{ + buyA = nullptr; + buyB = nullptr; + sell = nullptr; + uses = 0; + load(tag); +} + +MerchantRecipe::MerchantRecipe(shared_ptr buyA, shared_ptr buyB, shared_ptr sell, int uses, int maxUses) +{ + _init(buyA, buyB, sell); + this->uses = uses; + this->maxUses = maxUses; +} + +MerchantRecipe::MerchantRecipe(shared_ptr buy, shared_ptr sell) +{ + _init(buy, nullptr, sell); +} + +MerchantRecipe::MerchantRecipe(shared_ptr buy, Item *sell) +{ + _init(buy, nullptr, shared_ptr(new ItemInstance(sell))); +} + +MerchantRecipe::MerchantRecipe(shared_ptr buy, Tile *sell) +{ + _init(buy, nullptr, shared_ptr(new ItemInstance(sell))); +} + +shared_ptr MerchantRecipe::getBuyAItem() +{ + return buyA; +} + +shared_ptr MerchantRecipe::getBuyBItem() +{ + return buyB; +} + +bool MerchantRecipe::hasSecondaryBuyItem() +{ + return buyB != NULL; +} + +shared_ptr MerchantRecipe::getSellItem() +{ + return sell; +} + +bool MerchantRecipe::isSame(MerchantRecipe *other) +{ + if (buyA->id != other->buyA->id || sell->id != other->sell->id) + { + return false; + } + return (buyB == NULL && other->buyB == NULL) || (buyB != NULL && other->buyB != NULL && buyB->id == other->buyB->id); +} + +bool MerchantRecipe::isSameSameButBetter(MerchantRecipe *other) +{ + // same deal, but cheaper + return isSame(other) && (buyA->count < other->buyA->count || (buyB != NULL && buyB->count < other->buyB->count)); +} + +int MerchantRecipe::getUses() +{ + return uses; +} + +int MerchantRecipe::getMaxUses() +{ + return maxUses; +} + +void MerchantRecipe::increaseUses() +{ + uses++; +} + +void MerchantRecipe::increaseMaxUses(int amount) +{ + maxUses += amount; +} + +bool MerchantRecipe::isDeprecated() +{ + return uses >= maxUses; +} + +void MerchantRecipe::enforceDeprecated() +{ + uses = maxUses; +} + +void MerchantRecipe::load(CompoundTag *tag) +{ + CompoundTag *buyTag = tag->getCompound(L"buy"); + buyA = ItemInstance::fromTag(buyTag); + CompoundTag *sellTag = tag->getCompound(L"sell"); + sell = ItemInstance::fromTag(sellTag); + if (tag->contains(L"buyB")) + { + buyB = ItemInstance::fromTag(tag->getCompound(L"buyB")); + } + if (tag->contains(L"uses")) + { + uses = tag->getInt(L"uses"); + } + if (tag->contains(L"maxUses")) + { + maxUses = tag->getInt(L"maxUses"); + } + else + { + maxUses = 7; + } +} + +CompoundTag *MerchantRecipe::createTag() +{ + CompoundTag *tag = new CompoundTag(); + tag->putCompound(L"buy", buyA->save(new CompoundTag(L"buy"))); + tag->putCompound(L"sell", sell->save(new CompoundTag(L"sell"))); + if (buyB != NULL) + { + tag->putCompound(L"buyB", buyB->save(new CompoundTag(L"buyB"))); + } + tag->putInt(L"uses", uses); + tag->putInt(L"maxUses", maxUses); + return tag; +} \ No newline at end of file diff --git a/Minecraft.World/MerchantRecipe.h b/Minecraft.World/MerchantRecipe.h new file mode 100644 index 00000000..0ed84898 --- /dev/null +++ b/Minecraft.World/MerchantRecipe.h @@ -0,0 +1,35 @@ +#pragma once + +class MerchantRecipe +{ +private: + shared_ptr buyA; + shared_ptr buyB; + shared_ptr sell; + int uses; + int maxUses; + + void _init(shared_ptr buyA, shared_ptr buyB, shared_ptr sell); + +public: + MerchantRecipe(CompoundTag *tag); + MerchantRecipe(shared_ptr buyA, shared_ptr buyB, shared_ptr sell, int uses = 0, int maxUses = 7); + MerchantRecipe(shared_ptr buy, shared_ptr sell); + MerchantRecipe(shared_ptr buy, Item *sell); + MerchantRecipe(shared_ptr buy, Tile *sell); + + shared_ptr getBuyAItem(); + shared_ptr getBuyBItem(); + bool hasSecondaryBuyItem(); + shared_ptr getSellItem(); + bool isSame(MerchantRecipe *other); + bool isSameSameButBetter(MerchantRecipe *other); + int getUses(); + int getMaxUses(); + void increaseUses(); + void increaseMaxUses(int amount); + bool isDeprecated(); + void enforceDeprecated(); + void load(CompoundTag *tag); + CompoundTag *createTag(); +}; \ No newline at end of file diff --git a/Minecraft.World/MerchantRecipeList.cpp b/Minecraft.World/MerchantRecipeList.cpp new file mode 100644 index 00000000..bd7a126c --- /dev/null +++ b/Minecraft.World/MerchantRecipeList.cpp @@ -0,0 +1,195 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.trading.h" +#include "MerchantRecipeList.h" + +MerchantRecipeList::MerchantRecipeList() +{ +} + +MerchantRecipeList::MerchantRecipeList(CompoundTag *tag) +{ + load(tag); +} + +MerchantRecipeList::~MerchantRecipeList() +{ + for(AUTO_VAR(it, m_recipes.begin()); it != m_recipes.end(); ++it) + { + delete (*it); + } +} + +MerchantRecipe *MerchantRecipeList::getRecipeFor(shared_ptr buyA, shared_ptr buyB, int selectionHint) +{ + if (selectionHint > 0 && selectionHint < m_recipes.size()) + { + // attempt to match vs the hint + MerchantRecipe *r = m_recipes.at(selectionHint); + if (buyA->id == r->getBuyAItem()->id && ((buyB == NULL && !r->hasSecondaryBuyItem()) || (r->hasSecondaryBuyItem() && buyB != NULL && r->getBuyBItem()->id == buyB->id))) + { + if (buyA->count >= r->getBuyAItem()->count && (!r->hasSecondaryBuyItem() || buyB->count >= r->getBuyBItem()->count)) + { + return r; + } + } + return NULL; + } + for (int i = 0; i < m_recipes.size(); i++) + { + MerchantRecipe *r = m_recipes.at(i); + if (buyA->id == r->getBuyAItem()->id && buyA->count >= r->getBuyAItem()->count + && ((!r->hasSecondaryBuyItem() && buyB == NULL) || (r->hasSecondaryBuyItem() && buyB != NULL && r->getBuyBItem()->id == buyB->id && buyB->count >= r->getBuyBItem()->count))) + { + return r; + } + } + return NULL; +} + +bool MerchantRecipeList::addIfNewOrBetter(MerchantRecipe *recipe) +{ + bool added = false; + for (int i = 0; i < m_recipes.size(); i++) + { + MerchantRecipe *r = m_recipes.at(i); + if (recipe->isSame(r)) + { + if (recipe->isSameSameButBetter(r)) + { + delete m_recipes[i]; + m_recipes[i] = recipe; + added = true; + } + return added; + } + } + m_recipes.push_back(recipe); + return true; +} + +MerchantRecipe *MerchantRecipeList::getMatchingRecipeFor(shared_ptr buy, shared_ptr buyB, shared_ptr sell) +{ + for (int i = 0; i < m_recipes.size(); i++) + { + MerchantRecipe *r = m_recipes.at(i); + if (buy->id == r->getBuyAItem()->id && buy->count >= r->getBuyAItem()->count && sell->id == r->getSellItem()->id) + { + if (!r->hasSecondaryBuyItem() || (buyB != NULL && buyB->id == r->getBuyBItem()->id && buyB->count >= r->getBuyBItem()->count)) + { + return r; + } + } + } + return NULL; +} + +void MerchantRecipeList::writeToStream(DataOutputStream *stream) +{ + stream->writeByte((byte) (m_recipes.size() & 0xff)); + for (int i = 0; i < m_recipes.size(); i++) + { + MerchantRecipe *r = m_recipes.at(i); + Packet::writeItem(r->getBuyAItem(), stream); + Packet::writeItem(r->getSellItem(), stream); + + shared_ptr buyBItem = r->getBuyBItem(); + stream->writeBoolean(buyBItem != NULL); + if (buyBItem != NULL) + { + Packet::writeItem(buyBItem, stream); + } + stream->writeBoolean(r->isDeprecated()); + stream->writeInt(r->getUses()); + stream->writeInt(r->getMaxUses()); + } +} + +MerchantRecipeList *MerchantRecipeList::createFromStream(DataInputStream *stream) +{ + MerchantRecipeList *list = new MerchantRecipeList(); + + int count = (int) (stream->readByte() & 0xff); + for (int i = 0; i < count; i++) + { + shared_ptr buy = Packet::readItem(stream); + shared_ptr sell = Packet::readItem(stream); + + shared_ptr buyB = nullptr; + if (stream->readBoolean()) + { + buyB = Packet::readItem(stream); + } + bool isDeprecated = stream->readBoolean(); + int uses = stream->readInt(); + int maxUses = stream->readInt(); + + MerchantRecipe *recipe = new MerchantRecipe(buy, buyB, sell, uses, maxUses); + if (isDeprecated) + { + recipe->enforceDeprecated(); + } + list->push_back(recipe); + } + return list; +} + +void MerchantRecipeList::load(CompoundTag *tag) +{ + ListTag *list = (ListTag *) tag->getList(L"Recipes"); + + for (int i = 0; i < list->size(); i++) + { + CompoundTag *recipeTag = list->get(i); + m_recipes.push_back(new MerchantRecipe(recipeTag)); + } +} + +CompoundTag *MerchantRecipeList::createTag() +{ + CompoundTag *tag = new CompoundTag(); + + ListTag *list = new ListTag(L"Recipes"); + for (int i = 0; i < m_recipes.size(); i++) + { + MerchantRecipe *merchantRecipe = m_recipes.at(i); + list->add(merchantRecipe->createTag()); + } + tag->put(L"Recipes", list); + + return tag; +} + +void MerchantRecipeList::push_back(MerchantRecipe *recipe) +{ + m_recipes.push_back(recipe); +} + +MerchantRecipe *MerchantRecipeList::at(size_t index) +{ + return m_recipes.at(index); +} + +std::vector::iterator MerchantRecipeList::begin() +{ + return m_recipes.begin(); +} + +std::vector::iterator MerchantRecipeList::end() +{ + return m_recipes.end(); +} + +std::vector::iterator MerchantRecipeList::erase(std::vector::iterator it) +{ + return m_recipes.erase(it); +} + +size_t MerchantRecipeList::size() +{ + return m_recipes.size(); +} + +bool MerchantRecipeList::empty() +{ + return m_recipes.empty(); +} \ No newline at end of file diff --git a/Minecraft.World/MerchantRecipeList.h b/Minecraft.World/MerchantRecipeList.h new file mode 100644 index 00000000..4a761d39 --- /dev/null +++ b/Minecraft.World/MerchantRecipeList.h @@ -0,0 +1,35 @@ +#pragma once +#include + +class MerchantRecipe; +class CompoundTag; +class ItemInstance; +class DataOutputStream; +class DataInputStream; + +class MerchantRecipeList +{ +private: + std::vector m_recipes; + +public: + MerchantRecipeList(); + MerchantRecipeList(CompoundTag *tag); + ~MerchantRecipeList(); + + MerchantRecipe *getRecipeFor(shared_ptr buyA, shared_ptr buyB, int selectionHint); + bool addIfNewOrBetter(MerchantRecipe *recipe); // 4J Added bool return + MerchantRecipe *getMatchingRecipeFor(shared_ptr buy, shared_ptr buyB, shared_ptr sell); + void writeToStream(DataOutputStream *stream); + static MerchantRecipeList *createFromStream(DataInputStream *stream); + void load(CompoundTag *tag); + CompoundTag *createTag(); + + void push_back(MerchantRecipe *recipe); + MerchantRecipe *at(size_t index); + std::vector::iterator begin(); + std::vector::iterator end(); + std::vector::iterator erase(std::vector::iterator it); + size_t size(); + bool empty(); +}; \ No newline at end of file diff --git a/Minecraft.World/MerchantResultSlot.cpp b/Minecraft.World/MerchantResultSlot.cpp new file mode 100644 index 00000000..fedbcc4a --- /dev/null +++ b/Minecraft.World/MerchantResultSlot.cpp @@ -0,0 +1,94 @@ +#include "stdafx.h" +#include "net.minecraft.world.inventory.h" +#include "net.minecraft.world.item.trading.h" +#include "MerchantResultSlot.h" + +MerchantResultSlot::MerchantResultSlot(Player *player, shared_ptr merchant, shared_ptr slots, int id, int x, int y) : Slot(slots, id, x, y) +{ + this->player = player; + this->merchant = merchant; + this->slots = slots; + removeCount = 0; +} + +bool MerchantResultSlot::mayPlace(shared_ptr item) +{ + return false; +} + +shared_ptr MerchantResultSlot::remove(int c) +{ + if (hasItem()) + { + removeCount += min(c, getItem()->count); + } + return Slot::remove(c); +} + +void MerchantResultSlot::onQuickCraft(shared_ptr picked, int count) +{ + removeCount += count; + checkTakeAchievements(picked); +} + +void MerchantResultSlot::checkTakeAchievements(shared_ptr carried) +{ + carried->onCraftedBy(player->level, dynamic_pointer_cast(player->shared_from_this()), removeCount); + removeCount = 0; +} + +void MerchantResultSlot::onTake(shared_ptr player, shared_ptr carried) +{ + checkTakeAchievements(carried); + + MerchantRecipe *activeRecipe = slots->getActiveRecipe(); + if (activeRecipe != NULL) + { + shared_ptr item1 = slots->getItem(MerchantMenu::PAYMENT1_SLOT); + shared_ptr item2 = slots->getItem(MerchantMenu::PAYMENT2_SLOT); + + // remove payment items, but remember slots may have switched + if (removePaymentItemsIfMatching(activeRecipe, item1, item2) || removePaymentItemsIfMatching(activeRecipe, item2, item1)) + { + merchant->notifyTrade(activeRecipe); + + if (item1 && item1->count <= 0) + { + item1 = nullptr; + } + if (item2 && item2->count <= 0) + { + item2 = nullptr; + } + slots->setItem(MerchantMenu::PAYMENT1_SLOT, item1); + slots->setItem(MerchantMenu::PAYMENT2_SLOT, item2); + } + } +} + +bool MerchantResultSlot::mayCombine(shared_ptr second) +{ + return false; +} + +bool MerchantResultSlot::removePaymentItemsIfMatching(MerchantRecipe *activeRecipe, shared_ptr a, shared_ptr b) +{ + shared_ptr buyA = activeRecipe->getBuyAItem(); + shared_ptr buyB = activeRecipe->getBuyBItem(); + + if (a != NULL && a->id == buyA->id) + { + if (buyB != NULL && b != NULL && buyB->id == b->id) + { + a->count -= buyA->count; + b->count -= buyB->count; + return true; + } + else if (buyB == NULL && b == NULL) + { + a->count -= buyA->count; + return true; + } + } + return false; +} \ No newline at end of file diff --git a/Minecraft.World/MerchantResultSlot.h b/Minecraft.World/MerchantResultSlot.h new file mode 100644 index 00000000..7b8d97b6 --- /dev/null +++ b/Minecraft.World/MerchantResultSlot.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Slot.h" + +class MerchantContainer; +class Player; +class Merchant; + +class MerchantResultSlot : public Slot +{ +private: + shared_ptr slots; + Player *player; + int removeCount; + shared_ptr merchant; + +public: + MerchantResultSlot(Player *player, shared_ptr merchant, shared_ptr slots, int id, int x, int y); + + bool mayPlace(shared_ptr item); + shared_ptr remove(int c); + +protected: + void onQuickCraft(shared_ptr picked, int count); + void checkTakeAchievements(shared_ptr carried); + +public: + void onTake(shared_ptr player, shared_ptr carried); + virtual bool mayCombine(shared_ptr item); // 4J Added + +private: + bool removePaymentItemsIfMatching(MerchantRecipe *activeRecipe, shared_ptr a, shared_ptr b); +}; \ No newline at end of file diff --git a/Minecraft.World/MetalTile.cpp b/Minecraft.World/MetalTile.cpp new file mode 100644 index 00000000..9092f95d --- /dev/null +++ b/Minecraft.World/MetalTile.cpp @@ -0,0 +1,6 @@ +#include "stdafx.h" +#include "MetalTile.h" + +MetalTile::MetalTile(int id) : Tile(id, Material::metal) +{ +} \ No newline at end of file diff --git a/Minecraft.World/MetalTile.h b/Minecraft.World/MetalTile.h new file mode 100644 index 00000000..adbb02f8 --- /dev/null +++ b/Minecraft.World/MetalTile.h @@ -0,0 +1,8 @@ +#pragma once +#include "Tile.h" + +class MetalTile : public Tile +{ +public: + MetalTile(int id); +}; \ No newline at end of file diff --git a/Minecraft.World/MilkBucketItem.cpp b/Minecraft.World/MilkBucketItem.cpp new file mode 100644 index 00000000..4a371078 --- /dev/null +++ b/Minecraft.World/MilkBucketItem.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.player.h" +#include "MilkBucketItem.h" + +MilkBucketItem::MilkBucketItem(int id) : Item( id ) +{ + setMaxStackSize(1); +} + +shared_ptr MilkBucketItem::useTimeDepleted(shared_ptr instance, Level *level, shared_ptr player) +{ + if (!player->abilities.instabuild) instance->count--; + + if (!level->isClientSide) + { + player->removeAllEffects(); + } + + if (instance->count <= 0) + { + return shared_ptr( new ItemInstance(Item::bucket_empty) ); + } + return instance; +} + +int MilkBucketItem::getUseDuration(shared_ptr itemInstance) +{ + return DRINK_DURATION; +} + +UseAnim MilkBucketItem::getUseAnimation(shared_ptr itemInstance) +{ + return UseAnim_drink; +} + +shared_ptr MilkBucketItem::use(shared_ptr instance, Level *level, shared_ptr player) +{ + player->startUsingItem(instance, getUseDuration(instance)); + return instance; +} \ No newline at end of file diff --git a/Minecraft.World/MilkBucketItem.h b/Minecraft.World/MilkBucketItem.h new file mode 100644 index 00000000..672e3143 --- /dev/null +++ b/Minecraft.World/MilkBucketItem.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Item.h" + +class MilkBucketItem : public Item +{ +private: + static const int DRINK_DURATION = (int) (20 * 1.6); + +public: + MilkBucketItem(int id); + + virtual shared_ptr useTimeDepleted(shared_ptr instance, Level *level, shared_ptr player); + virtual int getUseDuration(shared_ptr itemInstance); + virtual UseAnim getUseAnimation(shared_ptr itemInstance); + virtual shared_ptr use(shared_ptr instance, Level *level, shared_ptr player); +}; \ No newline at end of file diff --git a/Minecraft.World/MineShaftFeature.cpp b/Minecraft.World/MineShaftFeature.cpp new file mode 100644 index 00000000..ac4d0c3f --- /dev/null +++ b/Minecraft.World/MineShaftFeature.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.levelgen.structure.h" +#include "JavaMath.h" + +bool MineShaftFeature::isFeatureChunk(int x, int z, bool bIsSuperflat) +{ + bool forcePlacement = false; + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + if( levelGenOptions != NULL ) + { + forcePlacement = levelGenOptions->isFeatureChunk(x,z,eFeature_Mineshaft); + } + + return forcePlacement || (random->nextInt(100) == 0 && random->nextInt(80) < max(abs(x), abs(z))); +} + +StructureStart *MineShaftFeature::createStructureStart(int x, int z) +{ + // 4J added + app.AddTerrainFeaturePosition(eTerrainFeature_Mineshaft,x,z); + + return new MineShaftStart(level, random, x, z); +} \ No newline at end of file diff --git a/Minecraft.World/MineShaftFeature.h b/Minecraft.World/MineShaftFeature.h new file mode 100644 index 00000000..698010ec --- /dev/null +++ b/Minecraft.World/MineShaftFeature.h @@ -0,0 +1,10 @@ +#pragma once + +#include "StructureFeature.h" + +class MineShaftFeature : public StructureFeature +{ +protected: + virtual bool isFeatureChunk(int x, int z, bool bIsSuperflat=false); + virtual StructureStart *createStructureStart(int x, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/MineShaftPieces.cpp b/Minecraft.World/MineShaftPieces.cpp new file mode 100644 index 00000000..fe13b49f --- /dev/null +++ b/Minecraft.World/MineShaftPieces.cpp @@ -0,0 +1,697 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.levelgen.structure.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.item.h" +#include "JavaMath.h" +#include "WeighedTreasure.h" +#include "MineShaftPieces.h" + +WeighedTreasureArray MineShaftPieces::smallTreasureItems;; + +void MineShaftPieces::staticCtor() +{ + smallTreasureItems = WeighedTreasureArray(11); + smallTreasureItems[0] = new WeighedTreasure(Item::ironIngot_Id, 0, 1, 5, 10); + smallTreasureItems[1] = new WeighedTreasure(Item::goldIngot_Id, 0, 1, 3, 5); + smallTreasureItems[2] = new WeighedTreasure(Item::redStone_Id, 0, 4, 9, 5); + smallTreasureItems[3] = new WeighedTreasure(Item::dye_powder_Id, DyePowderItem::BLUE, 4, 9, 5); + smallTreasureItems[4] = new WeighedTreasure(Item::diamond_Id, 0, 1, 2, 3); + smallTreasureItems[5] = new WeighedTreasure(Item::coal_Id, CoalItem::STONE_COAL, 3, 8, 10); + smallTreasureItems[6] = new WeighedTreasure(Item::bread_Id, 0, 1, 3, 15); + smallTreasureItems[7] = new WeighedTreasure(Item::pickAxe_iron_Id, 0, 1, 1, 1); + smallTreasureItems[8] = new WeighedTreasure(Tile::rail_Id, 0, 4, 8, 1); + smallTreasureItems[9] = new WeighedTreasure(Item::seeds_melon_Id, 0, 2, 4, 10); + // 4J-PB - Adding from 1.2.3 + smallTreasureItems[10] = new WeighedTreasure(Item::seeds_pumpkin_Id, 0, 2, 4, 10); +} + +StructurePiece *MineShaftPieces::createRandomShaftPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + int randomSelection = random->nextInt(100); + if (randomSelection >= 80) + { + BoundingBox *crossingBox = MineShaftCrossing::findCrossing(pieces, random, footX, footY, footZ, direction); + if (crossingBox != NULL) + { + return new MineShaftCrossing(genDepth, random, crossingBox, direction); + } + } + else if (randomSelection >= 70) + { + BoundingBox *stairsBox = MineShaftStairs::findStairs(pieces, random, footX, footY, footZ, direction); + if (stairsBox != NULL) + { + return new MineShaftPieces::MineShaftStairs(genDepth, random, stairsBox, direction); + } + } + else + { + BoundingBox *corridorBox = MineShaftCorridor::findCorridorSize(pieces, random, footX, footY, footZ, direction); + if (corridorBox != NULL) + { + return new MineShaftCorridor(genDepth, random, corridorBox, direction); + } + } + + return NULL; +} + +StructurePiece *MineShaftPieces::generateAndAddPiece(StructurePiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth) +{ + if (depth > MAX_DEPTH) + { + return NULL; + } + if (abs(footX - startPiece->getBoundingBox()->x0) > 5 * 16 || abs(footZ - startPiece->getBoundingBox()->z0) > 5 * 16) + { + return NULL; + } + + StructurePiece *newPiece = createRandomShaftPiece(pieces, random, footX, footY, footZ, direction, depth + 1); + if (newPiece != NULL) + { + MemSect(50); + pieces->push_back(newPiece); + MemSect(0); + newPiece->addChildren(startPiece, pieces, random); + } + return newPiece; +} + + +MineShaftPieces::MineShaftRoom::MineShaftRoom(int genDepth, Random *random, int west, int north) : StructurePiece(genDepth) +{ + boundingBox = new BoundingBox(west, 50, north, west + 7 + random->nextInt(6), 54 + random->nextInt(6), north + 7 + random->nextInt(6)); +} + +MineShaftPieces::MineShaftRoom::~MineShaftRoom() +{ + for(AUTO_VAR(it, childEntranceBoxes.begin()); it != childEntranceBoxes.end(); ++it) + { + delete (*it); + } +} + +void MineShaftPieces::MineShaftRoom::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + + int depth = getGenDepth(); + + int pos; + + int heightSpace = boundingBox->getYSpan() - DEFAULT_SHAFT_HEIGHT - 1; + if (heightSpace <= 0) + { + heightSpace = 1; + } + + // northern exits + pos = 0; + while (pos < boundingBox->getXSpan()) + { + pos += random->nextInt(boundingBox->getXSpan()); + if ((pos + DEFAULT_SHAFT_WIDTH) > boundingBox->getXSpan()) + { + break; + } + StructurePiece *child = generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + pos, boundingBox->y0 + random->nextInt(heightSpace) + 1, boundingBox->z0 - 1, Direction::NORTH, depth); + if (child != NULL) + { + BoundingBox *childBox = child->getBoundingBox(); + childEntranceBoxes.push_back(new BoundingBox(childBox->x0, childBox->y0, boundingBox->z0, childBox->x1, childBox->y1, boundingBox->z0 + 1)); + } + pos += DEFAULT_SHAFT_WIDTH + 1; + } + // southern exits + pos = 0; + while (pos < boundingBox->getXSpan()) + { + pos += random->nextInt(boundingBox->getXSpan()); + if ((pos + DEFAULT_SHAFT_WIDTH) > boundingBox->getXSpan()) + { + break; + } + StructurePiece *child = generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + pos, boundingBox->y0 + random->nextInt(heightSpace) + 1, boundingBox->z1 + 1, Direction::SOUTH, depth); + if (child != NULL) + { + BoundingBox *childBox = child->getBoundingBox(); + childEntranceBoxes.push_back(new BoundingBox(childBox->x0, childBox->y0, boundingBox->z1 - 1, childBox->x1, childBox->y1, boundingBox->z1)); + } + pos += DEFAULT_SHAFT_WIDTH + 1; + } + // western exits + pos = 0; + while (pos < boundingBox->getZSpan()) + { + pos += random->nextInt(boundingBox->getZSpan()); + if ((pos + DEFAULT_SHAFT_WIDTH) > boundingBox->getZSpan()) + { + break; + } + StructurePiece *child = generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + random->nextInt(heightSpace) + 1, boundingBox->z0 + pos, Direction::WEST, depth); + if (child != NULL) + { + BoundingBox *childBox = child->getBoundingBox(); + childEntranceBoxes.push_back(new BoundingBox(boundingBox->x0, childBox->y0, childBox->z0, boundingBox->x0 + 1, childBox->y1, childBox->z1)); + } + pos += DEFAULT_SHAFT_WIDTH + 1; + } + // eastern exits + pos = 0; + while (pos < boundingBox->getZSpan()) + { + pos += random->nextInt(boundingBox->getZSpan()); + if ((pos + DEFAULT_SHAFT_WIDTH) > boundingBox->getZSpan()) + { + break; + } + StructurePiece *child = generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + random->nextInt(heightSpace) + 1, boundingBox->z0 + pos, Direction::EAST, depth); + if (child != NULL) + { + BoundingBox *childBox = child->getBoundingBox(); + childEntranceBoxes.push_back(new BoundingBox(boundingBox->x1 - 1, childBox->y0, childBox->z0, boundingBox->x1, childBox->y1, childBox->z1)); + } + pos += DEFAULT_SHAFT_WIDTH + 1; + } +} + +bool MineShaftPieces::MineShaftRoom::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // floor + generateBox(level, chunkBB, boundingBox->x0, boundingBox->y0, boundingBox->z0, boundingBox->x1, boundingBox->y0, boundingBox->z1, Tile::dirt_Id, 0, true); + + // room air + generateBox(level, chunkBB, boundingBox->x0, boundingBox->y0 + 1, boundingBox->z0, boundingBox->x1, min(boundingBox->y0 + 3, boundingBox->y1), boundingBox->z1, 0, 0, false); + for(AUTO_VAR(it, childEntranceBoxes.begin()); it != childEntranceBoxes.end(); ++it) + { + BoundingBox *entranceBox = *it; + generateBox(level, chunkBB, entranceBox->x0, entranceBox->y1 - (DEFAULT_SHAFT_HEIGHT - 1), entranceBox->z0, entranceBox->x1, entranceBox->y1, entranceBox->z1, 0, 0, false); + } + generateUpperHalfSphere(level, chunkBB, boundingBox->x0, boundingBox->y0 + 4, boundingBox->z0, boundingBox->x1, boundingBox->y1, boundingBox->z1, 0, false); + + return true; +} + + +MineShaftPieces::MineShaftCorridor::MineShaftCorridor(int genDepth, Random *random, BoundingBox *corridorBox, int direction) + : StructurePiece(genDepth) +{ + orientation = direction; + boundingBox = corridorBox; + hasRails = random->nextInt(3) == 0; + hasPlacedSpider=false; + spiderCorridor = !hasRails && random->nextInt(23) == 0; + //debug + //spiderCorridor = !hasRails ;//&& random->nextInt(23) == 0; + + if (orientation == Direction::NORTH || orientation == Direction::SOUTH) + { + numSections = corridorBox->getZSpan() / DEFAULT_SHAFT_LENGTH; + } + else + { + numSections = corridorBox->getXSpan() / DEFAULT_SHAFT_LENGTH; + } +} + +BoundingBox *MineShaftPieces::MineShaftCorridor::findCorridorSize(list *pieces, Random *random, int footX, int footY, int footZ, int direction) +{ + + BoundingBox *box = new BoundingBox(footX, footY, footZ, footX, footY + (DEFAULT_SHAFT_HEIGHT - 1), footZ); + + int corridorLength = random->nextInt(3) + 2; + while (corridorLength > 0) + { + int blockLength = corridorLength * DEFAULT_SHAFT_LENGTH; + + switch (direction) + { + case Direction::NORTH: + box->x1 = footX + (DEFAULT_SHAFT_WIDTH - 1); + box->z0 = footZ - (blockLength - 1); + break; + case Direction::SOUTH: + box->x1 = footX + (DEFAULT_SHAFT_WIDTH - 1); + box->z1 = footZ + (blockLength - 1); + break; + case Direction::WEST: + box->x0 = footX - (blockLength - 1); + box->z1 = footZ + (DEFAULT_SHAFT_WIDTH - 1); + break; + case Direction::EAST: + box->x1 = footX + (blockLength - 1); + box->z1 = footZ + (DEFAULT_SHAFT_WIDTH - 1); + break; + } + + if (StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + corridorLength--; + } + else + { + break; + } + } + + if (corridorLength > 0) + { + return box; + } + delete box; + // unable to place corridor here + return NULL; +} + +void MineShaftPieces::MineShaftCorridor::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + int depth = getGenDepth(); + int endSelection = random->nextInt(4); + switch (orientation) + { + case Direction::NORTH: + if (endSelection <= 1) + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0, boundingBox->y0 - 1 + random->nextInt(3), boundingBox->z0 - 1, orientation, depth); + } + else if (endSelection == 2) + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 - 1 + random->nextInt(3), boundingBox->z0, Direction::WEST, depth); + } + else + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 - 1 + random->nextInt(3), boundingBox->z0, Direction::EAST, depth); + } + break; + case Direction::SOUTH: + if (endSelection <= 1) + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0, boundingBox->y0 - 1 + random->nextInt(3), boundingBox->z1 + 1, orientation, depth); + } + else if (endSelection == 2) + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 - 1 + random->nextInt(3), boundingBox->z1 - DEFAULT_SHAFT_WIDTH, Direction::WEST, depth); + } + else + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 - 1 + random->nextInt(3), boundingBox->z1 - DEFAULT_SHAFT_WIDTH, Direction::EAST, depth); + } + break; + case Direction::WEST: + if (endSelection <= 1) + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 - 1 + random->nextInt(3), boundingBox->z0, orientation, depth); + } + else if (endSelection == 2) + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0, boundingBox->y0 - 1 + random->nextInt(3), boundingBox->z0 - 1, Direction::NORTH, depth); + } + else + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0, boundingBox->y0 - 1 + random->nextInt(3), boundingBox->z1 + 1, Direction::SOUTH, depth); + } + break; + case Direction::EAST: + if (endSelection <= 1) + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 - 1 + random->nextInt(3), boundingBox->z0, orientation, depth); + } + else if (endSelection == 2) + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 - DEFAULT_SHAFT_WIDTH, boundingBox->y0 - 1 + random->nextInt(3), boundingBox->z0 - 1, Direction::NORTH, depth); + } + else + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 - DEFAULT_SHAFT_WIDTH, boundingBox->y0 - 1 + random->nextInt(3), boundingBox->z1 + 1, Direction::SOUTH, depth); + } + break; + } + + // generate cross sections using higher depth + if (depth < MAX_DEPTH) + { + if (orientation == Direction::NORTH || orientation == Direction::SOUTH) + { + for (int z = boundingBox->z0 + 3; (z + DEFAULT_SHAFT_WIDTH) <= boundingBox->z1; z += DEFAULT_SHAFT_LENGTH) + { + int selection = random->nextInt(5); + if (selection == 0) + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0, z, Direction::WEST, depth + 1); + } + else if (selection == 1) + { + generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0, z, Direction::EAST, depth + 1); + } + } + } + else + { + for (int x = boundingBox->x0 + 3; (x + DEFAULT_SHAFT_WIDTH) <= boundingBox->x1; x += DEFAULT_SHAFT_LENGTH) + { + int selection = random->nextInt(5); + if (selection == 0) + { + generateAndAddPiece(startPiece, pieces, random, x, boundingBox->y0, boundingBox->z0 - 1, Direction::NORTH, depth + 1); + } + else if (selection == 1) + { + generateAndAddPiece(startPiece, pieces, random, x, boundingBox->y0, boundingBox->z1 + 1, Direction::SOUTH, depth + 1); + } + } + } + } +} + +bool MineShaftPieces::MineShaftCorridor::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + const int x0 = 0; + const int x1 = DEFAULT_SHAFT_WIDTH - 1; + const int y0 = 0; + const int y1 = DEFAULT_SHAFT_HEIGHT - 1; + const int length = (numSections * DEFAULT_SHAFT_LENGTH) - 1; + + // corridor air + generateBox(level, chunkBB, x0, 0, y0, x1, y1 - 1, length, 0, 0, false); + generateMaybeBox(level, chunkBB, random, .8f, x0, y1, y0, x1, y1, length, 0, 0, false); + + if (spiderCorridor) + { + generateMaybeBox(level, chunkBB, random, .6f, x0, 0, y0, x1, y1 - 1, length, Tile::web_Id, 0, false); + } + + // place a support in every section + for (int section = 0; section < numSections; section++) + { + + int z = 2 + section * DEFAULT_SHAFT_LENGTH; + + // 4J-PB - Bringing forward the changes in 1.2.3 + generateBox(level, chunkBB, x0, y0, z, x0, y1 - 1, z, Tile::fence_Id, 0, false); + generateBox(level, chunkBB, x1, y0, z, x1, y1 - 1, z, Tile::fence_Id, 0, false); + if (random->nextInt(4) == 0) + { + generateBox(level, chunkBB, x0, y1, z, x0, y1, z, Tile::wood_Id, 0, false); + generateBox(level, chunkBB, x1, y1, z, x1, y1, z, Tile::wood_Id, 0, false); + } + else + { + generateBox(level, chunkBB, x0, y1, z, x1, y1, z, Tile::wood_Id, 0, false); + } + maybeGenerateBlock(level, chunkBB, random, .1f, x0, y1, z - 1, Tile::web_Id, 0); + maybeGenerateBlock(level, chunkBB, random, .1f, x1, y1, z - 1, Tile::web_Id, 0); + maybeGenerateBlock(level, chunkBB, random, .1f, x0, y1, z + 1, Tile::web_Id, 0); + maybeGenerateBlock(level, chunkBB, random, .1f, x1, y1, z + 1, Tile::web_Id, 0); + maybeGenerateBlock(level, chunkBB, random, .05f, x0, y1, z - 2, Tile::web_Id, 0); + maybeGenerateBlock(level, chunkBB, random, .05f, x1, y1, z - 2, Tile::web_Id, 0); + maybeGenerateBlock(level, chunkBB, random, .05f, x0, y1, z + 2, Tile::web_Id, 0); + maybeGenerateBlock(level, chunkBB, random, .05f, x1, y1, z + 2, Tile::web_Id, 0); + + maybeGenerateBlock(level, chunkBB, random, .05f, x0 + 1, y1, z - 1, Tile::torch_Id, 0); + maybeGenerateBlock(level, chunkBB, random, .05f, x0 + 1, y1, z + 1, Tile::torch_Id, 0); + + if (random->nextInt(100) == 0) + { + createChest(level, chunkBB, random, x1, y0, z - 1, WeighedTreasure::addToTreasure(smallTreasureItems, Item::enchantedBook->createForRandomTreasure(random)), 3 + random->nextInt(4)); + } + if (random->nextInt(100) == 0) + { + createChest(level, chunkBB, random, x0, y0, z + 1, WeighedTreasure::addToTreasure(smallTreasureItems, Item::enchantedBook->createForRandomTreasure(random)), 3 + random->nextInt(4)); + } + + if (spiderCorridor && !hasPlacedSpider) + { + int y = getWorldY(y0), newZ = z - 1 + random->nextInt(3); + int x = getWorldX(x0 + 1, newZ); + newZ = getWorldZ(x0 + 1, newZ); + if (chunkBB->isInside(x, y, newZ)) + { + hasPlacedSpider = true; + level->setTile(x, y, newZ, Tile::mobSpawner_Id); + shared_ptr entity = dynamic_pointer_cast( level->getTileEntity(x, y, newZ) ); + if (entity != NULL) entity->setEntityId(L"CaveSpider"); + } + } + } + + // prevent air floating + for (int x = x0; x <= x1; x++) + { + for (int z = 0; z <= length; z++) + { + int block = getBlock(level, x, -1, z, chunkBB); + if (block == 0) + { + placeBlock(level, Tile::wood_Id, 0, x, -1, z, chunkBB); + } + } + } + + if (hasRails) + { + for (int z = 0; z <= length; z++) + { + int floor = getBlock(level, x0 + 1, y0 - 1, z, chunkBB); + if (floor > 0 && Tile::solid[floor]) + { + maybeGenerateBlock(level, chunkBB, random, .7f, x0 + 1, y0, z, Tile::rail_Id, getOrientationData(Tile::rail_Id, RailTile::DIR_FLAT_Z)); + } + } + } + + return true; +} + +MineShaftPieces::MineShaftCrossing::MineShaftCrossing(int genDepth, Random *random, BoundingBox *crossingBox, int direction) + : StructurePiece(genDepth), direction(direction), isTwoFloored( crossingBox->getYSpan() > DEFAULT_SHAFT_HEIGHT ) +{ + boundingBox = crossingBox; +} + +BoundingBox *MineShaftPieces::MineShaftCrossing::findCrossing(list *pieces, Random *random, int footX, int footY, int footZ, int direction) +{ + + BoundingBox *box = new BoundingBox(footX, footY, footZ, footX, footY + (DEFAULT_SHAFT_HEIGHT - 1), footZ); + + if (random->nextInt(4) == 0) + { + box->y1 += DEFAULT_SHAFT_HEIGHT + 1; // two-floored + } + + switch (direction) + { + case Direction::NORTH: + box->x0 = footX - 1; + box->x1 = footX + DEFAULT_SHAFT_WIDTH; + box->z0 = footZ - (DEFAULT_SHAFT_WIDTH + 1); + break; + case Direction::SOUTH: + box->x0 = footX - 1; + box->x1 = footX + DEFAULT_SHAFT_WIDTH; + box->z1 = footZ + (DEFAULT_SHAFT_WIDTH + 1); + break; + case Direction::WEST: + box->x0 = footX - (DEFAULT_SHAFT_WIDTH + 1); + box->z0 = footZ - 1; + box->z1 = footZ + DEFAULT_SHAFT_WIDTH; + break; + case Direction::EAST: + box->x1 = footX + (DEFAULT_SHAFT_WIDTH + 1); + box->z0 = footZ - 1; + box->z1 = footZ + DEFAULT_SHAFT_WIDTH; + break; + } + + if (StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return box; +} + +void MineShaftPieces::MineShaftCrossing::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + + int depth = getGenDepth(); + // crossings are coming from a direction and will generate children + // in the + // remaining three directions + switch (direction) + { + case Direction::NORTH: + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + 1, boundingBox->y0, boundingBox->z0 - 1, Direction::NORTH, depth); + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0, boundingBox->z0 + 1, Direction::WEST, depth); + generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0, boundingBox->z0 + 1, Direction::EAST, depth); + break; + case Direction::SOUTH: + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + 1, boundingBox->y0, boundingBox->z1 + 1, Direction::SOUTH, depth); + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0, boundingBox->z0 + 1, Direction::WEST, depth); + generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0, boundingBox->z0 + 1, Direction::EAST, depth); + break; + case Direction::WEST: + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + 1, boundingBox->y0, boundingBox->z0 - 1, Direction::NORTH, depth); + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + 1, boundingBox->y0, boundingBox->z1 + 1, Direction::SOUTH, depth); + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0, boundingBox->z0 + 1, Direction::WEST, depth); + break; + case Direction::EAST: + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + 1, boundingBox->y0, boundingBox->z0 - 1, Direction::NORTH, depth); + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + 1, boundingBox->y0, boundingBox->z1 + 1, Direction::SOUTH, depth); + generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0, boundingBox->z0 + 1, Direction::EAST, depth); + break; + } + + if (isTwoFloored) + { + if (random->nextBoolean()) generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + 1, boundingBox->y0 + DEFAULT_SHAFT_HEIGHT + 1, boundingBox->z0 - 1, Direction::NORTH, depth); + if (random->nextBoolean()) generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + DEFAULT_SHAFT_HEIGHT + 1, boundingBox->z0 + 1, Direction::WEST, depth); + if (random->nextBoolean()) generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + DEFAULT_SHAFT_HEIGHT + 1, boundingBox->z0 + 1, Direction::EAST, depth); + if (random->nextBoolean()) generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + 1, boundingBox->y0 + DEFAULT_SHAFT_HEIGHT + 1, boundingBox->z1 + 1, Direction::SOUTH, depth); + } +} + +bool MineShaftPieces::MineShaftCrossing::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // corridor air + if (isTwoFloored) + { + generateBox(level, chunkBB, boundingBox->x0 + 1, boundingBox->y0, boundingBox->z0, boundingBox->x1 - 1, boundingBox->y0 + DEFAULT_SHAFT_HEIGHT - 1, boundingBox->z1, 0, 0, false); + generateBox(level, chunkBB, boundingBox->x0, boundingBox->y0, boundingBox->z0 + 1, boundingBox->x1, boundingBox->y0 + DEFAULT_SHAFT_HEIGHT - 1, boundingBox->z1 - 1, 0, 0, false); + generateBox(level, chunkBB, boundingBox->x0 + 1, boundingBox->y1 - (DEFAULT_SHAFT_HEIGHT - 1), boundingBox->z0, boundingBox->x1 - 1, boundingBox->y1, boundingBox->z1, 0, 0, false); + generateBox(level, chunkBB, boundingBox->x0, boundingBox->y1 - (DEFAULT_SHAFT_HEIGHT - 1), boundingBox->z0 + 1, boundingBox->x1, boundingBox->y1, boundingBox->z1 - 1, 0, 0, false); + generateBox(level, chunkBB, boundingBox->x0 + 1, boundingBox->y0 + DEFAULT_SHAFT_HEIGHT, boundingBox->z0 + 1, boundingBox->x1 - 1, boundingBox->y0 + DEFAULT_SHAFT_HEIGHT, boundingBox->z1 - 1, 0, 0, false); + } + else + { + generateBox(level, chunkBB, boundingBox->x0 + 1, boundingBox->y0, boundingBox->z0, boundingBox->x1 - 1, boundingBox->y1, boundingBox->z1, 0, 0, false); + generateBox(level, chunkBB, boundingBox->x0, boundingBox->y0, boundingBox->z0 + 1, boundingBox->x1, boundingBox->y1, boundingBox->z1 - 1, 0, 0, false); + } + + // support pillars + generateBox(level, chunkBB, boundingBox->x0 + 1, boundingBox->y0, boundingBox->z0 + 1, boundingBox->x0 + 1, boundingBox->y1, boundingBox->z0 + 1, Tile::wood_Id, 0, false); + generateBox(level, chunkBB, boundingBox->x0 + 1, boundingBox->y0, boundingBox->z1 - 1, boundingBox->x0 + 1, boundingBox->y1, boundingBox->z1 - 1, Tile::wood_Id, 0, false); + generateBox(level, chunkBB, boundingBox->x1 - 1, boundingBox->y0, boundingBox->z0 + 1, boundingBox->x1 - 1, boundingBox->y1, boundingBox->z0 + 1, Tile::wood_Id, 0, false); + generateBox(level, chunkBB, boundingBox->x1 - 1, boundingBox->y0, boundingBox->z1 - 1, boundingBox->x1 - 1, boundingBox->y1, boundingBox->z1 - 1, Tile::wood_Id, 0, false); + + // prevent air floating + // note: use world coordinates because the corridor hasn't defined + // orientation + for (int x = boundingBox->x0; x <= boundingBox->x1; x++) + { + for (int z = boundingBox->z0; z <= boundingBox->z1; z++) + { + int block = getBlock(level, x, boundingBox->y0 - 1, z, chunkBB); + if (block == 0) + { + placeBlock(level, Tile::wood_Id, 0, x, boundingBox->y0 - 1, z, chunkBB); + } + } + } + + return true; +} + + +MineShaftPieces::MineShaftStairs::MineShaftStairs(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StructurePiece(genDepth) +{ + this->orientation = direction; + boundingBox = stairsBox; +} + +BoundingBox *MineShaftPieces::MineShaftStairs::findStairs(list *pieces, Random *random, int footX, int footY, int footZ, int direction) +{ + // stairs are two steps in, 5x5 steps down, two steps out + + BoundingBox *box = new BoundingBox(footX, footY - 5, footZ, footX, footY + (DEFAULT_SHAFT_HEIGHT - 1), footZ); + + switch (direction) + { + case Direction::NORTH: + box->x1 = footX + (DEFAULT_SHAFT_WIDTH - 1); + box->z0 = footZ - 8; + break; + case Direction::SOUTH: + box->x1 = footX + (DEFAULT_SHAFT_WIDTH - 1); + box->z1 = footZ + 8; + break; + case Direction::WEST: + box->x0 = footX - 8; + box->z1 = footZ + (DEFAULT_SHAFT_WIDTH - 1); + break; + case Direction::EAST: + box->x1 = footX + 8; + box->z1 = footZ + (DEFAULT_SHAFT_WIDTH - 1); + break; + } + + if (StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return box; +} + +void MineShaftPieces::MineShaftStairs::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + int depth = getGenDepth(); + // crossings are coming from a direction and will generate children + // in the + // remaining three directions + switch (orientation) + { + case Direction::NORTH: + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0, boundingBox->y0, boundingBox->z0 - 1, Direction::NORTH, depth); + break; + case Direction::SOUTH: + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0, boundingBox->y0, boundingBox->z1 + 1, Direction::SOUTH, depth); + break; + case Direction::WEST: + generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0, boundingBox->z0, Direction::WEST, depth); + break; + case Direction::EAST: + generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0, boundingBox->z0, Direction::EAST, depth); + break; + } + +} + +bool MineShaftPieces::MineShaftStairs::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // upper floor + generateBox(level, chunkBB, 0, 5, 0, (DEFAULT_SHAFT_WIDTH - 1), 5 + (DEFAULT_SHAFT_HEIGHT - 1), 1, 0, 0, false); + // lower floor + generateBox(level, chunkBB, 0, 0, 7, (DEFAULT_SHAFT_WIDTH - 1), (DEFAULT_SHAFT_HEIGHT - 1), 8, 0, 0, false); + // stairs + for (int i = 0; i < 5; i++) + { + generateBox(level, chunkBB, 0, 5 - i - ((i < 4) ? 1 : 0), 2 + i, (DEFAULT_SHAFT_WIDTH - 1), 5 + (DEFAULT_SHAFT_HEIGHT - 1) - i, 2 + i, 0, 0, false); + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/MineShaftPieces.h b/Minecraft.World/MineShaftPieces.h new file mode 100644 index 00000000..5f9cf000 --- /dev/null +++ b/Minecraft.World/MineShaftPieces.h @@ -0,0 +1,96 @@ +#pragma once + +#include "StructurePiece.h" + +class MineShaftPieces +{ +private: + static const int DEFAULT_SHAFT_WIDTH = 3; + static const int DEFAULT_SHAFT_HEIGHT = 3; + static const int DEFAULT_SHAFT_LENGTH = 5; + + static const int MAX_DEPTH = 8; // 1.2.3 change + + static StructurePiece *createRandomShaftPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + static StructurePiece *generateAndAddPiece(StructurePiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth); + + /** + * + * + */ +public: + class MineShaftRoom : public StructurePiece + { + private: + list childEntranceBoxes; + + public: + MineShaftRoom(int genDepth, Random *random, int west, int north); + ~MineShaftRoom(); + + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ + class MineShaftCorridor : public StructurePiece + { + private: + bool hasRails; // was final + bool spiderCorridor; // was final + bool hasPlacedSpider; + int numSections; + + public: + MineShaftCorridor(int genDepth, Random *random, BoundingBox *corridorBox, int direction); + + static BoundingBox *findCorridorSize(list *pieces, Random *random, int footX, int footY, int footZ, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ + class MineShaftCrossing : public StructurePiece + { + private: + const int direction; + const bool isTwoFloored; + + public: + MineShaftCrossing(int genDepth, Random *random, BoundingBox *crossingBox, int direction); + + static BoundingBox *findCrossing(list *pieces, Random *random, int footX, int footY, int footZ, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ + class MineShaftStairs : public StructurePiece + { + public: + MineShaftStairs(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + + static BoundingBox *findStairs(list *pieces, Random *random, int footX, int footY, int footZ, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + + }; + + /* @formatter:off */ +private: + static WeighedTreasureArray smallTreasureItems; + /* @formatter:on */ + +public: + static void staticCtor(); + +}; \ No newline at end of file diff --git a/Minecraft.World/MineShaftStart.cpp b/Minecraft.World/MineShaftStart.cpp new file mode 100644 index 00000000..badd3a04 --- /dev/null +++ b/Minecraft.World/MineShaftStart.cpp @@ -0,0 +1,12 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.levelgen.structure.h" + +MineShaftStart::MineShaftStart(Level *level, Random *random, int chunkX, int chunkZ) +{ + MineShaftPieces::MineShaftRoom *mineShaftRoom = new MineShaftPieces::MineShaftRoom(0, random, (chunkX << 4) + 2, (chunkZ << 4) + 2); + pieces.push_back(mineShaftRoom); + mineShaftRoom->addChildren(mineShaftRoom, &pieces, random); + + calculateBoundingBox(); + moveBelowSeaLevel(level, random, 10); +} \ No newline at end of file diff --git a/Minecraft.World/MineShaftStart.h b/Minecraft.World/MineShaftStart.h new file mode 100644 index 00000000..b46e3d67 --- /dev/null +++ b/Minecraft.World/MineShaftStart.h @@ -0,0 +1,9 @@ +#pragma once + +#include "StructureStart.h" + +class MineShaftStart : public StructureStart +{ +public: + MineShaftStart(Level *level, Random *random, int chunkX, int chunkZ); +}; \ No newline at end of file diff --git a/Minecraft.World/Minecart.cpp b/Minecraft.World/Minecart.cpp new file mode 100644 index 00000000..2b8fbb95 --- /dev/null +++ b/Minecraft.World/Minecart.cpp @@ -0,0 +1,1211 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.damagesource.h" +#include "com.mojang.nbt.h" +#include "Minecart.h" +#include "SharedConstants.h" + + + +const int Minecart::EXITS[][2][3] = { // + // + { + { + +0, +0, -1 + }, { + +0, +0, +1 + } + }, // 0 + { + { + -1, +0, +0 + }, { + +1, +0, +0 + } + }, // 1 + { + { + -1, -1, +0 + }, { + +1, +0, +0 + } + }, // 2 + { + { + -1, +0, +0 + }, { + +1, -1, +0 + } + }, // 3 + { + { + +0, +0, -1 + }, { + +0, -1, +1 + } + }, // 4 + { + { + +0, -1, -1 + }, { + +0, +0, +1 + } + }, // 5 + + { + { + +0, +0, +1 + }, { + +1, +0, +0 + } + }, // 6 + { + { + +0, +0, +1 + }, { + -1, +0, +0 + } + }, // 7 + { + { + +0, +0, -1 + }, { + -1, +0, +0 + } + }, // 8 + { + { + +0, +0, -1 + }, { + +1, +0, +0 + } + }, // 9 +}; + +void Minecart::_init() +{ + // 4J TODO This gets replaced again later so should maybe be inited as NULL? + items = new ItemInstanceArray(9 * 4); + + flipped = false; + + type = fuel = 0; + xPush = zPush = 0.0; + + lSteps = 0; + lx = ly = lz = lyr = lxr = 0.0; + lxd = lyd = lzd = 0.0; + + // Java default ctor + blocksBuilding = true; + setSize(0.98f, 0.7f); + heightOffset = bbHeight / 2.0f; + // + + // 4J Added + m_bHasPushedCartThisTick = false; +} + +Minecart::Minecart(Level *level) : Entity( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + _init(); +} + + +bool Minecart::makeStepSound() +{ + return false; +} + +void Minecart::defineSynchedData() +{ + entityData->define(DATA_ID_FUEL, (byte) 0); + entityData->define(DATA_ID_HURT, 0); + entityData->define(DATA_ID_HURTDIR, 1); + entityData->define(DATA_ID_DAMAGE, 0); +} + + +AABB *Minecart::getCollideAgainstBox(shared_ptr entity) +{ + return entity->bb; +} + +AABB *Minecart::getCollideBox() +{ + // if (level->isClientSide) return NULL; + return NULL; +} + +bool Minecart::isPushable() +{ + return true; +} + +Minecart::Minecart(Level *level, double x, double y, double z, int type) : Entity( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + _init(); + setPos(x, y + heightOffset, z); + + xd = 0; + yd = 0; + zd = 0; + + xo = x; + yo = y; + zo = z; + this->type = type; +} + +double Minecart::getRideHeight() +{ + return bbHeight * 0.0 - 0.3f; +} + +bool Minecart::hurt(DamageSource *source, int hurtDamage) +{ + if (level->isClientSide || removed) return true; + + // 4J-JEV: Fix for #88212, + // Untrusted players shouldn't be able to damage minecarts or boats. + if (dynamic_cast(source) != NULL) + { + shared_ptr attacker = source->getDirectEntity(); + + if (dynamic_pointer_cast(attacker) != NULL && + !dynamic_pointer_cast(attacker)->isAllowedToHurtEntity( shared_from_this() )) + return false; + } + + setHurtDir(-getHurtDir()); + setHurtTime(10); + markHurt(); + + // 4J Stu - If someone is riding in this, then it can tick multiple times which causes the damage to + // decrease too quickly. So just make the damage a bit higher to start with for similar behaviour + // to an unridden one. Only do this change if the riding player is attacking it. + if( rider.lock() != NULL && rider.lock() == source->getEntity() ) hurtDamage += 1; + + // 4J Stu - Brought froward from 12w36 to fix #46611 - TU5: Gameplay: Minecarts and boat requires more hits than one to be destroyed in creative mode + shared_ptr player = dynamic_pointer_cast(source->getEntity()); + if (player != NULL && player->abilities.instabuild) this->setDamage(100); + + this->setDamage(getDamage() + (hurtDamage * 10)); + if (this->getDamage() > 20 * 2) + { + // 4J HEG - Fixed issue with player falling through the ground on destroying a minecart while riding (issue #160607) + if (rider.lock() != NULL) rider.lock()->ride(nullptr); + remove(); + + spawnAtLocation(Item::minecart->id, 1, 0); + if (type == Minecart::CHEST) + { + shared_ptr container = dynamic_pointer_cast( shared_from_this() ); + for (unsigned int i = 0; i < container->getContainerSize(); i++) + { + shared_ptr item = container->getItem(i); + if (item != NULL) + { + float xo = random->nextFloat() * 0.8f + 0.1f; + float yo = random->nextFloat() * 0.8f + 0.1f; + float zo = random->nextFloat() * 0.8f + 0.1f; + + while (item->count > 0) + { + int count = random->nextInt(21) + 10; + if (count > item->count) count = item->count; + item->count -= count; + + shared_ptr itemEntity = shared_ptr( new ItemEntity(level, x + xo, y + yo, z + zo, shared_ptr( new ItemInstance(item->id, count, item->getAuxValue()) ) ) ); + float pow = 0.05f; + itemEntity->xd = (float) random->nextGaussian() * pow; + itemEntity->yd = (float) random->nextGaussian() * pow + 0.2f; + itemEntity->zd = (float) random->nextGaussian() * pow; + if (item->hasTag()) + { + itemEntity->getItem()->setTag((CompoundTag *) item->getTag()->copy()); + } + level->addEntity(itemEntity); + } + } + } + spawnAtLocation(Tile::chest_Id, 1, 0); + } + else if (type == Minecart::FURNACE) + { + spawnAtLocation(Tile::furnace_Id, 1, 0); + } + } + return true; +} + +void Minecart::animateHurt() +{ + setHurtDir(-getHurtDir()); + setHurtTime(10); + this->setDamage(this->getDamage() + (getDamage() * 10)); +} + +bool Minecart::isPickable() +{ + return !removed; +} + +void Minecart::remove() +{ + for (unsigned int i = 0; i < getContainerSize(); i++) + { + shared_ptr item = getItem(i); + if (item != NULL) + { + float xo = random->nextFloat() * 0.8f + 0.1f; + float yo = random->nextFloat() * 0.8f + 0.1f; + float zo = random->nextFloat() * 0.8f + 0.1f; + + while (item->count > 0) + { + int count = random->nextInt(21) + 10; + if (count > item->count) count = item->count; + item->count -= count; + + shared_ptr itemEntity = shared_ptr( new ItemEntity(level, x + xo, y + yo, z + zo, shared_ptr( new ItemInstance(item->id, count, item->getAuxValue()) ) ) ); + float pow = 0.05f; + itemEntity->xd = (float) random->nextGaussian() * pow; + itemEntity->yd = (float) random->nextGaussian() * pow + 0.2f; + itemEntity->zd = (float) random->nextGaussian() * pow; + if (item->hasTag()) + { + itemEntity->getItem()->setTag((CompoundTag *) item->getTag()->copy()); + } + level->addEntity(itemEntity); + } + } + } + Entity::remove(); +} + + +void Minecart::tick() +{ + // 4J - make minecarts (server-side) tick twice, to put things back to how they were when we were accidently ticking them twice + for( int i = 0; i < 2; i++ ) + { + if (getHurtTime() > 0) setHurtTime(getHurtTime() - 1); + if (getDamage() > 0) setDamage(getDamage() - 1); + if(y < -64) + { + outOfWorld(); + } + + if (hasFuel() && random->nextInt(4) == 0) + { + level->addParticle(eParticleType_largesmoke, x, y + 0.8, z, 0, 0, 0); + } + + // 4J Stu - Fix for #8284 - Gameplay: Collision: Minecart clips into/ through blocks at the end of the track, prevents player from riding + if (level->isClientSide) // && lSteps > 0) + { + if (lSteps > 0) + { + double xt = x + (lx - x) / lSteps; + double yt = y + (ly - y) / lSteps; + double zt = z + (lz - z) / lSteps; + + double yrd = Mth::wrapDegrees(lyr - yRot); + + yRot += (float) ( (yrd) / lSteps ); + xRot += (float) ( (lxr - xRot) / lSteps ); + + lSteps--; + this->setPos(xt, yt, zt); + this->setRot(yRot, xRot); + } + else + { + this->setPos(x, y, z); + this->setRot(yRot, xRot); + } + + return; // 4J - return here stops the client-side version of this from ticking twice + } + xo = x; + yo = y; + zo = z; + + yd -= 0.04f; + + int xt = Mth::floor(x); + int yt = Mth::floor(y); + int zt = Mth::floor(z); + if (RailTile::isRail(level, xt, yt - 1, zt)) + { + yt--; + } + + double max = 0.4; + + double slideSpeed = 1 / 128.0; + int tile = level->getTile(xt, yt, zt); + if (RailTile::isRail(tile)) + { + Vec3 *oldPos = getPos(x, y, z); + int data = level->getData(xt, yt, zt); + y = yt; + + bool powerTrack = false; + bool haltTrack = false; + if (tile == Tile::goldenRail_Id) + { + powerTrack = (data & RailTile::RAIL_DATA_BIT) != 0; + haltTrack = !powerTrack; + } + if (((RailTile *) Tile::tiles[tile])->isUsesDataBit()) + { + data &= RailTile::RAIL_DIRECTION_MASK; + } + + if (data >= 2 && data <= 5) + { + y = yt + 1; + } + + if (data == 2) xd -= slideSpeed; + if (data == 3) xd += slideSpeed; + if (data == 4) zd += slideSpeed; + if (data == 5) zd -= slideSpeed; + + // 4J TODO Is this a good way to copy the bit of the array that we need? + int exits[2][3]; + memcpy( &exits, (void *)EXITS[data], sizeof(int) * 2 * 3); + //int exits[2][3] = EXITS[data]; + + double xD = exits[1][0] - exits[0][0]; + double zD = exits[1][2] - exits[0][2]; + double dd = sqrt(xD * xD + zD * zD); + + double flip = xd * xD + zd * zD; + if (flip < 0) + { + xD = -xD; + zD = -zD; + } + + double pow = sqrt(xd * xd + zd * zd); + + xd = pow * xD / dd; + zd = pow * zD / dd; + + shared_ptr sharedRider = rider.lock(); + if (sharedRider != NULL) + { + double riderDist = (sharedRider->xd * sharedRider->xd + sharedRider->zd * sharedRider->zd); + double ownDist = xd * xd + zd * zd; + + if (riderDist > 0.0001 && ownDist < 0.01) + { + xd += sharedRider->xd * 0.1; + zd += sharedRider->zd * 0.1; + + haltTrack = false; + } + } + + // on golden rails without power, stop the cart + if (haltTrack) + { + double speedLength = sqrt(xd * xd + zd * zd); + if (speedLength < 0.03) + { + xd *= 0; + yd *= 0; + zd *= 0; + } + else + { + xd *= 0.5f; + yd *= 0; + zd *= 0.5f; + } + } + + double progress = 0; + double x0 = xt + 0.5 + exits[0][0] * 0.5; + double z0 = zt + 0.5 + exits[0][2] * 0.5; + double x1 = xt + 0.5 + exits[1][0] * 0.5; + double z1 = zt + 0.5 + exits[1][2] * 0.5; + + xD = x1 - x0; + zD = z1 - z0; + + if (xD == 0) + { + x = xt + 0.5; + progress = z - zt; + } + else if (zD == 0) + { + z = zt + 0.5; + progress = x - xt; + } + else + { + + double xx = x - x0; + double zz = z - z0; + + progress = (xx * xD + zz * zD) * 2; + } + + x = x0 + xD * progress; + z = z0 + zD * progress; + + setPos(x, y + heightOffset, z); + + double xdd = xd; + double zdd = zd; + if (rider.lock() != NULL) + { + xdd *= 0.75; + zdd *= 0.75; + } + if (xdd < -max) xdd = -max; + if (xdd > +max) xdd = +max; + if (zdd < -max) zdd = -max; + if (zdd > +max) zdd = +max; + move(xdd, 0, zdd); + + if (exits[0][1] != 0 && Mth::floor(x) - xt == exits[0][0] && Mth::floor(z) - zt == exits[0][2]) + { + setPos(x, y + exits[0][1], z); + } + else if (exits[1][1] != 0 && Mth::floor(x) - xt == exits[1][0] && Mth::floor(z) - zt == exits[1][2]) + { + setPos(x, y + exits[1][1], z); + } + else + { + } + + if (rider.lock() != NULL) + { + xd *= 0.997f; + yd *= 0; + zd *= 0.997f; + } + else + { + if (type == Minecart::FURNACE) + { + double sd = xPush * xPush + zPush * zPush; + if (sd > 0.01 * 0.01) + { + sd = sqrt(sd); + xPush /= sd; + zPush /= sd; + double speed = 0.04; + xd *= 0.8f; + yd *= 0; + zd *= 0.8f; + xd += xPush * speed; + zd += zPush * speed; + } + else + { + xd *= 0.9f; + yd *= 0; + zd *= 0.9f; + } + } + xd *= 0.96f; + yd *= 0; + zd *= 0.96f; + + } + + Vec3 *newPos = getPos(x, y, z); + if (newPos != NULL && oldPos != NULL) + { + double speed = (oldPos->y - newPos->y) * 0.05; + + pow = sqrt(xd * xd + zd * zd); + if (pow > 0) + { + xd = xd / pow * (pow + speed); + zd = zd / pow * (pow + speed); + } + setPos(x, newPos->y, z); + } + + int xn = Mth::floor(x); + int zn = Mth::floor(z); + if (xn != xt || zn != zt) + { + pow = sqrt(xd * xd + zd * zd); + + xd = pow * (xn - xt); + zd = pow * (zn - zt); + } + + if (type == Minecart::FURNACE) + { + double sd = xPush * xPush + zPush * zPush; + if (sd > 0.01 * 0.01 && xd * xd + zd * zd > 0.001) + { + sd = sqrt(sd); + xPush /= sd; + zPush /= sd; + + if (xPush * xd + zPush * zd < 0) + { + xPush = 0; + zPush = 0; + } + else + { + xPush = xd; + zPush = zd; + } + } + } + + // if on golden rail with power, increase speed + if (powerTrack) + { + double speedLength = sqrt(xd * xd + zd * zd); + if (speedLength > .01) + { + double speed = 0.06; + xd += xd / speedLength * speed; + zd += zd / speedLength * speed; + } + else + { + // if the minecart is standing still, accelerate it away + // from potentional walls + if (data == RailTile::DIR_FLAT_X) + { + if (level->isSolidBlockingTile(xt - 1, yt, zt)) + { + xd = .02; + } + else if (level->isSolidBlockingTile(xt + 1, yt, zt)) + { + xd = -.02; + } + } + else if (data == RailTile::DIR_FLAT_Z) + { + if (level->isSolidBlockingTile(xt, yt, zt - 1)) + { + zd = .02; + } + else if (level->isSolidBlockingTile(xt, yt, zt + 1)) + { + zd = -.02; + } + } + } + } + + checkInsideTiles(); + } + else + { + if (xd < -max) xd = -max; + if (xd > +max) xd = +max; + if (zd < -max) zd = -max; + if (zd > +max) zd = +max; + if (onGround) + { + xd *= 0.5f; + yd *= 0.5f; + zd *= 0.5f; + } + move(xd, yd, zd); + + if (onGround) + { + } + else + { + xd *= 0.95f; + yd *= 0.95f; + zd *= 0.95f; + } + } + + xRot = 0; + double xDiff = xo - x; + double zDiff = zo - z; + if (xDiff * xDiff + zDiff * zDiff > 0.001) + { + yRot = (float) (atan2(zDiff, xDiff) * 180 / PI); + if (flipped) yRot += 180; + } + + double rotDiff = Mth::wrapDegrees(yRot - yRotO); + + if (rotDiff < -170 || rotDiff >= 170) + { + yRot += 180; + flipped = !flipped; + } + setRot(yRot, xRot); + + // if (!level->isClientSide) { + { + vector > *entities = level->getEntities(shared_from_this(), this->bb->grow(0.2f, 0, 0.2f)); + if (entities != NULL && !entities->empty()) + { + AUTO_VAR(itEnd, entities->end()); + for (AUTO_VAR(it, entities->begin()); it != itEnd; it++) + { + shared_ptr e = (*it); //entities->at(i); + if (e != rider.lock() && e->isPushable() && e->GetType() == eTYPE_MINECART) + { + shared_ptr cart = dynamic_pointer_cast(e); + cart->m_bHasPushedCartThisTick = false; + cart->push(shared_from_this()); + + // 4J Added - We should only be pushed by one minecart per tick, the closest one + // Fix for #46937 - TU5: Gameplay: Crash/Freeze occurs when a minecart with an animal inside will be forced to despawn + if( cart->m_bHasPushedCartThisTick ) break; + } + } + } + } + + if (rider.lock() != NULL) + { + if (rider.lock()->removed) + { + if (rider.lock()->riding == shared_from_this()) + { + rider.lock()->riding = nullptr; + } + rider = weak_ptr(); + } + } + + if (fuel > 0) + { + fuel--; + } + if (fuel <= 0) + { + xPush = zPush = 0; + } + setHasFuel(fuel > 0); + } +} + +Vec3 *Minecart::getPosOffs(double x, double y, double z, double offs) +{ + int xt = Mth::floor(x); + int yt = Mth::floor(y); + int zt = Mth::floor(z); + if (RailTile::isRail(level, xt, yt - 1, zt)) + { + yt--; + } + + int tile = level->getTile(xt, yt, zt); + if (RailTile::isRail(tile)) + { + int data = level->getData(xt, yt, zt); + + if (((RailTile *) Tile::tiles[tile])->isUsesDataBit()) + { + data &= RailTile::RAIL_DIRECTION_MASK; + } + + y = yt; + if (data >= 2 && data <= 5) + { + y = yt + 1; + } + + // 4J TODO Is this a good way to copy the bit of the array that we need? + int exits[2][3]; + memcpy( &exits, (void *)EXITS[data], sizeof(int) * 2 * 3); + //int exits[2][3] = EXITS[data]; + + double xD = exits[1][0] - exits[0][0]; + double zD = exits[1][2] - exits[0][2]; + double dd = sqrt(xD * xD + zD * zD); + xD /= dd; + zD /= dd; + + x += xD * offs; + z += zD * offs; + + if (exits[0][1] != 0 && Mth::floor(x) - xt == exits[0][0] && Mth::floor(z) - zt == exits[0][2]) + { + y += exits[0][1]; + } + else if (exits[1][1] != 0 && Mth::floor(x) - xt == exits[1][0] && Mth::floor(z) - zt == exits[1][2]) + { + y += exits[1][1]; + } + else + { + } + + return getPos(x, y, z); + } + return NULL; +} + +Vec3 *Minecart::getPos(double x, double y, double z) +{ + int xt = Mth::floor(x); + int yt = Mth::floor(y); + int zt = Mth::floor(z); + if (RailTile::isRail(level, xt, yt - 1, zt)) + { + yt--; + } + + int tile = level->getTile(xt, yt, zt); + if (RailTile::isRail(tile)) + { + int data = level->getData(xt, yt, zt); + y = yt; + + if (((RailTile *) Tile::tiles[tile])->isUsesDataBit()) + { + data &= RailTile::RAIL_DIRECTION_MASK; + } + + if (data >= 2 && data <= 5) + { + y = yt + 1; + } + + // 4J TODO Is this a good way to copy the bit of the array that we need? + int exits[2][3]; + memcpy( &exits, (void *)EXITS[data], sizeof(int) * 2 * 3); + //int exits[2][3] = EXITS[data]; + + double progress = 0; + double x0 = xt + 0.5 + exits[0][0] * 0.5; + double y0 = yt + 0.5 + exits[0][1] * 0.5; + double z0 = zt + 0.5 + exits[0][2] * 0.5; + double x1 = xt + 0.5 + exits[1][0] * 0.5; + double y1 = yt + 0.5 + exits[1][1] * 0.5; + double z1 = zt + 0.5 + exits[1][2] * 0.5; + + double xD = x1 - x0; + double yD = (y1 - y0) * 2; + double zD = z1 - z0; + + if (xD == 0) + { + x = xt + 0.5; + progress = z - zt; + } + else if (zD == 0) + { + z = zt + 0.5; + progress = x - xt; + } + else + { + + double xx = x - x0; + double zz = z - z0; + + progress = (xx * xD + zz * zD) * 2; + } + + x = x0 + xD * progress; + y = y0 + yD * progress; + z = z0 + zD * progress; + if (yD < 0) y += 1; + if (yD > 0) y += 0.5; + return Vec3::newTemp(x, y, z); + } + return NULL; +} + + +void Minecart::addAdditonalSaveData(CompoundTag *base) +{ + base->putInt(L"Type", type); + + if (type == Minecart::FURNACE) + { + base->putDouble(L"PushX", xPush); + base->putDouble(L"PushZ", zPush); + base->putShort(L"Fuel", (short) fuel); + } + else if (type == Minecart::CHEST) + { + ListTag *listTag = new ListTag(); + + for (unsigned int i = 0; i < items->length; i++) + { + if ( (*items)[i] != NULL) + { + CompoundTag *tag = new CompoundTag(); + tag->putByte(L"Slot", (byte) i); + (*items)[i]->save(tag); + listTag->add(tag); + } + } + base->put(L"Items", listTag); + } +} + +void Minecart::readAdditionalSaveData(CompoundTag *base) +{ + type = base->getInt(L"Type"); + if (type == Minecart::FURNACE) + { + xPush = base->getDouble(L"PushX"); + zPush = base->getDouble(L"PushZ"); + fuel = base->getShort(L"Fuel"); + } + else if (type == Minecart::CHEST) + { + ListTag *inventoryList = (ListTag *) base->getList(L"Items"); + items = new ItemInstanceArray( getContainerSize() ); + for (int i = 0; i < inventoryList->size(); i++) + { + CompoundTag *tag = inventoryList->get(i); + unsigned int slot = tag->getByte(L"Slot") & 0xff; + if (slot >= 0 && slot < items->length) (*items)[slot] = shared_ptr( ItemInstance::fromTag(tag) ); + } + } +} + + +float Minecart::getShadowHeightOffs() +{ + return 0; +} + +void Minecart::push(shared_ptr e) +{ + if (level->isClientSide) return; + + if (e == rider.lock()) return; + if (( dynamic_pointer_cast(e)!=NULL) && dynamic_pointer_cast(e)==NULL && dynamic_pointer_cast(e) == NULL && type == Minecart::RIDEABLE && xd * xd + zd * zd > 0.01) + { + if (rider.lock() == NULL && e->riding == NULL) + { + e->ride( shared_from_this() ); + } + } + + double xa = e->x - x; + double za = e->z - z; + + double dd = xa * xa + za * za; + if (dd >= 0.0001f) + { + dd = sqrt(dd); + xa /= dd; + za /= dd; + double pow = 1 / dd; + if (pow > 1) pow = 1; + xa *= pow; + za *= pow; + xa *= 0.1f; + za *= 0.1f; + + xa *= 1 - pushthrough; + za *= 1 - pushthrough; + xa *= 0.5; + za *= 0.5; + + if (e->GetType() == eTYPE_MINECART) + { + double xo = e->x - x; + double zo = e->z - z; + + //4J Stu - Brought forward changes to fix minecarts pushing each other + // Fix for #38882 - TU5: Gameplay: Minecart with furnace is not able to move another minecart on the rail. + Vec3 *dir = Vec3::newTemp(xo, 0, zo)->normalize(); + Vec3 *facing = Vec3::newTemp(cos(yRot * PI / 180), 0, sin(yRot * PI / 180))->normalize(); + + double dot = abs(dir->dot(facing)); + + if (dot < 0.8f) + { + return; + } + + double xdd = (e->xd + xd); + double zdd = (e->zd + zd); + + shared_ptr cart = dynamic_pointer_cast(e); + if (cart != NULL && cart->type == Minecart::FURNACE && type != Minecart::FURNACE) + { + xd *= 0.2f; + zd *= 0.2f; + this->Entity::push( e->xd - xa, 0, e->zd - za); + e->xd *= 0.95f; + e->zd *= 0.95f; + m_bHasPushedCartThisTick = true; + } + else if (cart != NULL && cart->type != Minecart::FURNACE && type == Minecart::FURNACE) + { + e->xd *= 0.2f; + e->zd *= 0.2f; + e->push(xd + xa, 0, zd + za); + xd *= 0.95f; + zd *= 0.95f; + m_bHasPushedCartThisTick = true; + } + else + { + xdd /= 2; + zdd /= 2; + xd *= 0.2f; + zd *= 0.2f; + this->Entity::push(xdd - xa, 0, zdd - za); + e->xd *= 0.2f; + e->zd *= 0.2f; + e->push(xdd + xa, 0, zdd + za); + m_bHasPushedCartThisTick = true; + + // 4J Stu - Fix for #46937 - TU5: Gameplay: Crash/Freeze occurs when a minecart with an animal inside will be forced to despawn + // Minecarts can end up stuck inside each other, so if they are too close then they should separate quickly + double modifier = 1.0; + if( abs(xo) < 1 && abs(zo) < 1) + { + modifier += 1-( (abs(xo) + abs(zo))/2); + } + // 4J Stu - Decelerate the cart that is pushing this one if they are too close + e->xd /= modifier; + e->zd /= modifier; + + // 4J Backup fix for QNAN + if( !(xd==xd) ) xd = 0; + if( !(zd==zd) ) zd = 0; + if( !(e->xd == e->xd) ) e->xd = 0; + if( !(e->zd == e->zd) ) e->zd = 0; + } + + } + else + { + this->Entity::push(-xa, 0, -za); + e->push(xa / 4, 0, za / 4); + } + } +} + +unsigned int Minecart::getContainerSize() +{ + return 9 * 3; +} + +shared_ptr Minecart::getItem(unsigned int slot) +{ + return (*items)[slot]; +} + +shared_ptr Minecart::removeItem(unsigned int slot, int count) +{ + if ( (*items)[slot] != NULL) + { + if ( (*items)[slot]->count <= count) + { + shared_ptr item = (*items)[slot]; + (*items)[slot] = nullptr; + return item; + } + else + { + shared_ptr i = (*items)[slot]->remove(count); + if ((*items)[slot]->count == 0) (*items)[slot] = nullptr; + return i; + } + } + return nullptr; +} + +shared_ptr Minecart::removeItemNoUpdate(int slot) +{ + if ( (*items)[slot] != NULL) + { + shared_ptr item = (*items)[slot]; + (*items)[slot] = nullptr; + return item; + } + return nullptr; +} + +void Minecart::setItem(unsigned int slot, shared_ptr item) +{ + (*items)[slot] = item; + if (item != NULL && item->count > getMaxStackSize()) item->count = getMaxStackSize(); +} + +int Minecart::getName() +{ + return IDS_ITEM_MINECART; +} + +int Minecart::getMaxStackSize() +{ + return Container::LARGE_MAX_STACK_SIZE; +} + +void Minecart::setChanged() +{ +} + +bool Minecart::interact(shared_ptr player) +{ + if (type == Minecart::RIDEABLE) + { + if (rider.lock() != NULL && dynamic_pointer_cast(rider.lock())!=NULL && rider.lock() != player) return true; + if (!level->isClientSide) + { + // 4J HEG - Fixed issue with player not being able to dismount minecart (issue #4455) + player->ride( rider.lock() == player ? nullptr : shared_from_this() ); + } + } + else if (type == Minecart::CHEST) + { + if ( player->isAllowedToInteract(shared_from_this()) ) + { + if (!level->isClientSide) + player->openContainer( dynamic_pointer_cast( shared_from_this() ) ); + } + else + { + return false; + } + } + else if (type == Minecart::FURNACE) + { + shared_ptr selected = player->inventory->getSelected(); + if (selected != NULL && selected->id == Item::coal->id) + { + if (--selected->count == 0) player->inventory->setItem(player->inventory->selected, nullptr); + fuel += SharedConstants::TICKS_PER_SECOND * 180; + + } + xPush = x - player->x; + zPush = z - player->z; + } + return true; +} + +float Minecart::getLootContent() +{ + int count = 0; + for (unsigned int i = 0; i < items->length; i++) + { + if ( (*items)[i] != NULL) count++; + } + return count / (float) items->length; +} + + +void Minecart::lerpTo(double x, double y, double z, float yRot, float xRot, int steps) +{ + lx = x; + ly = y; + lz = z; + lyr = yRot; + lxr = xRot; + + lSteps = steps + 2; + + this->xd = lxd; + this->yd = lyd; + this->zd = lzd; +} + +void Minecart::lerpMotion(double xd, double yd, double zd) +{ + lxd = this->xd = xd; + lyd = this->yd = yd; + lzd = this->zd = zd; +} + +bool Minecart::stillValid(shared_ptr player) +{ + if (this->removed) return false; + if (player->distanceToSqr(shared_from_this()) > 8 * 8) return false; + return true; +} + +bool Minecart::hasFuel() +{ + return (entityData->getByte(DATA_ID_FUEL) & 1) != 0; +} + +void Minecart::setHasFuel(bool fuel) +{ + if (fuel) + { + entityData->set(DATA_ID_FUEL, (byte) (entityData->getByte(DATA_ID_FUEL) | 1)); + } + else + { + entityData->set(DATA_ID_FUEL, (byte) (entityData->getByte(DATA_ID_FUEL) & ~1)); + } +} + +void Minecart::startOpen() +{ + // TODO Auto-generated method stub + +} + +void Minecart::stopOpen() +{ + // TODO Auto-generated method stub + +} + +void Minecart::setDamage(int damage) +{ + entityData->set(DATA_ID_DAMAGE, damage); +} + +int Minecart::getDamage() +{ + return entityData->getInteger(DATA_ID_DAMAGE); +} + +void Minecart::setHurtTime(int hurtTime) +{ + entityData->set(DATA_ID_HURT, hurtTime); +} + +int Minecart::getHurtTime() +{ + return entityData->getInteger(DATA_ID_HURT); +} + +void Minecart::setHurtDir(int hurtDir) +{ + entityData->set(DATA_ID_HURTDIR, hurtDir); +} + +int Minecart::getHurtDir() +{ + return entityData->getInteger(DATA_ID_HURTDIR); +} \ No newline at end of file diff --git a/Minecraft.World/Minecart.h b/Minecraft.World/Minecart.h new file mode 100644 index 00000000..ea119bce --- /dev/null +++ b/Minecraft.World/Minecart.h @@ -0,0 +1,114 @@ +#pragma once +#include "Entity.h" +#include "Container.h" + +class DamageSource; + +class Minecart : public Entity, public Container +{ +public: + eINSTANCEOF GetType() { return eTYPE_MINECART; }; + static Entity *create(Level *level) { return new Minecart(level); } + +public: + static const int RIDEABLE = 0; + static const int CHEST = 1; + static const int FURNACE = 2; + +private: + ItemInstanceArray *items; // Array + +public: + static const int serialVersionUID = 0; + +private: + static const int DATA_ID_FUEL = 16; + static const int DATA_ID_HURT = 17; + static const int DATA_ID_HURTDIR = 18; + static const int DATA_ID_DAMAGE = 19; + + int fuel; + +private: + bool flipped; + +protected: + // 4J Added + bool m_bHasPushedCartThisTick; + +public: + int type; + double xPush, zPush; + + void _init(); + + Minecart(Level *level); + +protected: + virtual bool makeStepSound(); + virtual void defineSynchedData(); + +public: + virtual AABB *getCollideAgainstBox(shared_ptr entity); + virtual AABB *getCollideBox(); + virtual bool isPushable(); + + Minecart(Level *level, double x, double y, double z, int type); + + virtual double getRideHeight(); + virtual bool hurt(DamageSource *source, int damage); + virtual void animateHurt(); + virtual bool isPickable(); + virtual void remove(); + +private: + static const int EXITS[][2][3]; + +public: + virtual void tick(); + virtual Vec3 *getPosOffs(double x, double y, double z, double offs); + virtual Vec3 *getPos(double x, double y, double z); + +protected: + virtual void addAdditonalSaveData(CompoundTag *base); + virtual void readAdditionalSaveData(CompoundTag *base); + +public: + virtual float getShadowHeightOffs(); + virtual void push(shared_ptr e); + virtual unsigned int getContainerSize(); + virtual shared_ptr getItem(unsigned int slot); + virtual shared_ptr removeItem(unsigned int slot, int count); + virtual shared_ptr removeItemNoUpdate(int slot); + virtual void setItem(unsigned int slot, shared_ptr item); + int getName(); + virtual int getMaxStackSize(); + virtual void setChanged(); + virtual bool interact(shared_ptr player); + virtual float getLootContent(); + +private: + int lSteps; + double lx, ly, lz, lyr, lxr; + double lxd, lyd, lzd; + +public: + virtual void lerpTo(double x, double y, double z, float yRot, float xRot, int steps); + virtual void lerpMotion(double xd, double yd, double zd); + virtual bool stillValid(shared_ptr player); + +protected: + bool hasFuel(); + void setHasFuel(bool fuel); + +public: + virtual void startOpen(); + virtual void stopOpen(); + + void setDamage(int damage); + int getDamage(); + void setHurtTime(int hurtTime); + int getHurtTime(); + void setHurtDir(int hurtDir); + int getHurtDir(); +}; diff --git a/Minecraft.World/MinecartItem.cpp b/Minecraft.World/MinecartItem.cpp new file mode 100644 index 00000000..3b062ccf --- /dev/null +++ b/Minecraft.World/MinecartItem.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "ItemInstance.h" +#include "MinecartItem.h" + +MinecartItem::MinecartItem(int id, int type) : Item(id) +{ + this->maxStackSize = 1; + this->type = type; +} + +bool MinecartItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + // 4J-PB - Adding a test only version to allow tooltips to be displayed + int targetType = level->getTile(x, y, z); + + if (RailTile::isRail(targetType)) + { + if(!bTestUseOnOnly) + { + if (!level->isClientSide) + { + level->addEntity(shared_ptr( new Minecart(level, x + 0.5f, y + 0.5f, z + 0.5f, type) ) ); + } + instance->count--; + } + return true; + } + return false; +} \ No newline at end of file diff --git a/Minecraft.World/MinecartItem.h b/Minecraft.World/MinecartItem.h new file mode 100644 index 00000000..c63082a1 --- /dev/null +++ b/Minecraft.World/MinecartItem.h @@ -0,0 +1,14 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class MinecartItem : public Item +{ +public: + int type; + + MinecartItem(int id, int type); + + virtual bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); +}; \ No newline at end of file diff --git a/Minecraft.World/Minecraft.World.cpp b/Minecraft.World/Minecraft.World.cpp new file mode 100644 index 00000000..30495443 --- /dev/null +++ b/Minecraft.World/Minecraft.World.cpp @@ -0,0 +1,104 @@ +#include "stdafx.h" + +#include "Packet.h" +#include "MaterialColor.h" +#include "Material.h" +#include "Tile.h" +#include "HatchetItem.h" +#include "PickaxeItem.h" +#include "ShovelItem.h" +#include "BlockReplacements.h" +#include "Biome.h" +#include "Item.h" +#include "FurnaceRecipes.h" +#include "Recipes.h" +#include "Stats.h" +#include "Achievements.h" +#include "Skeleton.h" +#include "PigZombie.h" +#include "TileEntity.h" +#include "EntityIO.h" +#include "SharedConstants.h" +#include "MobCategory.h" +#include "LevelChunk.h" +#include "MineShaftPieces.h" +#include "StrongholdFeature.h" +#include "VillageFeature.h" +#include "LevelType.h" +#include "EnderMan.h" +#include "PotionBrewing.h" +#include "Enchantment.h" +#include "VillagePieces.h" +#include "RandomScatteredLargeFeature.h" + +#include "Minecraft.World.h" +#include "..\Minecraft.Client\ServerLevel.h" +#include "SparseLightStorage.h" +#include "SparseDataStorage.h" +#include "McRegionChunkStorage.h" +#include "Villager.h" +#include "LevelSettings.h" + +#ifdef _DURANGO +#include "DurangoStats.h" +#else +#include "CommonStats.h" +#endif + +void MinecraftWorld_RunStaticCtors() +{ + // The ordering of these static ctors can be important. If they are within statement blocks then + // DO NOT CHANGE the ordering - 4J Stu + + Packet::staticCtor(); + + { + MaterialColor::staticCtor(); + Material::staticCtor(); + Tile::staticCtor(); + HatchetItem::staticCtor(); + PickaxeItem::staticCtor(); + ShovelItem::staticCtor(); + BlockReplacements::staticCtor(); + Biome::staticCtor(); + Item::staticCtor(); + FurnaceRecipes::staticCtor(); + Recipes::staticCtor(); +#ifdef _DURANGO + GenericStats::setInstance(new DurangoStats()); +#else + GenericStats::setInstance(new CommonStats()); + Stats::staticCtor(); +#endif + //Achievements::staticCtor(); // 4J Stu - This is now called from within the Stats::staticCtor() + Skeleton::staticCtor(); + PigZombie::staticCtor(); + TileEntity::staticCtor(); + EntityIO::staticCtor(); + MobCategory::staticCtor(); + + Item::staticInit(); + LevelChunk::staticCtor(); + + LevelType::staticCtor(); + + MineShaftPieces::staticCtor(); + StrongholdFeature::staticCtor(); + VillagePieces::Smithy::staticCtor(); + VillageFeature::staticCtor(); + RandomScatteredLargeFeature::staticCtor(); + } + EnderMan::staticCtor(); + PotionBrewing::staticCtor(); + Enchantment::staticCtor(); + + SharedConstants::staticCtor(); + + ServerLevel::staticCtor(); + SparseLightStorage::staticCtor(); + CompressedTileStorage::staticCtor(); + SparseDataStorage::staticCtor(); + McRegionChunkStorage::staticCtor(); + Villager::staticCtor(); + GameType::staticCtor(); +} diff --git a/Minecraft.World/Minecraft.World.h b/Minecraft.World/Minecraft.World.h new file mode 100644 index 00000000..e9abeec1 --- /dev/null +++ b/Minecraft.World/Minecraft.World.h @@ -0,0 +1,3 @@ +#pragma once + +void MinecraftWorld_RunStaticCtors(); \ No newline at end of file diff --git a/Minecraft.World/Minecraft.World.vcxproj b/Minecraft.World/Minecraft.World.vcxproj new file mode 100644 index 00000000..5146b56c --- /dev/null +++ b/Minecraft.World/Minecraft.World.vcxproj @@ -0,0 +1,3969 @@ + + + + + ContentPackage_NO_TU + Durango + + + ContentPackage_NO_TU + ORBIS + + + ContentPackage_NO_TU + PS3 + + + ContentPackage_NO_TU + PSVita + + + ContentPackage_NO_TU + x64 + + + ContentPackage_NO_TU + Xbox 360 + + + CONTENTPACKAGE_SYMBOLS + Durango + + + CONTENTPACKAGE_SYMBOLS + ORBIS + + + CONTENTPACKAGE_SYMBOLS + PS3 + + + CONTENTPACKAGE_SYMBOLS + PSVita + + + CONTENTPACKAGE_SYMBOLS + x64 + + + CONTENTPACKAGE_SYMBOLS + Xbox 360 + + + ContentPackage_Vita + Durango + + + ContentPackage_Vita + ORBIS + + + ContentPackage_Vita + PS3 + + + ContentPackage_Vita + PSVita + + + ContentPackage_Vita + x64 + + + ContentPackage_Vita + Xbox 360 + + + ContentPackage + Durango + + + ContentPackage + ORBIS + + + ContentPackage + PS3 + + + ContentPackage + PSVita + + + ContentPackage + x64 + + + ContentPackage + Xbox 360 + + + Debug + Durango + + + Debug + ORBIS + + + Debug + PS3 + + + Debug + PSVita + + + Debug + x64 + + + Debug + Xbox 360 + + + ReleaseForArt + Durango + + + ReleaseForArt + ORBIS + + + ReleaseForArt + PS3 + + + ReleaseForArt + PSVita + + + ReleaseForArt + x64 + + + ReleaseForArt + Xbox 360 + + + Release + Durango + + + Release + ORBIS + + + Release + PS3 + + + Release + PSVita + + + Release + x64 + + + Release + Xbox 360 + + + + en-US + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8} + Xbox360Proj + SAK + SAK + SAK + SAK + title + 10.0 + + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + WithExceptsWithRtti + + + StaticLibrary + MultiByte + WithExceptsWithRtti + + + StaticLibrary + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + StaticLibrary + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + StaticLibrary + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + StaticLibrary + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + StaticLibrary + MultiByte + v143 + 10.0 + + + StaticLibrary + Unicode + v143 + + + StaticLibrary + MultiByte + v143 + 10.0 + + + StaticLibrary + MultiByte + v143 + + + StaticLibrary + Unicode + v143 + + + StaticLibrary + Unicode + v143 + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + StaticLibrary + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + StaticLibrary + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + StaticLibrary + MultiByte + WithExceptsWithRtti + NoTocRestore2 + + + StaticLibrary + MultiByte + WithExceptsWithRtti + + + StaticLibrary + MultiByte + WithExceptsWithRtti + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + + + StaticLibrary + MultiByte + v143 + + + StaticLibrary + MultiByte + v143 + + + StaticLibrary + MultiByte + v143 + + + StaticLibrary + MultiByte + v143 + + + StaticLibrary + Unicode + v143 + + + StaticLibrary + MultiByte + v143 + + + StaticLibrary + MultiByte + v143 + + + StaticLibrary + MultiByte + v143 + + + StaticLibrary + MultiByte + v110 + + + Clang + StaticLibrary + + + Clang + StaticLibrary + + + Clang + StaticLibrary + + + Clang + StaticLibrary + + + Clang + StaticLibrary + + + Clang + StaticLibrary + + + Clang + StaticLibrary + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(OutDir)$(ProjectName).lib + + + $(OutDir)$(ProjectName).lib + + + $(OutDir)$(ProjectName).lib + + + $(OutDir)$(ProjectName).lib + $(SCE_PSP2_SDK_DIR)/target\src\npToolkit\include\np_toolkit;$(ProjectDir)\..\Minecraft.Client\PS3\Assert;$(SCE_PS3_ROOT)\target\ppu\include;$(SCE_PS3_ROOT)\target\common\include;$(SCE_PS3_ROOT)\host-win32\sn\ppu\include;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0\boost\tr1\tr1;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0;$(ProjectDir)..\Minecraft.World\x64headers + + + $(OutDir)$(ProjectName).lib + $(SCE_PSP2_SDK_DIR)/target\src\npToolkit\include;$(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras;$(ProjectDir)..\Minecraft.World\x64headers;$(IncludePath) + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\..\Minecraft.Client\PS3\Assert;$(SCE_PS3_ROOT)\target\ppu\include;$(SCE_PS3_ROOT)\target\common\include;$(SCE_PS3_ROOT)\host-win32\sn\ppu\include;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0\boost\tr1\tr1;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0;$(ProjectDir)..\Minecraft.World\x64headers + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\..\Minecraft.Client\PS3\Assert;$(SCE_PS3_ROOT)\target\ppu\include;$(SCE_PS3_ROOT)\target\common\include;$(SCE_PS3_ROOT)\host-win32\sn\ppu\include;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0\boost\tr1\tr1;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0;$(ProjectDir)..\Minecraft.World\x64headers + + + $(OutDir)$(ProjectName).lib + $(SCE_PSP2_SDK_DIR)/target\src\npToolkit\include;$(MINECRAFT_CONSOLES_DIR)\Minecraft.Client\PSVita\Assert;$(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras;$(ProjectDir)..\Minecraft.World\x64headers;$(IncludePath) + + + $(OutDir)$(ProjectName).lib + $(SCE_PSP2_SDK_DIR)/target\src\npToolkit\include;$(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras;$(ProjectDir)..\Minecraft.World\x64headers;$(IncludePath) + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\x64headers;$(IncludePath) + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)..\Minecraft.Client\Durango\DurangoExtras;$(ProjectDir)\x64headers;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)\bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\x64headers;$(IncludePath) + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\x64headers;$(IncludePath) + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)..\Minecraft.Client\Durango\DurangoExtras;$(ProjectDir)\x64headers;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)\bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)..\Minecraft.Client\Durango\DurangoExtras;$(ProjectDir)\x64headers;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)\bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + + + $(OutDir)$(ProjectName).lib + + + $(OutDir)$(ProjectName).lib + + + $(OutDir)$(ProjectName).lib + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\..\Minecraft.Client\PS3\Assert;$(SCE_PS3_ROOT)\target\ppu\include;$(SCE_PS3_ROOT)\target\common\include;$(SCE_PS3_ROOT)\host-win32\sn\ppu\include;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0\boost\tr1\tr1;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0;$(ProjectDir)..\Minecraft.World\x64headers + + + $(OutDir)$(ProjectName).lib + $(SCE_PSP2_SDK_DIR)/target\src\npToolkit\include;$(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras;$(ProjectDir)..\Minecraft.World\x64headers;$(IncludePath) + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\..\Minecraft.Client\PS3\Assert;$(SCE_PS3_ROOT)\target\ppu\include;$(SCE_PS3_ROOT)\target\common\include;$(SCE_PS3_ROOT)\host-win32\sn\ppu\include;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0\boost\tr1\tr1;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0;$(ProjectDir)..\Minecraft.World\x64headers + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras;$(ProjectDir)..\Minecraft.World\x64headers;$(IncludePath) + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\..\Minecraft.Client\PS3\Assert;$(SCE_PS3_ROOT)\target\ppu\include;$(SCE_PS3_ROOT)\target\common\include;$(SCE_PS3_ROOT)\host-win32\sn\ppu\include;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0\boost\tr1\tr1;$(ProjectDir)..\Minecraft.Client\PS3\PS3Extras\boost_1_53_0;$(ProjectDir)..\Minecraft.World\x64headers + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras;$(ProjectDir)..\Minecraft.World\x64headers;$(IncludePath) + + + $(OutDir)$(ProjectName).lib + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\..\Minecraft.Client\PSVita\PSVitaExtras;$(ProjectDir)..\Minecraft.World\x64headers;$(IncludePath) + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\x64header;$(DXSDK_DIR)include;$(IncludePath) + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\x64header;$(DXSDK_DIR)include;$(IncludePath) + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\x64header;$(DXSDK_DIR)include;$(IncludePath) + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\x64header;$(DXSDK_DIR)include;$(IncludePath) + $(ProjectDir)\$(Configuration)\ + $(ProjectDir)\$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\x64headers;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)\bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\x64headers;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)\bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\x64headers;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)\bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\x64headers;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)\bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(ProjectDir)\$(Configuration)\ + $(ProjectDir)\$(Configuration)\ + + + $(OutDir)$(ProjectName).lib + $(ProjectDir)\x64headers;$(Console_SdkIncludeRoot) + $(Console_SdkRoot)\bin;$(VCInstallDir)bin\x86_amd64;$(VCInstallDir)bin;$(WindowsSDK_ExecutablePath_x86);$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(MSBuildToolsPath32);$(FxCopDir);$(PATH); + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(Console_SdkLibPath) + $(Console_SdkLibPath);$(Console_SdkWindowsMetadataPath) + $(ProjectDir)\$(Configuration)\ + $(ProjectDir)\$(Configuration)\ + + + $(ProjectDir);$(ProjectDir)..\Minecraft.Client\Orbis\OrbisExtras;$(ProjectDir)\x64headers;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common + + + $(ProjectDir);$(ProjectDir)..\Minecraft.Client\Orbis\OrbisExtras;$(ProjectDir)\x64headers;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common + + + $(ProjectDir);$(ProjectDir)..\Minecraft.Client\Orbis\OrbisExtras;$(ProjectDir)\x64headers;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common + .a + $(Platform)_$(Configuration)\ + + + $(ProjectDir);$(ProjectDir)..\Minecraft.Client\Orbis\OrbisExtras;$(ProjectDir)\x64headers;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common + + + $(ProjectDir);$(ProjectDir)..\Minecraft.Client\Orbis\OrbisExtras;$(ProjectDir)\x64headers;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common + $(Platform)_$(Configuration)\ + + + $(ProjectDir);$(ProjectDir)..\Minecraft.Client\Orbis\OrbisExtras;$(ProjectDir)\x64headers;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common + .a + $(Platform)_$(Configuration)\ + + + $(ProjectDir);$(ProjectDir)..\Minecraft.Client\Orbis\OrbisExtras;$(ProjectDir)\x64headers;$(SCE_ORBIS_SDK_DIR)\host_tools\lib\clang\include;$(SCE_ORBIS_SDK_DIR)\target\include;$(SCE_ORBIS_SDK_DIR)\target\include_common + .a + $(Platform)_$(Configuration)\ + + + + Use + Level3 + ProgramDatabase + Disabled + false + false + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_DEBUG;_XBOX;_LIB;%(PreprocessorDefinitions) + Disabled + true + false + false + true + Default + + + true + + + + + Use + Level3 + ProgramDatabase + Full + false + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _DEBUG_MENUS_ENABLED;NDEBUG;_XBOX;_LIB;%(PreprocessorDefinitions);PROFILE + Disabled + true + false + false + true + Default + Speed + true + true + + + true + + + + + Use + Level3 + ProgramDatabase + Full + false + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _DEBUG_MENUS_ENABLED;NDEBUG;_XBOX;_LIB;%(PreprocessorDefinitions);PROFILE + Disabled + true + false + false + true + Default + Speed + true + true + + + true + + + + + Use + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_DEBUG;_LIB;%(PreprocessorDefinitions) + Disabled + true + false + false + true + Default + GenerateWarnings + true + true + Leveld + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + + + true + + + + + Use + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_DEBUG;_LIB;__PSVITA__;%(PreprocessorDefinitions) + Disabled + true + false + false + true + Default + GenerateWarnings + true + true + Level0 + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + Cpp11 + + + true + + + + + Use + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_LIB;%(PreprocessorDefinitions) + Disabled + true + false + false + true + Default + GenerateWarnings + true + true + Levels + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + Branchless2 + true + Yes + + + true + + + + + Use + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_LIB;%(PreprocessorDefinitions) + Disabled + true + false + false + true + Default + GenerateWarnings + true + true + Levels + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + Branchless2 + true + Yes + + + true + + + + + Use + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_LIB;__PSVITA__;%(PreprocessorDefinitions) + Disabled + true + false + false + true + Default + GenerateWarnings + true + true + Levels + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + Branchless2 + true + Yes + Cpp11 + + + true + + + + + Use + Level3 + ProgramDatabase + Disabled + false + true + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _EXTENDED_ACHIEVEMENTS;_CONTENT_PACKAGE;_FINAL_BUILD;_LIB;__PSVITA__;%(PreprocessorDefinitions) + Disabled + true + false + false + true + Default + GenerateWarnings + true + false + Level3 + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + Branchless2 + false + Yes + Cpp11 + true + + + true + + + + + Use + Level3 + ProgramDatabase + Disabled + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreadedDebug + _LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_DEBUG;_LIB;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;%(PreprocessorDefinitions) + Disabled + true + false + false + true + Default + + + true + + + + + Use + Level3 + ProgramDatabase + Disabled + Sync + true + $(OutDir)$(ProjectName).pch + MultiThreadedDebugDLL + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;UNICODE;_UNICODE;__WRL_NO_DEFAULT_LIB__;WINAPI_FAMILY=WINAPI_FAMILY_TV_TITLE;WIN32_LEAN_AND_MEAN;_XM_AVX_INTRINSICS_;_DEBUG_MENUS_ENABLED;_DEBUG;_LIB;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_DURANGO;%(PreprocessorDefinitions) + Disabled + true + false + false + true + EnableFastChecks + true + false + true + + + true + + + /ignore:4264 + + + + + Use + Level3 + ProgramDatabase + Full + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_LIB;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;%(PreprocessorDefinitions) + Disabled + true + false + false + true + Default + Speed + + + true + + + + + Use + Level3 + ProgramDatabase + Full + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_LIB;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;%(PreprocessorDefinitions) + Disabled + true + false + false + true + Default + Speed + + + true + + + + + Use + Level3 + ProgramDatabase + Full + Sync + true + $(OutDir)$(ProjectName).pch + MultiThreadedDLL + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;UNICODE;_UNICODE;__WRL_NO_DEFAULT_LIB__;WINAPI_FAMILY=WINAPI_FAMILY_TV_TITLE;WIN32_LEAN_AND_MEAN;_XM_AVX_INTRINSICS_;_DEBUG_MENUS_ENABLED;_LIB;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_DURANGO;USE_PIX;%(PreprocessorDefinitions) + Disabled + true + false + false + true + Default + Speed + true + false + + + true + + + /ignore:4264 + + + + + Use + Level3 + ProgramDatabase + Full + Sync + true + $(OutDir)$(ProjectName).pch + MultiThreadedDLL + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;UNICODE;_UNICODE;__WRL_NO_DEFAULT_LIB__;WINAPI_FAMILY=WINAPI_FAMILY_TV_TITLE;WIN32_LEAN_AND_MEAN;_XM_AVX_INTRINSICS_;_DEBUG_MENUS_ENABLED;_LIB;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_DURANGO;USE_PIX;%(PreprocessorDefinitions) + Disabled + true + false + false + true + Default + Speed + true + false + + + true + + + /ignore:4264 + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;NDEBUG;_XBOX;_LIB;_CONTENT_PACKAGE;%(PreprocessorDefinitions) + true + Default + true + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;NDEBUG;_XBOX;_LIB;_CONTENT_PACKAGE;%(PreprocessorDefinitions) + true + Default + true + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;NDEBUG;_XBOX;_LIB;_CONTENT_PACKAGE;%(PreprocessorDefinitions) + true + Default + true + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + false + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _FINAL_BUILD;_CONTENT_PACKAGE;NDEBUG;_XBOX;_LIB;%(PreprocessorDefinitions) + true + Default + true + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _CONTENT_PACKAGE;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_LIB;%(PreprocessorDefinitions) + true + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + true + Levels + Branchless2 + true + Yes + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _EXTENDED_ACHIEVEMENTS;_CONTENT_PACKAGE;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_LIB;__PSVITA__;%(PreprocessorDefinitions) + true + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + true + Level3 + Branchless2 + true + Yes + true + Cpp11 + true + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _CONTENT_PACKAGE;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_LIB;%(PreprocessorDefinitions) + true + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + true + Levels + Branchless2 + true + Yes + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _EXTENDED_ACHIEVEMENTS;_CONTENT_PACKAGE;_FINAL_BUILD;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_LIB;__PSVITA__;%(PreprocessorDefinitions) + true + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + true + Levels + Branchless2 + true + Yes + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_LIB;%(PreprocessorDefinitions) + true + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + true + Level2 + Branchless2 + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED;_ITERATOR_DEBUG_LEVEL=0;_SECURE_SCL=0;_LIB;__PSVITA__;%(PreprocessorDefinitions) + true + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + true + Level2 + Branchless2 + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;NDEBUG;_XBOX;_LIB;_CONTENT_PACKAGE;%(PreprocessorDefinitions) + true + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _EXTENDED_ACHIEVEMENTS;_TU_BUILD;_FINAL_BUILD;NDEBUG;_LIB;_CONTENT_PACKAGE;__PSVITA__;%(PreprocessorDefinitions) + true + Default + 1700;613;1011 + -Xpch_override=1 %(AdditionalOptions) + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;NDEBUG;_XBOX;_LIB;_CONTENT_PACKAGE;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;NDEBUG;_XBOX;_LIB;_CONTENT_PACKAGE;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;NDEBUG;_XBOX;_LIB;_CONTENT_PACKAGE;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;NDEBUG;_XBOX;_LIB;_CONTENT_PACKAGE;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + + + true + true + true + + + + + Level3 + Use + MaxSpeed + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreadedDLL + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_FINAL_BUILD;_CONTENT_PACKAGE;NDEBUG;__WRL_NO_DEFAULT_LIB__;_XM_AVX_INTRINSICS_;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + true + true + + + true + true + true + + + /ignore:4264 + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;NDEBUG;_XBOX;_LIB;_CONTENT_PACKAGE;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;NDEBUG;_XBOX;_LIB;_CONTENT_PACKAGE;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;NDEBUG;_XBOX;_LIB;_CONTENT_PACKAGE;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + + + true + true + true + + + + + Level3 + Use + Full + true + true + ProgramDatabase + Speed + Sync + false + $(OutDir)$(ProjectName).pch + MultiThreaded + _TU_BUILD;_FINAL_BUILD;NDEBUG;_XBOX;_LIB;_CONTENT_PACKAGE;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + Default + + + true + true + true + + + + + WarningsOff + true + Use + true + true + true + Level2 + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED + + + + + WarningsOff + true + Use + true + true + true + Level2 + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED + + + + + Use + true + true + Level3 + true + true + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_CONTENT_PACKAGE;_FINAL_BUILD + false + + + + + Use + true + true + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_CONTENT_PACKAGE;_FINAL_BUILD + Level3 + true + true + + + + + Use + true + true + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED + WarningsOff + Levels + true + + + StripSymsAndDebug + + + None + + + + + Use + true + true + + + + + Use + true + true + WarningsOff + true + true + SPLIT_SAVES;_LARGE_WORLDS;_EXTENDED_ACHIEVEMENTS;_DEBUG_MENUS_ENABLED;_DEBUG + + + + + Document + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + Level0 + + + + + + + false + false + false + false + false + false + false + false + false + false + false + false + false + false + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + false + false + false + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + false + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + Create + false + false + false + false + false + false + Create + Create + Create + Create + Create + Create + Create + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + $(OutDir)$(ProjectName).pch + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + true + + + + + + + + + + \ No newline at end of file diff --git a/Minecraft.World/Minecraft.World.vcxproj.filters b/Minecraft.World/Minecraft.World.vcxproj.filters new file mode 100644 index 00000000..eb5e4a8c --- /dev/null +++ b/Minecraft.World/Minecraft.World.vcxproj.filters @@ -0,0 +1,4927 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {4b57b3e7-79bd-45d2-8f8b-08e51f79acc3} + + + {44b10ccd-9af5-4e82-9c77-2a7086f5b169} + + + {b8500899-1376-467c-ae8f-a3ae60e2b656} + + + {1c0c3eb4-2256-4920-9260-1058bd03f75f} + + + {c33f2cde-ee86-40f7-bbaf-34a214bd9c6d} + + + {59b2ed9d-8246-4171-9614-3ad9855e2813} + + + {48df29f2-1a5a-4421-aa02-45cdccb5259f} + + + {0ec66926-fc94-4498-bd1c-16f8196ae2dc} + + + {114ebac9-248f-4be2-9345-b9100079d63d} + + + {0a1baf2e-2c84-488f-afd0-f5ddba3ac849} + + + {bf44f635-1f68-4c61-a946-acc2cca87955} + + + {b734aa5c-6770-4181-b8ab-e86980054dca} + + + {9b1cfec6-a40a-450b-9962-9cbc7b250705} + + + {0d8c53a4-8251-4964-9918-f5664646ba04} + + + {b17885f1-37c1-481f-94b1-a74f0c886b29} + + + {6252deb7-1b31-4ba0-83b7-db11fa2123eb} + + + {0954c9e3-8650-4c2c-b51c-1ab9a4476b14} + + + {1152442e-07f2-4ec9-85c0-7e3b040eee40} + + + {0023db14-c89d-4ce1-b0d3-b4d83c742c35} + + + {becb642b-be13-43a9-90e2-61d563c35682} + + + {be720f83-accf-4b92-8831-c890ef2fa69d} + + + {c29b0728-1151-40d1-ae10-4b5d1bd136cd} + + + {983e9245-f2b6-4698-a3f1-544bec5f6e8d} + + + {31c1f8ed-deda-4c3c-81e0-794b3f02c13b} + + + {a1f1fffe-ed21-4fa2-8c72-ebeb904ab836} + + + {8f7a6c26-b05b-4a47-b4ed-2e13831327b4} + + + {ffd36b35-3e5a-4d0c-ac47-af8d810181a0} + + + {c16247bb-b940-4a7b-aaaf-dd98c4109308} + + + {82ed1932-1f2f-4540-9f2b-d676f0c922ef} + + + {2f846444-ed17-4436-9161-fc88e6ed0723} + + + {36e63199-ceb2-4e85-ae2e-110e034589cf} + + + {ea532493-bd24-4d05-9fd5-12d8b4260597} + + + {17ac8740-72a4-486c-86a9-8071edc888b2} + + + {00099120-7184-4416-829e-a5bdb72aa44e} + + + {0bad03c9-c024-454a-ae31-7f7ba2a7f2a3} + + + {9a4992cd-7557-475f-aaed-8680c8fc2a83} + + + {c09e3edc-9886-4b68-a2cb-e79cb3061c2a} + + + {96ba270b-f4fd-4bee-934b-8da371e22395} + + + {bb9698d0-9ffc-4007-891a-c1f46a5028a0} + + + {61399267-1105-4354-9bea-0204ba3783df} + + + {173699c3-0600-47d1-83ed-f2c2aceaa364} + + + {3948a2cb-f3ff-4574-81af-5e0641865f8e} + + + {f42bb1ee-d0be-43b4-a607-c067718361da} + + + {9bb717ff-847a-4ca3-8194-6adffadf3016} + + + {65f37772-0bc2-462d-b708-079b7f85a3bd} + + + {2ac6971f-ddc3-438b-921a-315ea5e3ad5a} + + + {a81770a3-9c91-487b-9321-e39f63998514} + + + {5abf2525-8223-4c6b-bb6b-5c9446334868} + + + {24e2f412-a3f1-447a-bbea-7aa6561a93eb} + + + {05f1cf3e-10f1-40a0-a72d-99bdd4d783d4} + + + {5c427349-fbbe-49db-b81c-bc7d7b32b7fc} + + + {6eaf883b-3e1e-4f06-93b9-360395bc9902} + + + {0cef3aca-71c2-4440-bea1-3cd3a28e9e84} + + + {4f1b40c3-610b-4f39-a71a-217153db5295} + + + {c87ae9ac-b823-48e4-b007-39d45aff76e6} + + + {bd03576a-c1b3-4c04-bc52-d67abed85da4} + + + {1bcefe01-a252-4ec6-8dab-7fcebbefda00} + + + {83228256-d4e1-447b-8102-4f824d131ff0} + + + {5c2a5df8-8116-4e7e-ae85-c688843e36bc} + + + {569feaf7-5d52-44fc-883d-87c10196ea2f} + + + {c554494b-5cc6-4251-8d1f-c70afdb4ba2d} + + + {fe68c974-acb2-44f5-b82b-2c4057194780} + + + {19721528-fc70-4673-8183-d9329e751555} + + + {e61b6eae-3e06-4649-86f6-ab1a6624833f} + + + {e43a9174-9dbd-4e7d-8aa0-7609d879b0e3} + + + {3cc9bf7f-fcf2-4819-85ea-3cdf5bbea411} + + + {1aaaae5f-20f2-4dea-9182-c5263a8085a6} + + + + + com\mojang\nbt + + + net\minecraft\world\level\chunk + + + net\minecraft\world\entity\projectile + + + net\minecraft\world + + + ConsoleJavaLibs + + + Header Files + + + argo + + + net\minecraft\world\damageSource + + + + + Header Files + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + Header Files + + + com\mojang\nbt + + + com\mojang\nbt + + + com\mojang\nbt + + + com\mojang\nbt + + + com\mojang\nbt + + + com\mojang\nbt + + + com\mojang\nbt + + + com\mojang\nbt + + + com\mojang\nbt + + + com\mojang\nbt + + + com\mojang\nbt + + + com\mojang\nbt + + + com\mojang\nbt + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\dimension + + + net\minecraft\world\level\dimension + + + net\minecraft\world\level\dimension + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\material + + + net\minecraft\world\level\material + + + net\minecraft\world\level\material + + + net\minecraft\world\level\material + + + net\minecraft\world\level\material + + + net\minecraft\world\level\material + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\entity + + + net\minecraft\world\entity + + + net\minecraft\world\entity + + + net\minecraft\world\entity\global + + + net\minecraft\world\entity\global + + + net\minecraft\world\entity\global + + + net\minecraft\world\entity\item + + + net\minecraft\world\entity\item + + + net\minecraft\world\entity\item + + + net\minecraft\world\entity + + + net\minecraft\world\level\pathfinder + + + net\minecraft\world\level\pathfinder + + + net\minecraft\world\level\pathfinder + + + net\minecraft\world\entity\item + + + net\minecraft\world\entity\item + + + net\minecraft\world + + + net\minecraft\world\phys + + + net\minecraft\world\phys + + + net\minecraft\world\entity\item + + + net\minecraft\world\entity + + + net\minecraft\world\entity + + + net\minecraft\world\entity + + + net\minecraft\world\entity + + + net\minecraft\world\entity\player + + + net\minecraft\world\entity\player + + + net\minecraft\world + + + net\minecraft\world + + + net\minecraft\world\entity\player + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\animal + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\animal + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\phys + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\entity\monster + + + net\minecraft\world\inventory + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\entity\monster + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\animal + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft + + + net\minecraft + + + net\minecraft + + + net\minecraft + + + net\minecraft\world + + + net\minecraft\world\inventory + + + net\minecraft\world + + + net\minecraft\world + + + net\minecraft\world\entity\animal + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\entity\animal + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level\dimension + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\entity + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\network\packet + + + net\minecraft\world\level\material + + + net\minecraft\world\level\saveddata + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\locale + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile\entity + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\tile\entity + + + ConsoleJavaLibs + + + net\minecraft\network + + + net\minecraft\world\entity + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + ConsoleJavaLibs + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleHelpers + + + ConsoleHelpers + + + net\minecraft\world\item\crafting + + + net\minecraft\world\phys + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\tile + + + net\minecraft\world\entity + + + ConsoleJavaLibs + + + net\minecraft + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + net\minecraft\world\level\saveddata + + + net\minecraft\world\level\saveddata + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\network + + + net\minecraft\util + + + net\minecraft\util + + + net\minecraft\util + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + net\minecraft\locale + + + net\minecraft\locale + + + net\minecraft\world\entity + + + net\minecraft\world\level\pathfinder + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + net\minecraft\world\level\pathfinder + + + ConsoleJavaLibs + + + ConsoleHelpers + + + net\minecraft\util + + + net\minecraft\world\level + + + Header Files + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers + + + ConsoleHelpers + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\level\chunk + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + Header Files + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\network\packet + + + net\minecraft\world\item + + + net\minecraft\world\level\tile\piston + + + net\minecraft\world\level\tile\piston + + + net\minecraft\world\level\tile\piston + + + net\minecraft\world\level\tile\piston + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\network\packet + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level + + + net\minecraft\world\level\material + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\util + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\damageSource + + + net\minecraft\world\damageSource + + + net\minecraft\world\damageSource + + + net\minecraft\world\effect + + + net\minecraft\world\effect + + + net\minecraft\world\effect + + + net\minecraft\world\effect + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\player + + + net\minecraft\world\entity + + + net\minecraft\world\entity + + + net\minecraft\world\food + + + net\minecraft\world\food + + + net\minecraft\world\food + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\item + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\network\packet + + + net\minecraft\world\level\dimension + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\entity + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\boss + + + net\minecraft\world\entity\boss\enderdragon + + + net\minecraft\world\entity\boss + + + net\minecraft\world\entity\boss + + + net\minecraft\world\entity\boss\enderdragon + + + net\minecraft\world\entity\boss\enderdragon + + + net\minecraft\world\entity\boss\enderdragon + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\npc + + + net\minecraft\world\entity\npc + + + net\minecraft\world\entity\npc + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\item\alchemy + + + net\minecraft\world\item\alchemy + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + Header Files + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\item + + + net\minecraft\network\packet + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\item + + + x64headers + + + x64headers + + + x64headers + + + x64headers + + + x64headers + + + x64headers + + + x64headers + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers + + + net\minecraft\world\entity + + + net\minecraft\world\item + + + net\minecraft\network\packet + + + net\minecraft\world\entity\ai\control + + + net\minecraft\world\entity\ai\control + + + net\minecraft\world\entity\ai\control + + + net\minecraft\world\entity\ai\control + + + net\minecraft\world\entity\ai\control + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\navigation + + + net\minecraft\world\entity\ai\navigation + + + net\minecraft\world\item + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\level\tile + + + net\minecraft\world\item + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\entity + + + net\minecraft\world + + + net\minecraft\world + + + net\minecraft\world + + + net\minecraft\world\level + + + com\mojang\nbt + + + net\minecraft\network\packet + + + net\minecraft\world\entity\ai\control + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\sensing + + + net\minecraft\world\entity\ai\sensing + + + net\minecraft\world\entity\ai\util + + + net\minecraft\world\entity\ai\util + + + net\minecraft\world\entity\ai\village + + + net\minecraft\world\entity\ai\village + + + net\minecraft\world\entity\ai\village + + + net\minecraft\world\entity\ai\village + + + net\minecraft\world\entity\ai\village + + + net\minecraft\world\entity + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\animal + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\item + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\levelgen + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\npc + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\item\trading + + + net\minecraft\world\item\trading + + + net\minecraft\world\item\trading + + + net\minecraft\world\item\trading + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\util + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level + + + net\minecraft\world\level\tile\piston + + + net\minecraft\world\level\storage + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\network\packet + + + net\minecraft\commands + + + net\minecraft\commands + + + net\minecraft\commands + + + net\minecraft\commands + + + net\minecraft\commands + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\network\packet + + + net\minecraft\commands + + + net\minecraft\commands\common + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers + + + + + Source Files + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\levelgen\synth + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + com\mojang\nbt + + + com\mojang\nbt + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\dimension + + + net\minecraft\world\level\dimension + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\material + + + net\minecraft\world\level\material + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\entity + + + net\minecraft\world\entity + + + net\minecraft\world\entity\global + + + net\minecraft\world\entity\item + + + net\minecraft\world\level\pathfinder + + + net\minecraft\world\level\pathfinder + + + net\minecraft\world\entity\item + + + net\minecraft\world\entity\item + + + net\minecraft\world\phys + + + net\minecraft\world\entity\item + + + net\minecraft\world\entity\item + + + net\minecraft\world\entity + + + net\minecraft\world\entity + + + net\minecraft\world\entity + + + net\minecraft\world\entity\player + + + net\minecraft\world\entity\player + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\animal + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\entity\animal + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\level\tile + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\phys + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\monster + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\entity\monster + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\animal + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft + + + net\minecraft + + + net\minecraft\world + + + net\minecraft\world\inventory + + + net\minecraft\world + + + net\minecraft\world\entity\animal + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\entity\animal + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\entity + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\level + + + net\minecraft\world\level + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile\entity + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\level\chunk + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\network\packet + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item\crafting + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile\entity + + + ConsoleJavaLibs + + + net\minecraft\world\entity + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + ConsoleHelpers + + + net\minecraft\world\level\chunk\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\phys + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\entity + + + net\minecraft + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs\InputOutputStream + + + net\minecraft\world\level\storage + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + net\minecraft\world\level\saveddata + + + net\minecraft\world\level\saveddata + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\world\level\storage + + + net\minecraft\util + + + net\minecraft\util + + + net\minecraft\world + + + net\minecraft\world\entity + + + net\minecraft\world\entity\monster + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + ConsoleJavaLibs + + + net\minecraft\locale + + + net\minecraft\locale + + + net\minecraft\network + + + net\minecraft\world\entity + + + net\minecraft\world\level\pathfinder + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs + + + net\minecraft\world\level\pathfinder + + + ConsoleJavaLibs + + + net\minecraft\util + + + net\minecraft\world\level + + + ConsoleJavaLibs + + + Source Files + + + ConsoleJavaLibs\InputOutputStream + + + ConsoleJavaLibs + + + net\minecraft\world\entity\global + + + ConsoleHelpers\ConsoleSaveFileIO + + + net\minecraft\world\item + + + ConsoleHelpers + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\level\chunk + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\network\packet + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\level\tile\piston + + + net\minecraft\world\level\tile\piston + + + net\minecraft\world\level\tile\piston + + + net\minecraft\world\level\tile\piston + + + net\minecraft\network\packet + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\util + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\damageSource + + + net\minecraft\world\damageSource + + + net\minecraft\world\damageSource + + + net\minecraft\world\effect + + + net\minecraft\world\effect + + + net\minecraft\world\effect + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity + + + net\minecraft\world\entity + + + net\minecraft\world\food + + + net\minecraft\world\food + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\item + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\biome + + + net\minecraft\network\packet + + + net\minecraft\world\level\dimension + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\boss + + + net\minecraft\world\entity\boss + + + net\minecraft\world\entity\boss\enderdragon + + + net\minecraft\world\entity\boss\enderdragon + + + net\minecraft\world\entity\boss\enderdragon + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\monster + + + net\minecraft\world\entity\npc + + + net\minecraft\world\entity\player + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\level\levelgen + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\item\alchemy + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft + + + net\minecraft\world\entity\npc + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\item + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\chunk + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\item + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers + + + net\minecraft\world\entity + + + net\minecraft\world\item + + + net\minecraft\network\packet + + + net\minecraft\world\entity\ai\control + + + net\minecraft\world\entity\ai\control + + + net\minecraft\world\entity\ai\control + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\navigation + + + net\minecraft\world\item + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\entity\projectile + + + net\minecraft\world\item + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\entity + + + net\minecraft\world\item + + + net\minecraft\world + + + net\minecraft\world\level + + + net\minecraft\world\level\tile + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\entity\ai\control + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal\target + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\sensing + + + net\minecraft\world\entity\ai\util + + + net\minecraft\world\entity\ai\village + + + net\minecraft\world\entity\ai\village + + + net\minecraft\world\entity\ai\village + + + net\minecraft\world\entity\ai\village + + + net\minecraft\world\entity + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\animal + + + net\minecraft\world\entity\animal + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\levelgen\feature + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + Source Files + + + net\minecraft\world\item + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\level\newbiome\layer + + + net\minecraft\world\level\levelgen + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\network\packet + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\entity\npc + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\item\trading + + + net\minecraft\world\item\trading + + + net\minecraft\world\level\biome + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\world\level\levelgen\structure + + + net\minecraft\util + + + net\minecraft\world\level\tile\entity + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level + + + net\minecraft\network\packet + + + net\minecraft\world\level\storage + + + net\minecraft\world\entity\ai\goal + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\inventory + + + net\minecraft\world\item\crafting + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\item + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\world\item\enchantment + + + net\minecraft\world\level\tile + + + net\minecraft\world\level\tile + + + net\minecraft\network\packet + + + net\minecraft\commands + + + net\minecraft\commands + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\commands\common + + + net\minecraft\network\packet + + + net\minecraft\commands\common + + + net\minecraft\stats + + + net\minecraft\stats + + + net\minecraft\stats + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers\ConsoleSaveFileIO + + + ConsoleHelpers + + + \ No newline at end of file diff --git a/Minecraft.World/Minecraft.World.vcxproj.user b/Minecraft.World/Minecraft.World.vcxproj.user new file mode 100644 index 00000000..ace9a86a --- /dev/null +++ b/Minecraft.World/Minecraft.World.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Minecraft.World/Minecraft.World.vcxproj.vspscc b/Minecraft.World/Minecraft.World.vcxproj.vspscc new file mode 100644 index 00000000..b6d32892 --- /dev/null +++ b/Minecraft.World/Minecraft.World.vcxproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Minecraft.World/Mob.cpp b/Minecraft.World/Mob.cpp new file mode 100644 index 00000000..d2dcddfb --- /dev/null +++ b/Minecraft.World/Mob.cpp @@ -0,0 +1,1943 @@ +#include "stdafx.h" +#include "JavaMath.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.sensing.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.effect.h" +#include "net.minecraft.world.item.alchemy.h" +#include "net.minecraft.world.item.enchantment.h" +#include "com.mojang.nbt.h" +#include "Mob.h" +#include "..\Minecraft.Client\Textures.h" +#include "SoundTypes.h" +#include "BasicTypeContainers.h" +#include "ParticleTypes.h" +#include "GenericStats.h" +#include "ItemEntity.h" + +const double Mob::MIN_MOVEMENT_DISTANCE = 0.005; + +void Mob::_init() +{ + invulnerableDuration = 20; + timeOffs = 0.0f; + + yBodyRot = 0; + yBodyRotO = 0; + yHeadRot = 0; + yHeadRotO = 0; + + oRun = 0.0f; + run = 0.0f; + + animStep = 0.0f; + animStepO = 0.0f; + + MemSect(31); + hasHair = true; + textureIdx = TN_MOB_CHAR; // 4J was L"/mob/char.png"; + allowAlpha = true; + rotOffs = 0; + modelName = L""; + bobStrength = 1; + deathScore = 0; + renderOffset = 0; + MemSect(0); + + walkingSpeed = 0.1f; + flyingSpeed = 0.02f; + + oAttackAnim = 0.0f; + attackAnim = 0.0f; + + lastHealth = 0; + dmgSpill = 0; + + ambientSoundTime = 0; + + hurtTime = 0; + hurtDuration = 0; + hurtDir = 0; + deathTime = 0; + attackTime = 0; + oTilt = 0; + tilt = 0; + + dead = false; + xpReward = 0; + + modelNum = -1; + animSpeed = (float) (Math::random() * 0.9f + 0.1f); + + walkAnimSpeedO = 0.0f; + walkAnimSpeed = 0.0f; + walkAnimPos = 0.0f; + + lastHurtByPlayer = nullptr; + lastHurtByPlayerTime = 0; + lastHurtByMob = nullptr; + lastHurtByMobTime = 0; + lastHurtMob = nullptr; + + arrowCount = 0; + removeArrowTime = 0; + + lSteps = 0; + lx = ly = lz = lyr = lxr = 0.0; + + fallTime = 0.0f; + + lastHurt = 0; + + noActionTime = 0; + xxa = yya = yRotA = 0.0f; + jumping = false; + defaultLookAngle = 0.0f; + runSpeed = 0.7f; + noJumpDelay = 0; + + lookingAt = nullptr; + lookTime = 0; + + effectsDirty = true; + effectColor = 0; + + target = nullptr; + sensing = NULL; + speed = 0.0f; + + restrictCenter = new Pos(0, 0, 0); + restrictRadius = -1.0f; +} + +Mob::Mob( Level* level) : Entity(level) +{ + _init(); + + // 4J Stu - This will not call the correct derived function, so moving to each derived class + //health = getMaxHealth(); + health = 0; + + blocksBuilding = true; + + lookControl = new LookControl(this); + moveControl = new MoveControl(this); + jumpControl = new JumpControl(this); + bodyControl = new BodyControl(this); + navigation = new PathNavigation(this, level, 16); + sensing = new Sensing(this); + + rotA = (float) (Math::random() + 1) * 0.01f; + setPos(x, y, z); + timeOffs = (float) Math::random() * 12398; + yRot = (float) (Math::random() * PI * 2); + yHeadRot = yRot; + + this->footSize = 0.5f; +} + +Mob::~Mob() +{ + for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it) + { + delete it->second; + } + + if(lookControl != NULL) delete lookControl; + if(moveControl != NULL) delete moveControl; + if(jumpControl != NULL) delete jumpControl; + if(bodyControl != NULL) delete bodyControl; + if(navigation != NULL) delete navigation; + if(sensing != NULL) delete sensing; + + delete restrictCenter; +} + +LookControl *Mob::getLookControl() +{ + return lookControl; +} + +MoveControl *Mob::getMoveControl() +{ + return moveControl; +} + +JumpControl *Mob::getJumpControl() +{ + return jumpControl; +} + +PathNavigation *Mob::getNavigation() +{ + return navigation; +} + +Sensing *Mob::getSensing() +{ + return sensing; +} + +Random *Mob::getRandom() +{ + return random; +} + +shared_ptr Mob::getLastHurtByMob() +{ + return lastHurtByMob; +} + +shared_ptr Mob::getLastHurtMob() +{ + return lastHurtMob; +} + +void Mob::setLastHurtMob(shared_ptr target) +{ + shared_ptr mob = dynamic_pointer_cast(target); + if (mob != NULL) lastHurtMob = mob; +} + +int Mob::getNoActionTime() +{ + return noActionTime; +} + +float Mob::getYHeadRot() +{ + return yHeadRot; +} + +void Mob::setYHeadRot(float yHeadRot) +{ + this->yHeadRot = yHeadRot; +} + +float Mob::getSpeed() +{ + return speed; +} + +void Mob::setSpeed(float speed) +{ + this->speed = speed; + setYya(speed); +} + +bool Mob::doHurtTarget(shared_ptr target) +{ + setLastHurtMob(target); + return false; +} + +shared_ptr Mob::getTarget() +{ + return target; +} + +void Mob::setTarget(shared_ptr target) +{ + this->target = target; +} + +bool Mob::canAttackType(eINSTANCEOF targetType) +{ + return !(targetType == eTYPE_CREEPER || targetType == eTYPE_GHAST); +} + +// Called by eatTileGoal +void Mob::ate() +{ +} + +// might move to navigation, might make area +bool Mob::isWithinRestriction() +{ + return isWithinRestriction(Mth::floor(x), Mth::floor(y), Mth::floor(z)); +} + +bool Mob::isWithinRestriction(int x, int y, int z) +{ + if (restrictRadius == -1) return true; + return restrictCenter->distSqr(x, y, z) < restrictRadius * restrictRadius; +} + +void Mob::restrictTo(int x, int y, int z, int radius) +{ + restrictCenter->set(x, y, z); + restrictRadius = radius; +} + +Pos *Mob::getRestrictCenter() +{ + return restrictCenter; +} + +float Mob::getRestrictRadius() +{ + return restrictRadius; +} + +void Mob::clearRestriction() +{ + restrictRadius = -1; +} + +bool Mob::hasRestriction() +{ + return restrictRadius != -1; +} + +void Mob::setLastHurtByMob(shared_ptr hurtBy) +{ + lastHurtByMob = hurtBy; + lastHurtByMobTime = lastHurtByMob != NULL ? PLAYER_HURT_EXPERIENCE_TIME : 0; +} + +void Mob::defineSynchedData() +{ + entityData->define(DATA_EFFECT_COLOR_ID, effectColor); +} + +bool Mob::canSee(shared_ptr target) +{ + HitResult *hres = level->clip(Vec3::newTemp(x, y + getHeadHeight(), z), Vec3::newTemp(target->x, target->y + target->getHeadHeight(), target->z)); + bool retVal = (hres == NULL); + delete hres; + return retVal; +} + +int Mob::getTexture() +{ + return textureIdx; +} + +bool Mob::isPickable() +{ + return !removed; +} + +bool Mob::isPushable() +{ + return !removed; +} + +float Mob::getHeadHeight() +{ + return bbHeight * 0.85f; +} + +int Mob::getAmbientSoundInterval() +{ + return 20 * 4; +} + +void Mob::playAmbientSound() +{ + MemSect(31); + int ambient = getAmbientSound(); + if (ambient != -1) + { + level->playSound(shared_from_this(), ambient, getSoundVolume(), getVoicePitch()); + } + MemSect(0); +} + +void Mob::baseTick() +{ + oAttackAnim = attackAnim; + Entity::baseTick(); + + if (isAlive() && random->nextInt(1000) < ambientSoundTime++) + { + ambientSoundTime = -getAmbientSoundInterval(); + + playAmbientSound(); + } + + if (isAlive() && isInWall()) + { + hurt(DamageSource::inWall, 1); + } + + if (isFireImmune() || level->isClientSide) clearFire(); + + if (isAlive() && isUnderLiquid(Material::water) && !isWaterMob() && activeEffects.find(MobEffect::waterBreathing->id) == activeEffects.end()) + { + setAirSupply(decreaseAirSupply(getAirSupply())); + if (getAirSupply() == -20) + { + setAirSupply(0); + if(canCreateParticles()) + { + for (int i = 0; i < 8; i++) + { + float xo = random->nextFloat() - random->nextFloat(); + float yo = random->nextFloat() - random->nextFloat(); + float zo = random->nextFloat() - random->nextFloat(); + level->addParticle(eParticleType_bubble, x + xo, y + yo, z + zo, xd, yd, zd); + } + } + hurt(DamageSource::drown, 2); + } + + clearFire(); + } + else + { + setAirSupply(TOTAL_AIR_SUPPLY); + } + + oTilt = tilt; + + if (attackTime > 0) attackTime--; + if (hurtTime > 0) hurtTime--; + if (invulnerableTime > 0) invulnerableTime--; + if (health <= 0) + { + tickDeath(); + } + + if (lastHurtByPlayerTime > 0) lastHurtByPlayerTime--; + else + { + // Note - this used to just set to nullptr, but that has to create a new shared_ptr and free an old one, when generally this won't be doing anything at all. This + // is the lightweight but ugly alternative + if( lastHurtByPlayer ) + { + lastHurtByPlayer.reset(); + } + } + if (lastHurtMob != NULL && !lastHurtMob->isAlive()) lastHurtMob = nullptr; + + if (lastHurtByMob != NULL) + { + if (!lastHurtByMob->isAlive()) setLastHurtByMob(nullptr); + else if (lastHurtByMobTime > 0) lastHurtByMobTime--; + else setLastHurtByMob(nullptr); + } + + // update effects + tickEffects(); + + animStepO = animStep; + + yBodyRotO = yBodyRot; + yHeadRotO = yHeadRot; + yRotO = yRot; + xRotO = xRot; +} + +void Mob::tickDeath() +{ + deathTime++; + if (deathTime == 20) + { + // 4J Stu - Added level->isClientSide check from 1.2 to fix XP orbs being created client side + if(!level->isClientSide && (lastHurtByPlayerTime > 0 || isAlwaysExperienceDropper()) ) + { + if (!isBaby()) + { + int xpCount = this->getExperienceReward(lastHurtByPlayer); + while (xpCount > 0) + { + int newCount = ExperienceOrb::getExperienceValue(xpCount); + xpCount -= newCount; + level->addEntity(shared_ptr( new ExperienceOrb(level, x, y, z, newCount) ) ); + } + } + } + + remove(); + for (int i = 0; i < 20; i++) + { + double xa = random->nextGaussian() * 0.02; + double ya = random->nextGaussian() * 0.02; + double za = random->nextGaussian() * 0.02; + level->addParticle(eParticleType_explode, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); + } + } +} + +int Mob::decreaseAirSupply(int currentSupply) +{ + return currentSupply - 1; +} + +int Mob::getExperienceReward(shared_ptr killedBy) +{ + return xpReward; +} + +bool Mob::isAlwaysExperienceDropper() +{ + return false; +} + +void Mob::spawnAnim() +{ + for (int i = 0; i < 20; i++) + { + double xa = random->nextGaussian() * 0.02; + double ya = random->nextGaussian() * 0.02; + double za = random->nextGaussian() * 0.02; + double dd = 10; + level->addParticle(eParticleType_explode, x + random->nextFloat() * bbWidth * 2 - bbWidth - xa * dd, y + random->nextFloat() * bbHeight - ya * dd, z + random->nextFloat() * bbWidth * 2 - bbWidth - za + * dd, xa, ya, za); + } +} + +void Mob::rideTick() +{ + Entity::rideTick(); + oRun = run; + run = 0; + fallDistance = 0; +} + +void Mob::lerpTo(double x, double y, double z, float yRot, float xRot, int steps) +{ + heightOffset = 0; + lx = x; + ly = y; + lz = z; + lyr = yRot; + lxr = xRot; + + lSteps = steps; +} + +void Mob::superTick() +{ + Entity::tick(); +} + +void Mob::tick() +{ + Entity::tick(); + + if (arrowCount > 0) + { + if (removeArrowTime <= 0) + { + removeArrowTime = 20 * 3; + } + removeArrowTime--; + if (removeArrowTime <= 0) + { + arrowCount--; + } + } + + aiStep(); + + double xd = x - xo; + double zd = z - zo; + + float sideDist = xd * xd + zd * zd; + + float yBodyRotT = yBodyRot; + + float walkSpeed = 0; + oRun = run; + float tRun = 0; + if (sideDist <= 0.05f * 0.05f) + { + // animStep = 0; + } + else + { + tRun = 1; + walkSpeed = sqrt(sideDist) * 3; + yBodyRotT = ((float) atan2(zd, xd) * 180 / (float) PI - 90); + } + if (attackAnim > 0) + { + yBodyRotT = yRot; + } + if (!onGround) + { + tRun = 0; + } + run = run + (tRun - run) * 0.3f; + + /* + * float yBodyRotD = yRot-yBodyRot; while (yBodyRotD < -180) yBodyRotD + * += 360; while (yBodyRotD >= 180) yBodyRotD -= 360; yBodyRot += + * yBodyRotD * 0.1f; + */ + + if (useNewAi()) + { + bodyControl->clientTick(); + } + else + { + float yBodyRotD = Mth::wrapDegrees(yBodyRotT - yBodyRot); + yBodyRot += yBodyRotD * 0.3f; + + float headDiff = Mth::wrapDegrees(yRot - yBodyRot); + bool behind = headDiff < -90 || headDiff >= 90; + if (headDiff < -75) headDiff = -75; + if (headDiff >= 75) headDiff = +75; + yBodyRot = yRot - headDiff; + if (headDiff * headDiff > 50 * 50) + { + yBodyRot += headDiff * 0.2f; + } + + if (behind) + { + walkSpeed *= -1; + } + } + while (yRot - yRotO < -180) + yRotO -= 360; + while (yRot - yRotO >= 180) + yRotO += 360; + + while (yBodyRot - yBodyRotO < -180) + yBodyRotO -= 360; + while (yBodyRot - yBodyRotO >= 180) + yBodyRotO += 360; + + while (xRot - xRotO < -180) + xRotO -= 360; + while (xRot - xRotO >= 180) + xRotO += 360; + + while (yHeadRot - yHeadRotO < -180) + yHeadRotO -= 360; + while (yHeadRot - yHeadRotO >= 180) + yHeadRotO += 360; + + animStep += walkSpeed; +} + +void Mob::heal(int heal) +{ + if (health <= 0) return; + health += heal; + if (health > getMaxHealth()) health = getMaxHealth(); + invulnerableTime = invulnerableDuration / 2; +} + +int Mob::getHealth() +{ + return health; +} + +void Mob::setHealth(int health) +{ + this->health = health; + if (health > getMaxHealth()) + { + health = getMaxHealth(); + } +} + +bool Mob::hurt(DamageSource *source, int dmg) +{ + // 4J Stu - Reworked this function a bit to show hurt damage on the client before the server responds. + // Fix for #8823 - Gameplay: Confirmation that a monster or animal has taken damage from an attack is highly delayed + // 4J Stu - Change to the fix to only show damage when attacked, rather than collision damage + // Fix for #10299 - When in corners, passive mobs may show that they are taking damage. + // 4J Stu - Change to the fix for TU6, as source is never NULL due to changes in 1.8.2 to what source actually is + if (level->isClientSide && dynamic_cast(source) == NULL) return false; + noActionTime = 0; + if (health <= 0) return false; + + if ( source->isFire() && hasEffect(MobEffect::fireResistance) ) + { + // 4J-JEV, for new achievement Stayin'Frosty, TODO merge with Java version. + shared_ptr plr = dynamic_pointer_cast(shared_from_this()); + if ( plr != NULL && source == DamageSource::lava ) // Only award when in lava (not any fire). + { + plr->awardStat(GenericStats::stayinFrosty(),GenericStats::param_stayinFrosty()); + } + return false; + } + + this->walkAnimSpeed = 1.5f; + + bool sound = true; + if (invulnerableTime > invulnerableDuration / 2.0f) + { + if (dmg <= lastHurt) return false; + if(!level->isClientSide) actuallyHurt(source, dmg - lastHurt); + lastHurt = dmg; + sound = false; + } + else + { + lastHurt = dmg; + lastHealth = health; + invulnerableTime = invulnerableDuration; + if (!level->isClientSide) actuallyHurt(source, dmg); + hurtTime = hurtDuration = 10; + } + + hurtDir = 0; + + shared_ptr sourceEntity = source->getEntity(); + if (sourceEntity != NULL) + { + if (dynamic_pointer_cast(sourceEntity) != NULL) { + setLastHurtByMob(dynamic_pointer_cast(sourceEntity)); + + } + if (dynamic_pointer_cast(sourceEntity) != NULL) + { + lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME; + lastHurtByPlayer = dynamic_pointer_cast(sourceEntity); + } + else if (dynamic_pointer_cast(sourceEntity)) + { + shared_ptr w = dynamic_pointer_cast(sourceEntity); + if (w->isTame()) + { + lastHurtByPlayerTime = PLAYER_HURT_EXPERIENCE_TIME; + lastHurtByPlayer = nullptr; + } + } + } + + if (sound && level->isClientSide) + { + return false; + } + + if (sound) + { + level->broadcastEntityEvent(shared_from_this(), EntityEvent::HURT); + if (source != DamageSource::drown && source != DamageSource::controlledExplosion) markHurt(); + if (sourceEntity != NULL) + { + double xd = sourceEntity->x - x; + double zd = sourceEntity->z - z; + while (xd * xd + zd * zd < 0.0001) + { + xd = (Math::random() - Math::random()) * 0.01; + zd = (Math::random() - Math::random()) * 0.01; + } + hurtDir = (float) (atan2(zd, xd) * 180 / PI) - yRot; + knockback(sourceEntity, dmg, xd, zd); + } + else + { + hurtDir = (float) (int) ((Math::random() * 2) * 180); // 4J This cast is the same as Java + } + } + + MemSect(31); + if (health <= 0) + { + if (sound) level->playSound(shared_from_this(), getDeathSound(), getSoundVolume(), getVoicePitch()); + die(source); + } + else + { + if (sound) level->playSound(shared_from_this(), getHurtSound(), getSoundVolume(), getVoicePitch()); + } + MemSect(0); + + return true; +} + +float Mob::getVoicePitch() +{ + if (isBaby()) + { + return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.5f; + + } + return (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f; +} + +void Mob::animateHurt() +{ + hurtTime = hurtDuration = 10; + hurtDir = 0; +} + +int Mob::getArmorValue() +{ + return 0; +} + +void Mob::hurtArmor(int damage) +{ +} + +int Mob::getDamageAfterArmorAbsorb(DamageSource *damageSource, int damage) +{ + if (!damageSource->isBypassArmor()) + { + int absorb = 25 - getArmorValue(); + int v = (damage) * absorb + dmgSpill; + hurtArmor(damage); + damage = v / 25; + dmgSpill = v % 25; + } + return damage; +} + +int Mob::getDamageAfterMagicAbsorb(DamageSource *damageSource, int damage) +{ + if (hasEffect(MobEffect::damageResistance)) + { + int absorbValue = (getEffect(MobEffect::damageResistance)->getAmplifier() + 1) * 5; + int absorb = 25 - absorbValue; + int v = (damage) * absorb + dmgSpill; + damage = v / 25; + dmgSpill = v % 25; + } + return damage; +} + +void Mob::actuallyHurt(DamageSource *source, int dmg) +{ + dmg = getDamageAfterArmorAbsorb(source, dmg); + dmg = getDamageAfterMagicAbsorb(source, dmg); + health -= dmg; +} + + +float Mob::getSoundVolume() +{ + return 1; +} + +int Mob::getAmbientSound() +{ + return -1; +} + +int Mob::getHurtSound() +{ + return eSoundType_DAMAGE_HURT; +} + +int Mob::getDeathSound() +{ + return eSoundType_DAMAGE_HURT; +} + +void Mob::knockback(shared_ptr source, int dmg, double xd, double zd) +{ + hasImpulse = true; + float dd = (float) sqrt(xd * xd + zd * zd); + float pow = 0.4f; + + this->xd /= 2; + this->yd /= 2; + this->zd /= 2; + + this->xd -= xd / dd * pow; + this->yd += pow; + this->zd -= zd / dd * pow; + + if (this->yd > 0.4f) this->yd = 0.4f; +} + +void Mob::die(DamageSource *source) +{ + shared_ptr sourceEntity = source->getEntity(); + if (deathScore >= 0 && sourceEntity != NULL) sourceEntity->awardKillScore(shared_from_this(), deathScore); + + if (sourceEntity != NULL) sourceEntity->killed( dynamic_pointer_cast( shared_from_this() ) ); + + dead = true; + + if (!level->isClientSide) + { + int playerBonus = 0; + shared_ptr player = dynamic_pointer_cast(sourceEntity); + if (player != NULL) + { + playerBonus = EnchantmentHelper::getKillingLootBonus(player->inventory); + } + if (!isBaby()) + { + dropDeathLoot(lastHurtByPlayerTime > 0, playerBonus); + if (lastHurtByPlayerTime > 0) + { + int rareLoot = random->nextInt(200) - playerBonus; + if (rareLoot < 5) + { + dropRareDeathLoot((rareLoot <= 0) ? 1 : 0); + } + } + } + + // 4J-JEV, hook for Durango mobKill event. + if (player != NULL) + { + player->awardStat(GenericStats::killMob(),GenericStats::param_mobKill(player, dynamic_pointer_cast(shared_from_this()), source)); + } + } + + level->broadcastEntityEvent(shared_from_this(), EntityEvent::DEATH); +} + +/** +* Drop extra rare loot. Only occurs roughly 5% of the time, rareRootLevel +* is set to 1 (otherwise 0) 1% of the time. +* +* @param rareLootLevel +*/ +void Mob::dropRareDeathLoot(int rareLootLevel) +{ + +} + +void Mob::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + int loot = getDeathLoot(); + if (loot > 0) + { + int count = random->nextInt(3); + if (playerBonusLevel > 0) + { + count += random->nextInt(playerBonusLevel + 1); + } + for (int i = 0; i < count; i++) + spawnAtLocation(loot, 1); + } +} + +int Mob::getDeathLoot() +{ + return 0; +} + +void Mob::causeFallDamage(float distance) +{ + Entity::causeFallDamage(distance); + int dmg = (int) ceil(distance - 3); + if (dmg > 0) + { + // 4J - new sounds here brought forward from 1.2.3 + if (dmg > 4) + { + level->playSound(shared_from_this(), eSoundType_DAMAGE_FALL_BIG, 1, 1); + } + else + { + level->playSound(shared_from_this(), eSoundType_DAMAGE_FALL_SMALL, 1, 1); + } + hurt(DamageSource::fall, dmg); + + int t = level->getTile( Mth::floor(x), Mth::floor(y - 0.2f - this->heightOffset), Mth::floor(z)); + if (t > 0) + { + const Tile::SoundType *soundType = Tile::tiles[t]->soundType; + MemSect(31); + level->playSound(shared_from_this(), soundType->getStepSound(), soundType->getVolume() * 0.5f, soundType->getPitch() * 0.75f); + MemSect(0); + } + } +} + +void Mob::travel(float xa, float ya) +{ +#ifdef __PSVITA__ + // AP - dynamic_pointer_cast is a non-trivial call + Player *thisPlayer = NULL; + if( (GetType() & eTYPE_PLAYER) == eTYPE_PLAYER ) + { + thisPlayer = (Player*) this; + } +#else + shared_ptr thisPlayer = dynamic_pointer_cast(shared_from_this()); +#endif + if (isInWater() && !(thisPlayer && thisPlayer->abilities.flying) ) + { + double yo = y; + moveRelative(xa, ya, useNewAi() ? 0.04f : 0.02f); + move(xd, yd, zd); + + xd *= 0.80f; + yd *= 0.80f; + zd *= 0.80f; + yd -= 0.02; + + if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd)) + { + yd = 0.3f; + } + } + else if (isInLava() && !(thisPlayer && thisPlayer->abilities.flying) ) + { + double yo = y; + moveRelative(xa, ya, 0.02f); + move(xd, yd, zd); + xd *= 0.50f; + yd *= 0.50f; + zd *= 0.50f; + yd -= 0.02; + + if (horizontalCollision && isFree(xd, yd + 0.6f - y + yo, zd)) + { + yd = 0.3f; + } + } + else + { + float friction = 0.91f; + if (onGround) + { + friction = 0.6f * 0.91f; + int t = level->getTile(Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z)); + if (t > 0) + { + friction = Tile::tiles[t]->friction * 0.91f; + } + } + + float friction2 = (0.6f * 0.6f * 0.91f * 0.91f * 0.6f * 0.91f) / (friction * friction * friction); + + float speed; + if (onGround) + { + if (useNewAi()) speed = getSpeed(); + else speed = walkingSpeed; + speed *= friction2; + } + else speed = flyingSpeed; + + moveRelative(xa, ya, speed); + + friction = 0.91f; + if (onGround) + { + friction = 0.6f * 0.91f; + int t = level->getTile( Mth::floor(x), Mth::floor(bb->y0) - 1, Mth::floor(z)); + if (t > 0) + { + friction = Tile::tiles[t]->friction * 0.91f; + } + } + if (onLadder()) + { + float max = 0.15f; + if (xd < -max) xd = -max; + if (xd > max) xd = max; + if (zd < -max) zd = -max; + if (zd > max) zd = max; + this->fallDistance = 0; + if (yd < -0.15) yd = -0.15; + bool playerSneaking = isSneaking() && dynamic_pointer_cast(shared_from_this()) != NULL; + if (playerSneaking && yd < 0) yd = 0; + } + + move(xd, yd, zd); + + if (horizontalCollision && onLadder()) + { + yd = 0.2; + } + + yd -= 0.08; + yd *= 0.98f; + xd *= friction; + zd *= friction; + } + + walkAnimSpeedO = walkAnimSpeed; + double xxd = x - xo; + double zzd = z - zo; + float wst = Mth::sqrt(xxd * xxd + zzd * zzd) * 4; + if (wst > 1) wst = 1; + walkAnimSpeed += (wst - walkAnimSpeed) * 0.4f; + walkAnimPos += walkAnimSpeed; +} + +bool Mob::onLadder() +{ + int xt = Mth::floor(x); + int yt = Mth::floor(bb->y0); + int zt = Mth::floor(z); + + // 4J-PB - TU9 - add climbable vines + int iTile = level->getTile(xt, yt, zt); + return (iTile== Tile::ladder_Id) || (iTile== Tile::vine_Id); +} + + +bool Mob::isShootable() +{ + return true; +} + +void Mob::addAdditonalSaveData(CompoundTag *entityTag) +{ + entityTag->putShort(L"Health", (short) health); + entityTag->putShort(L"HurtTime", (short) hurtTime); + entityTag->putShort(L"DeathTime", (short) deathTime); + entityTag->putShort(L"AttackTime", (short) attackTime); + + if (!activeEffects.empty()) + { + ListTag *listTag = new ListTag(); + + for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it) + { + MobEffectInstance *effect = it->second; + + CompoundTag *tag = new CompoundTag(); + tag->putByte(L"Id", (BYTE) effect->getId()); + tag->putByte(L"Amplifier", (char) effect->getAmplifier()); + tag->putInt(L"Duration", effect->getDuration()); + listTag->add(tag); + } + entityTag->put(L"ActiveEffects", listTag); + } +} + +void Mob::readAdditionalSaveData(CompoundTag *tag) +{ + if (health < Short::MIN_VALUE) health = Short::MIN_VALUE; + health = tag->getShort(L"Health"); + if (!tag->contains(L"Health")) health = getMaxHealth(); + hurtTime = tag->getShort(L"HurtTime"); + deathTime = tag->getShort(L"DeathTime"); + attackTime = tag->getShort(L"AttackTime"); + + if (tag->contains(L"ActiveEffects")) + { + ListTag *effects = (ListTag *) tag->getList(L"ActiveEffects"); + for (int i = 0; i < effects->size(); i++) + { + CompoundTag *effectTag = effects->get(i); + int id = effectTag->getByte(L"Id"); + int amplifier = effectTag->getByte(L"Amplifier"); + int duration = effectTag->getInt(L"Duration"); + + activeEffects.insert( unordered_map::value_type( id, new MobEffectInstance(id, duration, amplifier) ) ); + } + } +} + +bool Mob::isAlive() +{ + return !removed && health > 0; +} + +bool Mob::isWaterMob() +{ + return false; +} + +// 4J - added for more accurate lighting of mobs. Takes a weighted average of all tiles touched by the bounding volume of the entity - the method in the Entity class (which used to be used for +// mobs too) simply gets a single tile's lighting value causing sudden changes of lighting values when entities go in and out of lit areas, for example when bobbing in the water. +int Mob::getLightColor(float a) +{ + float accum[2] = {0,0}; + float totVol = ( bb->x1 - bb->x0 ) * ( bb->y1 - bb->y0 ) * ( bb->z1 - bb->z0 ); + int xmin = Mth::floor(bb->x0); + int xmax = Mth::floor(bb->x1); + int ymin = Mth::floor(bb->y0); + int ymax = Mth::floor(bb->y1); + int zmin = Mth::floor(bb->z0); + int zmax = Mth::floor(bb->z1); + for( int xt = xmin; xt <= xmax; xt++ ) + for( int yt = ymin; yt <= ymax; yt++ ) + for( int zt = zmin; zt <= zmax; zt++ ) + { + float tilexmin = (float)xt; + float tilexmax = (float)(xt+1); + float tileymin = (float)yt; + float tileymax = (float)(yt+1); + float tilezmin = (float)zt; + float tilezmax = (float)(zt+1); + if( tilexmin < bb->x0 ) tilexmin = bb->x0; + if( tilexmax > bb->x1 ) tilexmax = bb->x1; + if( tileymin < bb->y0 ) tileymin = bb->y0; + if( tileymax > bb->y1 ) tileymax = bb->y1; + if( tilezmin < bb->z0 ) tilezmin = bb->z0; + if( tilezmax > bb->z1 ) tilezmax = bb->z1; + float tileVol = ( tilexmax - tilexmin ) * ( tileymax - tileymin ) * ( tilezmax - tilezmin ); + float frac = tileVol / totVol; + int lc = level->getLightColor(xt, yt, zt, 0); + accum[0] += frac * (float)( lc & 0xffff ); + accum[1] += frac * (float)( lc >> 16 ); + } + + if( accum[0] > 240.0f ) accum[0] = 240.0f; + if( accum[1] > 240.0f ) accum[1] = 240.0f; + + return ( ( (int)accum[1])<<16) | ((int)accum[0]); +} + +void Mob::setYya(float yya) +{ + this->yya = yya; +} + +void Mob::setJumping(bool jump) +{ + jumping = jump; +} + +void Mob::aiStep() +{ + if (noJumpDelay > 0) noJumpDelay--; + if (lSteps > 0) + { + double xt = x + (lx - x) / lSteps; + double yt = y + (ly - y) / lSteps; + double zt = z + (lz - z) / lSteps; + + double yrd = Mth::wrapDegrees(lyr - yRot); + double xrd = Mth::wrapDegrees(lxr - xRot); + + yRot += (float) ( (yrd) / lSteps ); + xRot += (float) ( (xrd) / lSteps ); + + lSteps--; + this->setPos(xt, yt, zt); + this->setRot(yRot, xRot); + + // 4J - this collision is carried out to try and stop the lerping push the mob through the floor, + // in which case gravity can then carry on moving the mob because the collision just won't work anymore. + // BB for collision used to be calculated as: bb->shrink(1 / 32.0, 0, 1 / 32.0) + // now using a reduced BB to try and get rid of some issues where mobs pop up the sides of walls, undersides of + // trees etc. + AABB *shrinkbb = bb->shrink(0.1, 0, 0.1); + shrinkbb->y1 = shrinkbb->y0 + 0.1; + AABBList *collisions = level->getCubes(shared_from_this(), shrinkbb); + if (collisions->size() > 0) + { + double yTop = 0; + AUTO_VAR(itEnd, collisions->end()); + for (AUTO_VAR(it, collisions->begin()); it != itEnd; it++) + { + AABB *ab = *it; //collisions->at(i); + if (ab->y1 > yTop) yTop = ab->y1; + } + + yt += yTop - bb->y0; + setPos(xt, yt, zt); + } + if (abs(xd) < MIN_MOVEMENT_DISTANCE) xd = 0; + if (abs(yd) < MIN_MOVEMENT_DISTANCE) yd = 0; + if (abs(zd) < MIN_MOVEMENT_DISTANCE) zd = 0; + } + + if (isImmobile()) + { + jumping = false; + xxa = 0; + yya = 0; + yRotA = 0; + } + else + { + MemSect(25); + if (isEffectiveAI()) + { + if (useNewAi()) + { + newServerAiStep(); + } + else + { + serverAiStep(); + yHeadRot = yRot; + } + } + MemSect(0); + } + + if (jumping) + { + if (isInWater() || isInLava() ) + { + yd += 0.04f; + } + else if (onGround) + { + if (noJumpDelay == 0) + { + jumpFromGround(); + noJumpDelay = 10; + } + } + } + else + { + noJumpDelay = 0; + } + + + xxa *= 0.98f; + yya *= 0.98f; + yRotA *= 0.9f; + + float normalSpeed = walkingSpeed; + walkingSpeed *= getWalkingSpeedModifier(); + travel(xxa, yya); + walkingSpeed = normalSpeed; + + if(!level->isClientSide) + { + vector > *entities = level->getEntities(shared_from_this(), this->bb->grow(0.2f, 0, 0.2f)); + if (entities != NULL && !entities->empty()) + { + AUTO_VAR(itEnd, entities->end()); + for (AUTO_VAR(it, entities->begin()); it != itEnd; it++) + { + shared_ptr e = *it; //entities->at(i); + if (e->isPushable()) e->push(shared_from_this()); + } + } + } +} + +bool Mob::useNewAi() +{ + return false; +} + +bool Mob::isEffectiveAI() +{ + return !level->isClientSide; +} + +bool Mob::isImmobile() +{ + return health <= 0; +} + +bool Mob::isBlocking() +{ + return false; +} + +void Mob::jumpFromGround() +{ + yd = 0.42f; + if (hasEffect(MobEffect::jump)) + { + yd += (getEffect(MobEffect::jump)->getAmplifier() + 1) * .1f; + } + if (isSprinting()) + { + float rr = yRot * Mth::RAD_TO_GRAD; + + xd -= Mth::sin(rr) * 0.2f; + zd += Mth::cos(rr) * 0.2f; + } + this->hasImpulse = true; +} + +bool Mob::removeWhenFarAway() +{ + return true; +} + +void Mob::checkDespawn() +{ + shared_ptr player = level->getNearestPlayer(shared_from_this(), -1); + if (player != NULL) + { + double xd = player->x - x; + double yd = player->y - y; + double zd = player->z - z; + double sd = xd * xd + yd * yd + zd * zd; + + if (removeWhenFarAway() && sd > 128 * 128) + { + remove(); + } + + if (noActionTime > 20 * 30 && random->nextInt(800) == 0 && sd > 32 * 32 && removeWhenFarAway()) + { + remove(); + } + else if (sd < 32 * 32) + { + noActionTime = 0; + } + } +} + +void Mob::newServerAiStep() +{ + MemSect(51); + noActionTime++; + checkDespawn(); + sensing->tick(); + targetSelector.tick(); + goalSelector.tick(); + navigation->tick(); + serverAiMobStep(); + moveControl->tick(); + lookControl->tick(); + jumpControl->tick(); + // Consider this for extra strolling if it is protected against despawning. We aren't interested in ones that aren't protected as the whole point of this + // extra wandering is to potentially transition from protected to not protected. + considerForExtraWandering( isDespawnProtected() ); + MemSect(0); +} + +void Mob::serverAiMobStep() +{ +} + +void Mob::serverAiStep() +{ + noActionTime++; + + checkDespawn(); + + xxa = 0; + yya = 0; + + float lookDistance = 8; + if (random->nextFloat() < 0.02f) + { + shared_ptr player = level->getNearestPlayer(shared_from_this(), lookDistance); + if (player != NULL) + { + lookingAt = player; + lookTime = 10 + random->nextInt(20); + } + else + { + yRotA = (random->nextFloat() - 0.5f) * 20; + } + } + + if (lookingAt != NULL) + { + lookAt(lookingAt, 10.0f, (float) getMaxHeadXRot()); + if (lookTime-- <= 0 || lookingAt->removed || lookingAt->distanceToSqr(shared_from_this()) > lookDistance * lookDistance) + { + lookingAt = nullptr; + } + } + else + { + if (random->nextFloat() < 0.05f) + { + yRotA = (random->nextFloat() - 0.5f) * 20; + } + yRot += yRotA; + xRot = defaultLookAngle; + } + + bool inWater = isInWater(); + bool inLava = isInLava(); + if (inWater || inLava) jumping = random->nextFloat() < 0.8f; +} + +int Mob::getMaxHeadXRot() +{ + return 40; +} + +void Mob::lookAt(shared_ptr e, float yMax, float xMax) +{ + double xd = e->x - x; + double yd; + double zd = e->z - z; + + shared_ptr mob = dynamic_pointer_cast(e); + if(mob != NULL) + { + yd = (y + getHeadHeight()) - (mob->y + mob->getHeadHeight()); + } + else + { + yd = (e->bb->y0 + e->bb->y1) / 2 - (y + getHeadHeight()); + } + + double sd = Mth::sqrt(xd * xd + zd * zd); + + float yRotD = (float) (atan2(zd, xd) * 180 / PI) - 90; + float xRotD = (float) -(atan2(yd, sd) * 180 / PI); + xRot = -rotlerp(xRot, xRotD, xMax); + yRot = rotlerp(yRot, yRotD, yMax); +} + +bool Mob::isLookingAtAnEntity() +{ + return lookingAt != NULL; +} + +shared_ptr Mob::getLookingAt() +{ + return lookingAt; +} + +float Mob::rotlerp(float a, float b, float max) +{ + float diff = Mth::wrapDegrees(b - a); + if (diff > max) + { + diff = max; + } + if (diff < -max) + { + diff = -max; + } + return a + diff; +} + +bool Mob::canSpawn() +{ + // 4J - altered to use special containsAnyLiquid variant + return level->isUnobstructed(bb) && level->getCubes(shared_from_this(), bb)->empty() && !level->containsAnyLiquid_NoLoad(bb); +} + +void Mob::outOfWorld() +{ + hurt(DamageSource::outOfWorld, 4); +} + +float Mob::getAttackAnim(float a) +{ + float diff = attackAnim - oAttackAnim; + if (diff < 0) diff += 1; + return oAttackAnim + diff * a; +} + + +Vec3 *Mob::getPos(float a) +{ + if (a == 1) + { + return Vec3::newTemp(x, y, z); + } + double x = xo + (this->x - xo) * a; + double y = yo + (this->y - yo) * a; + double z = zo + (this->z - zo) * a; + + return Vec3::newTemp(x, y, z); +} + +Vec3 *Mob::getLookAngle() +{ + return getViewVector(1); +} + +Vec3 *Mob::getViewVector(float a) +{ + if (a == 1) + { + float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI); + float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI); + float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD); + float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD); + + return Vec3::newTemp(ySin * xCos, xSin, yCos * xCos); + } + float xRot = xRotO + (this->xRot - xRotO) * a; + float yRot = yRotO + (this->yRot - yRotO) * a; + + float yCos = Mth::cos(-yRot * Mth::RAD_TO_GRAD - PI); + float ySin = Mth::sin(-yRot * Mth::RAD_TO_GRAD - PI); + float xCos = -Mth::cos(-xRot * Mth::RAD_TO_GRAD); + float xSin = Mth::sin(-xRot * Mth::RAD_TO_GRAD); + + return Vec3::newTemp(ySin * xCos, xSin, yCos * xCos); +} + +float Mob::getSizeScale() +{ + return 1.0f; +} + +float Mob::getHeadSizeScale() +{ + return 1.0f; +} + +HitResult *Mob::pick(double range, float a) +{ + Vec3 *from = getPos(a); + Vec3 *b = getViewVector(a); + Vec3 *to = from->add(b->x * range, b->y * range, b->z * range); + return level->clip(from, to); +} + +int Mob::getMaxSpawnClusterSize() +{ + return 4; +} + +shared_ptr Mob::getCarriedItem() +{ + return nullptr; +} + +shared_ptr Mob::getArmor(int pos) +{ + // 4J Stu - Not implemented yet + return nullptr; + //return equipment[pos + 1]; +} + +void Mob::handleEntityEvent(byte id) +{ + if (id == EntityEvent::HURT) + { + this->walkAnimSpeed = 1.5f; + + invulnerableTime = invulnerableDuration; + hurtTime = hurtDuration = 10; + hurtDir = 0; + + MemSect(31); + // 4J-PB -added because villagers have no sounds + int iHurtSound=getHurtSound(); + if(iHurtSound!=-1) + { + level->playSound(shared_from_this(), iHurtSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); + } + MemSect(0); + hurt(DamageSource::genericSource, 0); + } + else if (id == EntityEvent::DEATH) + { + MemSect(31); + // 4J-PB -added because villagers have no sounds + int iDeathSound=getDeathSound(); + if(iDeathSound!=-1) + { + level->playSound(shared_from_this(), iDeathSound, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); + } + MemSect(0); + health = 0; + die(DamageSource::genericSource); + } + else + { + Entity::handleEntityEvent(id); + } +} + +bool Mob::isSleeping() +{ + return false; +} + +Icon *Mob::getItemInHandIcon(shared_ptr item, int layer) +{ + return item->getIcon(); +} + +// 4J added so we can not render mobs before their chunks are loaded - to resolve bug 10327 :Gameplay: NPCs can spawn over chunks that have not yet been streamed and display jitter. +bool Mob::shouldRender(Vec3 *c) +{ + if( !level->reallyHasChunksAt( Mth::floor(bb->x0), Mth::floor(bb->y0), Mth::floor(bb->z0), Mth::floor(bb->x1), Mth::floor(bb->y1), Mth::floor(bb->z1))) + { + return false; + } + return Entity::shouldRender(c); +} + +void Mob::tickEffects() +{ + bool removed = false; + for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end();) + { + MobEffectInstance *effect = it->second; + removed = false; + if (!effect->tick(dynamic_pointer_cast(shared_from_this()))) + { + if (!level->isClientSide) + { + it = activeEffects.erase( it ); + onEffectRemoved(effect); + delete effect; + removed = true; + } + } + if(!removed) + { + ++it; + } + } + if (effectsDirty) + { + if (!level->isClientSide) + { + if (activeEffects.empty()) + { + entityData->set(DATA_EFFECT_COLOR_ID, (int) 0); + setInvisible(false); + setWeakened(false); + } + else + { + vector values; + for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end();++it) + { + values.push_back(it->second); + } + int colorValue = PotionBrewing::getColorValue(&values); + values.clear(); + entityData->set(DATA_EFFECT_COLOR_ID, colorValue); + setInvisible(hasEffect(MobEffect::invisibility->id)); + setWeakened(hasEffect(MobEffect::weakness->id)); + } + } + effectsDirty = false; + } + if (random->nextBoolean()) + { + int colorValue = entityData->getInteger(DATA_EFFECT_COLOR_ID); + if (colorValue > 0) + { + double red = (double) ((colorValue >> 16) & 0xff) / 255.0; + double green = (double) ((colorValue >> 8) & 0xff) / 255.0; + double blue = (double) ((colorValue >> 0) & 0xff) / 255.0; + + level->addParticle(eParticleType_mobSpell, x + (random->nextDouble() - 0.5) * bbWidth, y + random->nextDouble() * bbHeight - heightOffset, z + (random->nextDouble() - 0.5) * bbWidth, red, green, blue); + } + } +} + +void Mob::removeAllEffects() +{ + //Iterator effectIdIterator = activeEffects.keySet().iterator(); + //while (effectIdIterator.hasNext()) + for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ) + { + //Integer effectId = effectIdIterator.next(); + MobEffectInstance *effect = it->second;//activeEffects.get(effectId); + + if (!level->isClientSide) + { + //effectIdIterator.remove(); + it = activeEffects.erase(it); + onEffectRemoved(effect); + delete effect; + } + else + { + ++it; + } + } +} + +vector *Mob::getActiveEffects() +{ + vector *active = new vector(); + + for(AUTO_VAR(it, activeEffects.begin()); it != activeEffects.end(); ++it) + { + active->push_back(it->second); + } + + return active; +} + +bool Mob::hasEffect(int id) +{ + return activeEffects.find(id) != activeEffects.end();; +} + +bool Mob::hasEffect(MobEffect *effect) +{ + return activeEffects.find(effect->id) != activeEffects.end(); +} + +MobEffectInstance *Mob::getEffect(MobEffect *effect) +{ + MobEffectInstance *effectInst = NULL; + + AUTO_VAR(it, activeEffects.find(effect->id)); + if(it != activeEffects.end() ) effectInst = it->second; + + return effectInst; +} + +void Mob::addEffect(MobEffectInstance *newEffect) +{ + if (!canBeAffected(newEffect)) + { + return; + } + + if (activeEffects.find(newEffect->getId()) != activeEffects.end() ) + { + // replace effect and update + MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second; + effectInst->update(newEffect); + onEffectUpdated(effectInst); + } + else + { + activeEffects.insert( unordered_map::value_type( newEffect->getId(), newEffect ) ); + onEffectAdded(newEffect); + } +} + +// 4J Added +void Mob::addEffectNoUpdate(MobEffectInstance *newEffect) +{ + if (!canBeAffected(newEffect)) + { + return; + } + + if (activeEffects.find(newEffect->getId()) != activeEffects.end() ) + { + // replace effect and update + MobEffectInstance *effectInst = activeEffects.find(newEffect->getId())->second; + effectInst->update(newEffect); + } + else + { + activeEffects.insert( unordered_map::value_type( newEffect->getId(), newEffect ) ); + } +} + +bool Mob::canBeAffected(MobEffectInstance *newEffect) +{ + if (getMobType() == UNDEAD) + { + int id = newEffect->getId(); + if (id == MobEffect::regeneration->id || id == MobEffect::poison->id) + { + return false; + } + } + + return true; +} + +bool Mob::isInvertedHealAndHarm() +{ + return getMobType() == UNDEAD; +} + +void Mob::removeEffectNoUpdate(int effectId) +{ + AUTO_VAR(it, activeEffects.find(effectId)); + if (it != activeEffects.end()) + { + MobEffectInstance *effect = it->second; + if(effect != NULL) + { + delete effect; + } + activeEffects.erase(it); + } +} + +void Mob::removeEffect(int effectId) +{ + AUTO_VAR(it, activeEffects.find(effectId)); + if (it != activeEffects.end()) + { + MobEffectInstance *effect = it->second; + if(effect != NULL) + { + onEffectRemoved(effect); + delete effect; + } + activeEffects.erase(it); + } +} + +void Mob::onEffectAdded(MobEffectInstance *effect) +{ + effectsDirty = true; +} + +void Mob::onEffectUpdated(MobEffectInstance *effect) +{ + effectsDirty = true; +} + +void Mob::onEffectRemoved(MobEffectInstance *effect) +{ + effectsDirty = true; +} + +float Mob::getWalkingSpeedModifier() +{ + float speed = 1.0f; + if (hasEffect(MobEffect::movementSpeed)) + { + speed *= 1.0f + .2f * (getEffect(MobEffect::movementSpeed)->getAmplifier() + 1); + } + if (hasEffect(MobEffect::movementSlowdown)) + { + speed *= 1.0f - .15f * (getEffect(MobEffect::movementSlowdown)->getAmplifier() + 1); + } + return speed; +} + +void Mob::teleportTo(double x, double y, double z) +{ + moveTo(x, y, z, yRot, xRot); +} + +bool Mob::isBaby() +{ + return false; +} + +MobType Mob::getMobType() +{ + return UNDEFINED; +} + +void Mob::breakItem(shared_ptr itemInstance) +{ + level->playSound(shared_from_this(), eSoundType_RANDOM_BREAK, 0.8f, 0.8f + level->random->nextFloat() * 0.4f); + + for (int i = 0; i < 5; i++) + { + Vec3 *d = Vec3::newTemp((random->nextFloat() - 0.5) * 0.1, Math::random() * 0.1 + 0.1, 0); + d->xRot(-xRot * PI / 180); + d->yRot(-yRot * PI / 180); + + Vec3 *p = Vec3::newTemp((random->nextFloat() - 0.5) * 0.3, -random->nextFloat() * 0.6 - 0.3, 0.6); + p->xRot(-xRot * PI / 180); + p->yRot(-yRot * PI / 180); + p = p->add(x, y + getHeadHeight(), z); + level->addParticle(PARTICLE_ICONCRACK(itemInstance->getItem()->id,0), p->x, p->y, p->z, d->x, d->y + 0.05, d->z); + } +} + +bool Mob::isInvulnerable() +{ + // 4J-JEV: I have no idea what was going on here (it gets changed in a later java version). + return invulnerableTime > 0; // invulnerableTime <= invulnerableTime / 2; +} + +void Mob::setLevel(Level *level) +{ + Entity::setLevel(level); + navigation->setLevel(level); + goalSelector.setLevel(level); + targetSelector.setLevel(level); +} + +void Mob::finalizeMobSpawn() +{ + +} + +bool Mob::canBeControlledByRider() +{ + return false; +} diff --git a/Minecraft.World/Mob.h b/Minecraft.World/Mob.h new file mode 100644 index 00000000..0e1af2be --- /dev/null +++ b/Minecraft.World/Mob.h @@ -0,0 +1,369 @@ +#pragma once +using namespace std; + +#include "Entity.h" +#include "MobType.h" +#include "GoalSelector.h" + +class HitResult; +class Level; +class CompoundTag; +class MobEffectInstance; +class DamageSource; +class MobEffect; +class LookControl; +class MoveControl; +class JumpControl; +class BodyControl; +class PathNavigation; +class Sensing; +class Icon; +class Pos; + +class Mob : public Entity +{ + friend class MobSpawner; +protected: + // 4J - added for common ctor code + void _init(); +public: + Mob(Level* level); + virtual ~Mob(); + + // 4J-PB - added to replace (e instanceof Type), avoiding dynamic casts + eINSTANCEOF GetType() { return eTYPE_MOB;} + static Entity *create(Level *level) { return NULL; } + +public: + static const int ATTACK_DURATION = 5; + static const int PLAYER_HURT_EXPERIENCE_TIME = 20 * 3; + +public: // 4J Stu - Made public + static const int DATA_EFFECT_COLOR_ID = 8; + +private: + static const double MIN_MOVEMENT_DISTANCE; + +public: + int invulnerableDuration; + float timeOffs; + float rotA; + float yBodyRot, yBodyRotO; + float yHeadRot, yHeadRotO; + +protected: + float oRun, run; + float animStep, animStepO; + bool hasHair; + // wstring textureName; + int textureIdx; // 4J changed from wstring textureName + bool allowAlpha; + float rotOffs; + wstring modelName; + float bobStrength; + int deathScore; + float renderOffset; + +public: + float walkingSpeed; + float flyingSpeed; + float oAttackAnim, attackAnim; + +protected: + int health; + +public: + int lastHealth; + +protected: + int dmgSpill; + +public: + int ambientSoundTime; + int hurtTime; + int hurtDuration; + float hurtDir; + int deathTime; + int attackTime; + float oTilt, tilt; + +protected: + bool dead; + int xpReward; + +public: + int modelNum; + float animSpeed; + float walkAnimSpeedO; + float walkAnimSpeed; + float walkAnimPos; + +protected: + shared_ptr lastHurtByPlayer; + int lastHurtByPlayerTime; + +private: + shared_ptr lastHurtByMob; + int lastHurtByMobTime; + shared_ptr lastHurtMob; + +public: + int arrowCount; + int removeArrowTime; + +protected: + map activeEffects; + +private: + bool effectsDirty; + int effectColor; + + LookControl *lookControl; + MoveControl *moveControl; + JumpControl *jumpControl; + BodyControl *bodyControl; + PathNavigation *navigation; + +protected: + GoalSelector goalSelector; + GoalSelector targetSelector; + +private: + shared_ptr target; + Sensing *sensing; + float speed; + + Pos *restrictCenter; + float restrictRadius; + +public: + virtual LookControl *getLookControl(); + virtual MoveControl *getMoveControl(); + virtual JumpControl *getJumpControl(); + virtual PathNavigation *getNavigation(); + virtual Sensing *getSensing(); + virtual Random *getRandom(); + virtual shared_ptr getLastHurtByMob(); + virtual shared_ptr getLastHurtMob(); + void setLastHurtMob(shared_ptr target); + virtual int getNoActionTime(); + float getYHeadRot(); + void setYHeadRot(float yHeadRot); + float getSpeed(); + void setSpeed(float speed); + virtual bool doHurtTarget(shared_ptr target); + shared_ptr getTarget(); + virtual void setTarget(shared_ptr target); + virtual bool canAttackType(eINSTANCEOF targetType); + virtual void ate(); + + bool isWithinRestriction(); + bool isWithinRestriction(int x, int y, int z); + void restrictTo(int x, int y, int z, int radius); + Pos *getRestrictCenter(); + float getRestrictRadius(); + void clearRestriction(); + bool hasRestriction(); + + virtual void setLastHurtByMob(shared_ptr hurtBy); + +protected: + virtual void defineSynchedData(); + +public: + bool canSee(shared_ptr target); + virtual int getTexture(); // 4J - changed from wstring to int + virtual bool isPickable() ; + virtual bool isPushable(); + virtual float getHeadHeight(); + virtual int getAmbientSoundInterval(); + void playAmbientSound(); + virtual void baseTick(); + +protected: + virtual void tickDeath(); + virtual int decreaseAirSupply(int currentSupply); + virtual int getExperienceReward(shared_ptr killedBy); + virtual bool isAlwaysExperienceDropper(); + +public: + void spawnAnim(); + virtual void rideTick(); + +protected: + int lSteps; + double lx, ly, lz, lyr, lxr; + +public: + virtual void lerpTo(double x, double y, double z, float yRot, float xRot, int steps); + +private: + float fallTime; + +public: + void superTick(); + virtual void tick(); + virtual void heal(int heal); + virtual int getMaxHealth() = 0; + virtual int getHealth(); + virtual void setHealth(int health); + +protected: + int lastHurt; + +public: + virtual bool hurt(DamageSource *source, int dmg); + +protected: + float getVoicePitch(); + +public: + virtual void animateHurt(); + + /** + * Fetches the mob's armor value, from 0 (no armor) to 20 (full armor) + * + * @return + */ + virtual int getArmorValue(); + +protected: + virtual void hurtArmor(int damage); + virtual int getDamageAfterArmorAbsorb(DamageSource *damageSource, int damage); + virtual int getDamageAfterMagicAbsorb(DamageSource *damageSource, int damage); + + virtual void actuallyHurt(DamageSource *source, int dmg); + virtual float getSoundVolume(); + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + +public: + void knockback(shared_ptr source, int dmg, double xd, double zd); + virtual void die(DamageSource *source); + +protected: + virtual void dropRareDeathLoot(int rareLootLevel); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + virtual int getDeathLoot(); + virtual void causeFallDamage(float distance); + +public: + virtual void travel(float xa, float ya); + virtual bool onLadder(); + virtual bool isShootable(); + virtual void addAdditonalSaveData(CompoundTag *entityTag); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual bool isAlive(); + virtual bool isWaterMob(); + virtual int getLightColor(float a); // 4J - added + +protected: + int noActionTime; + float xxa, yya, yRotA; + bool jumping; + float defaultLookAngle; + float runSpeed; +protected: + int noJumpDelay; + +public: + virtual void setYya(float yya); + virtual void setJumping(bool jump); + + virtual void aiStep(); + +protected: + virtual bool useNewAi(); + virtual bool isEffectiveAI(); + virtual bool isImmobile(); + +public: + virtual bool isBlocking(); + +protected: + virtual void jumpFromGround(); + virtual bool removeWhenFarAway(); + +private: + shared_ptr lookingAt; + +protected: + int lookTime; + + virtual void checkDespawn(); + virtual void newServerAiStep(); + virtual void serverAiMobStep(); + virtual void serverAiStep(); + +public: + virtual int getMaxHeadXRot(); + +protected: + void lookAt(shared_ptr e, float yMax, float xMax); + bool isLookingAtAnEntity(); + shared_ptr getLookingAt(); + +private: + float rotlerp(float a, float b, float max); + +public: + virtual bool canSpawn(); + +protected: + virtual void outOfWorld(); + +public: + float getAttackAnim(float a); + virtual Vec3 *getPos(float a); + virtual Vec3 *getLookAngle(); + Vec3 *getViewVector(float a); + virtual float getSizeScale(); + virtual float getHeadSizeScale(); + HitResult *pick(double range, float a); + virtual int getMaxSpawnClusterSize(); + virtual shared_ptr getCarriedItem(); + virtual shared_ptr getArmor(int pos); + virtual void handleEntityEvent(byte id); + virtual bool isSleeping(); + virtual Icon *getItemInHandIcon(shared_ptr item, int layer); + virtual bool shouldRender(Vec3 *c); + +protected: + void tickEffects(); + +public: + void removeAllEffects(); + vector *getActiveEffects(); + bool hasEffect(int id); + bool hasEffect(MobEffect *effect); + MobEffectInstance *getEffect(MobEffect *effect); + void addEffect(MobEffectInstance *newEffect); + void addEffectNoUpdate(MobEffectInstance *newEffect); // 4J Added + virtual bool canBeAffected(MobEffectInstance *newEffect); + virtual bool isInvertedHealAndHarm(); + void removeEffectNoUpdate(int effectId); + void removeEffect(int effectId); + +protected: + virtual void onEffectAdded(MobEffectInstance *effect); + virtual void onEffectUpdated(MobEffectInstance *effect); + virtual void onEffectRemoved(MobEffectInstance *effect); + +public: + virtual float getWalkingSpeedModifier(); + + // 4J-Pb added (from 1.2.3) + virtual void teleportTo(double x, double y, double z); + virtual bool isBaby(); + virtual MobType getMobType(); + virtual void breakItem(shared_ptr itemInstance); + + virtual bool isInvulnerable(); + + virtual void finalizeMobSpawn(); + virtual bool canBeControlledByRider(); + + // 4J Added override to update ai elements when loading entity from schematics + virtual void setLevel(Level *level); +}; diff --git a/Minecraft.World/MobCategory.cpp b/Minecraft.World/MobCategory.cpp new file mode 100644 index 00000000..787b2004 --- /dev/null +++ b/Minecraft.World/MobCategory.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.monster.h" +#include "Creature.h" +#include "Material.h" +#include "MobCategory.h" + +MobCategory *MobCategory::monster = NULL; +MobCategory *MobCategory::creature = NULL; +MobCategory *MobCategory::waterCreature = NULL; +// 4J - added these extra categories +MobCategory *MobCategory::creature_wolf = NULL; +MobCategory *MobCategory::creature_chicken = NULL; +MobCategory *MobCategory::creature_mushroomcow = NULL; + +MobCategoryArray MobCategory::values = MobCategoryArray(6); + +void MobCategory::staticCtor() +{ + // 4J - adjusted the max levels here for the xbox version, which now represent the max levels in the whole world + monster = new MobCategory(70, Material::air, false, eTYPE_MONSTER, false, CONSOLE_MONSTERS_HARD_LIMIT); + creature = new MobCategory(10, Material::air, true, eTYPE_ANIMALS_SPAWN_LIMIT_CHECK, false, CONSOLE_ANIMALS_HARD_LIMIT); + waterCreature = new MobCategory(5, Material::water, true, eTYPE_WATERANIMAL, false, CONSOLE_SQUID_HARD_LIMIT); + + values[0] = monster; + values[1] = creature; + values[2] = waterCreature; + // 4J - added 2 new categories to give us better control over spawning wolves & chickens + creature_wolf = new MobCategory(3, Material::air, true, eTYPE_WOLF, true, MAX_XBOX_WOLVES); + creature_chicken = new MobCategory( 2, Material::air, true, eTYPE_CHICKEN, true, MAX_XBOX_CHICKENS); + creature_mushroomcow = new MobCategory(2, Material::air, true, eTYPE_MUSHROOMCOW, true, MAX_XBOX_MUSHROOMCOWS); + values[3] = creature_wolf; + values[4] = creature_chicken; + values[5] = creature_mushroomcow; +} + +MobCategory::MobCategory(int maxVar, Material *spawnPositionMaterial, bool isFriendly, eINSTANCEOF eBase, bool isSingleType, int maxPerLevel) + : m_max(maxVar), spawnPositionMaterial(spawnPositionMaterial), m_isFriendly(isFriendly), m_eBase(eBase), m_isSingleType(isSingleType), m_maxPerLevel(maxPerLevel) +{ +} + +// 4J - added +const eINSTANCEOF MobCategory::getEnumBaseClass() +{ + return m_eBase; +} + +int MobCategory::getMaxInstancesPerChunk() +{ + return m_max; +} + +int MobCategory::getMaxInstancesPerLevel() // 4J added +{ + return m_maxPerLevel; +} + +Material *MobCategory::getSpawnPositionMaterial() +{ + return (Material *) spawnPositionMaterial; +} + +bool MobCategory::isFriendly() +{ + return m_isFriendly; +} + +bool MobCategory::isSingleType() +{ + return m_isSingleType; +} diff --git a/Minecraft.World/MobCategory.h b/Minecraft.World/MobCategory.h new file mode 100644 index 00000000..a978c40e --- /dev/null +++ b/Minecraft.World/MobCategory.h @@ -0,0 +1,82 @@ +#pragma once +using namespace std; + +class Material; + +class MobCategory +{ +public: + // 4J - putting constants for xbox spawning in one place to tidy things up a bit - all numbers are per level + static const int CONSOLE_MONSTERS_HARD_LIMIT = 50; // Max number of enemies (skeleton, zombie, creeper etc) that the mob spawner will produce + static const int CONSOLE_ANIMALS_HARD_LIMIT = 50; // Max number of animals (cows, sheep, pigs) that the mob spawner will produce + + static const int MAX_XBOX_CHICKENS = 8; // Max number of chickens that the mob spawner will produce + static const int MAX_XBOX_WOLVES = 8; // Max number of wolves that the mob spawner will produce + static const int MAX_XBOX_MUSHROOMCOWS = 2; // Max number of mushroom cows that the mob spawner will produce + static const int MAX_XBOX_SNOWMEN = 16; // Max number of snow golems that can be created by placing blocks - 4J-PB increased limit due to player requests + static const int MAX_XBOX_IRONGOLEM = 16; // Max number of iron golems that can be created by placing blocks - 4J-PB increased limit due to player requests + static const int CONSOLE_SQUID_HARD_LIMIT = 5; + + static const int MAX_XBOX_ANIMALS_WITH_BREEDING = CONSOLE_ANIMALS_HARD_LIMIT + 20; // Max number of animals that we can produce (in total), when breeding + static const int MAX_XBOX_CHICKENS_WITH_BREEDING = MAX_XBOX_CHICKENS + 8; // Max number of chickens that we can produce (in total), when breeding/hatching + static const int MAX_XBOX_MUSHROOMCOWS_WITH_BREEDING = MAX_XBOX_MUSHROOMCOWS + 20; // Max number of mushroom cows that we can produce (in total), when breeding + static const int MAX_XBOX_WOLVES_WITH_BREEDING = MAX_XBOX_WOLVES + 8; // Max number of wolves that we can produce (in total), when breeding + static const int MAX_VILLAGERS_WITH_BREEDING = 35; + + static const int MAX_XBOX_ANIMALS_WITH_SPAWN_EGG = MAX_XBOX_ANIMALS_WITH_BREEDING + 20; + static const int MAX_XBOX_CHICKENS_WITH_SPAWN_EGG = MAX_XBOX_CHICKENS_WITH_BREEDING + 10; + static const int MAX_XBOX_WOLVES_WITH_SPAWN_EGG = MAX_XBOX_WOLVES_WITH_BREEDING + 10; + static const int MAX_XBOX_MONSTERS_WITH_SPAWN_EGG = CONSOLE_MONSTERS_HARD_LIMIT + 20; + static const int MAX_XBOX_VILLAGERS_WITH_SPAWN_EGG = MAX_VILLAGERS_WITH_BREEDING + 15; // 4J-PB - increased this limit due to player requests + static const int MAX_XBOX_MUSHROOMCOWS_WITH_SPAWN_EGG = MAX_XBOX_MUSHROOMCOWS_WITH_BREEDING + 8; + static const int MAX_XBOX_SQUIDS_WITH_SPAWN_EGG = CONSOLE_SQUID_HARD_LIMIT + 8; + + /* + Maximum animals = 50 + 20 + 20 = 90 + Maximum monsters = 50 + 20 = 70 + Maximum chickens = 8 + 8 + 10 = 26 + Maximum wolves = 8 + 8 + 10 = 26 + Maximum mooshrooms = 2 + 20 + 8 = 30 + Maximum snowmen = 16 + Maximum iron golem = 16 + Maximum squid = 5 + 8 = 13 + Maximum villagers = 35 + 15 = 50 + + Maximum natural = 50 + 50 + 8 + 8 + 2 + 5 + 35 = 158 + Total maxium = 90 + 70 + 26 + 26 + 30 + 16 + 16 + 13 + 50 = 337 + */ + + static MobCategory *monster; + static MobCategory *creature; + static MobCategory *waterCreature; + // 4J added extra categories, to break these out of general creatures & give us more control of levels + static MobCategory *creature_wolf; + static MobCategory *creature_chicken; + static MobCategory *creature_mushroomcow; + + // 4J Stu Sometimes we want to access the values by name, other times iterate over all values + // Added these arrays so we can static initialise a collection which we can iterate over + static MobCategoryArray values; + + +private: + const int m_max; + const int m_maxPerLevel; + const Material *spawnPositionMaterial; + const bool m_isFriendly; + const bool m_isSingleType; // 4J Added + const eINSTANCEOF m_eBase; // 4J added + + MobCategory(int maxVar, Material *spawnPositionMaterial, bool isFriendly, eINSTANCEOF eBase, bool isSingleType, int maxPerLevel); + +public: + const type_info getBaseClass(); + const eINSTANCEOF getEnumBaseClass(); // 4J added + int getMaxInstancesPerChunk(); + int getMaxInstancesPerLevel(); // 4J added + Material *getSpawnPositionMaterial(); + bool isFriendly(); + bool isSingleType(); +public: + static void staticCtor(); +}; diff --git a/Minecraft.World/MobEffect.cpp b/Minecraft.World/MobEffect.cpp new file mode 100644 index 00000000..f9687a15 --- /dev/null +++ b/Minecraft.World/MobEffect.cpp @@ -0,0 +1,270 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.food.h" +#include "net.minecraft.world.effect.h" +#include "SharedConstants.h" + +MobEffect *MobEffect::effects[NUM_EFFECTS]; + +MobEffect *MobEffect::voidEffect = NULL; +MobEffect *MobEffect::movementSpeed = (new MobEffect(1, false, eMinecraftColour_Effect_MovementSpeed)) ->setDescriptionId(IDS_POTION_MOVESPEED) ->setPostfixDescriptionId(IDS_POTION_MOVESPEED_POSTFIX)->setIcon(MobEffect::e_MobEffectIcon_Speed); //setIcon(0, 0); +MobEffect *MobEffect::movementSlowdown = (new MobEffect(2, true, eMinecraftColour_Effect_MovementSlowDown)) ->setDescriptionId(IDS_POTION_MOVESLOWDOWN) ->setPostfixDescriptionId(IDS_POTION_MOVESLOWDOWN_POSTFIX)->setIcon(MobEffect::e_MobEffectIcon_Slowness); //->setIcon(1, 0); +MobEffect *MobEffect::digSpeed = (new MobEffect(3, false, eMinecraftColour_Effect_DigSpeed)) ->setDescriptionId(IDS_POTION_DIGSPEED) ->setPostfixDescriptionId(IDS_POTION_DIGSPEED_POSTFIX)->setDurationModifier(1.5)->setIcon(MobEffect::e_MobEffectIcon_Haste); //->setIcon(2, 0); +MobEffect *MobEffect::digSlowdown = (new MobEffect(4, true, eMinecraftColour_Effect_DigSlowdown)) ->setDescriptionId(IDS_POTION_DIGSLOWDOWN) ->setPostfixDescriptionId(IDS_POTION_DIGSLOWDOWN_POSTFIX)->setIcon(MobEffect::e_MobEffectIcon_MiningFatigue); //->setIcon(3, 0); +MobEffect *MobEffect::damageBoost = (new MobEffect(5, false, eMinecraftColour_Effect_DamageBoost)) ->setDescriptionId(IDS_POTION_DAMAGEBOOST) ->setPostfixDescriptionId(IDS_POTION_DAMAGEBOOST_POSTFIX)->setIcon(MobEffect::e_MobEffectIcon_Strength); //->setIcon(4, 0); +MobEffect *MobEffect::heal = (new InstantenousMobEffect(6, false, eMinecraftColour_Effect_Heal)) ->setDescriptionId(IDS_POTION_HEAL) ->setPostfixDescriptionId(IDS_POTION_HEAL_POSTFIX); +MobEffect *MobEffect::harm = (new InstantenousMobEffect(7, true, eMinecraftColour_Effect_Harm)) ->setDescriptionId(IDS_POTION_HARM) ->setPostfixDescriptionId(IDS_POTION_HARM_POSTFIX); +MobEffect *MobEffect::jump = (new MobEffect(8, false, eMinecraftColour_Effect_Jump)) ->setDescriptionId(IDS_POTION_JUMP) ->setPostfixDescriptionId(IDS_POTION_JUMP_POSTFIX)->setIcon(MobEffect::e_MobEffectIcon_JumpBoost); //->setIcon(2, 1); +MobEffect *MobEffect::confusion = (new MobEffect(9, true, eMinecraftColour_Effect_Confusion)) ->setDescriptionId(IDS_POTION_CONFUSION) ->setPostfixDescriptionId(IDS_POTION_CONFUSION_POSTFIX)->setDurationModifier(.25)->setIcon(MobEffect::e_MobEffectIcon_Nausea); //->setIcon(3, 1); +MobEffect *MobEffect::regeneration = (new MobEffect(10, false, eMinecraftColour_Effect_Regeneration)) ->setDescriptionId(IDS_POTION_REGENERATION) ->setPostfixDescriptionId(IDS_POTION_REGENERATION_POSTFIX)->setDurationModifier(.25)->setIcon(MobEffect::e_MobEffectIcon_Regeneration); //->setIcon(7, 0); +MobEffect *MobEffect::damageResistance = (new MobEffect(11, false, eMinecraftColour_Effect_DamageResistance))->setDescriptionId(IDS_POTION_RESISTANCE) ->setPostfixDescriptionId(IDS_POTION_RESISTANCE_POSTFIX)->setIcon(MobEffect::e_MobEffectIcon_Resistance); //->setIcon(6, 1); +MobEffect *MobEffect::fireResistance = (new MobEffect(12, false, eMinecraftColour_Effect_FireResistance)) ->setDescriptionId(IDS_POTION_FIRERESISTANCE) ->setPostfixDescriptionId(IDS_POTION_FIRERESISTANCE_POSTFIX)->setIcon(MobEffect::e_MobEffectIcon_FireResistance); //->setIcon(7, 1); +MobEffect *MobEffect::waterBreathing = (new MobEffect(13, false, eMinecraftColour_Effect_WaterBreathing)) ->setDescriptionId(IDS_POTION_WATERBREATHING) ->setPostfixDescriptionId(IDS_POTION_WATERBREATHING_POSTFIX)->setIcon(MobEffect::e_MobEffectIcon_WaterBreathing); //->setIcon(0, 2); +MobEffect *MobEffect::invisibility = (new MobEffect(14, false, eMinecraftColour_Effect_Invisiblity)) ->setDescriptionId(IDS_POTION_INVISIBILITY) ->setPostfixDescriptionId(IDS_POTION_INVISIBILITY_POSTFIX)->setIcon(MobEffect::e_MobEffectIcon_Invisiblity); //->setIcon(0, 1); +MobEffect *MobEffect::blindness = (new MobEffect(15, true, eMinecraftColour_Effect_Blindness)) ->setDescriptionId(IDS_POTION_BLINDNESS) ->setPostfixDescriptionId(IDS_POTION_BLINDNESS_POSTFIX)->setDurationModifier(.25)->setIcon(MobEffect::e_MobEffectIcon_Blindness); //->setIcon(5, 1); +MobEffect *MobEffect::nightVision = (new MobEffect(16, false, eMinecraftColour_Effect_NightVision)) ->setDescriptionId(IDS_POTION_NIGHTVISION) ->setPostfixDescriptionId(IDS_POTION_NIGHTVISION_POSTFIX)->setIcon(MobEffect::e_MobEffectIcon_NightVision); //->setIcon(4, 1); +MobEffect *MobEffect::hunger = (new MobEffect(17, true, eMinecraftColour_Effect_Hunger)) ->setDescriptionId(IDS_POTION_HUNGER) ->setPostfixDescriptionId(IDS_POTION_HUNGER_POSTFIX)->setIcon(MobEffect::e_MobEffectIcon_Hunger); //->setIcon(1, 1); +MobEffect *MobEffect::weakness = (new MobEffect(18, true, eMinecraftColour_Effect_Weakness)) ->setDescriptionId(IDS_POTION_WEAKNESS) ->setPostfixDescriptionId(IDS_POTION_WEAKNESS_POSTFIX)->setIcon(MobEffect::e_MobEffectIcon_Weakness); //->setIcon(5, 0); +MobEffect *MobEffect::poison = (new MobEffect(19, true, eMinecraftColour_Effect_Poison)) ->setDescriptionId(IDS_POTION_POISON) ->setPostfixDescriptionId(IDS_POTION_POISON_POSTFIX)->setDurationModifier(.25)->setIcon(MobEffect::e_MobEffectIcon_Poison); //->setIcon(6, 0); +MobEffect *MobEffect::reserved_20 = NULL; +MobEffect *MobEffect::reserved_21 = NULL; +MobEffect *MobEffect::reserved_22 = NULL; +MobEffect *MobEffect::reserved_23 = NULL; +MobEffect *MobEffect::reserved_24 = NULL; +MobEffect *MobEffect::reserved_25 = NULL; +MobEffect *MobEffect::reserved_26 = NULL; +MobEffect *MobEffect::reserved_27 = NULL; +MobEffect *MobEffect::reserved_28 = NULL; +MobEffect *MobEffect::reserved_29 = NULL; +MobEffect *MobEffect::reserved_30 = NULL; +MobEffect *MobEffect::reserved_31 = NULL; + + +MobEffect::MobEffect(int id, bool isHarmful, eMinecraftColour color) : id(id), _isHarmful(isHarmful), color(color) +{ + descriptionId = -1; + m_postfixDescriptionId = -1; + icon = e_MobEffectIcon_None; + _isDisabled = false; + + effects[id] = this; + + if (isHarmful) + { + durationModifier = .5; + } + else + { + durationModifier = 1.0; + } +} + +// 4J Removed as using different value for icon +//MobEffect *MobEffect::setIcon(int xPos, int yPos) +//{ +// icon = xPos + yPos * 8; +// return this; +//} + +MobEffect *MobEffect::setIcon(EMobEffectIcon icon) +{ + this->icon = icon; + return this; +} + +int MobEffect::getId() +{ + return id; +} + +/** +* This method should perform periodic updates on the player. Mainly used +* for regeneration effects and the like. Other effects, such as blindness, +* are in effect for the whole duration of the effect. +* +* @param mob +* @param amplification +*/ +void MobEffect::applyEffectTick(shared_ptr mob, int amplification) +{ + + // Maybe move this to separate class implementations in the future? + if (id == regeneration->id) + { + if (mob->getHealth() < mob->getMaxHealth()) + { + mob->heal(1); + } + } + else if (id == poison->id) + { + if (mob->getHealth() > 1) + { + mob->hurt(DamageSource::magic, 1); + } + } + else if (id == hunger->id && dynamic_pointer_cast(mob) != NULL) + { + // every tick, cause the same amount of exhaustion as when removing + // a block, times amplification + dynamic_pointer_cast(mob)->causeFoodExhaustion(FoodConstants::EXHAUSTION_MINE * (amplification + 1)); + } + else if ((id == heal->id && !mob->isInvertedHealAndHarm()) || (id == harm->id && mob->isInvertedHealAndHarm())) + { + mob->heal(6 << amplification); + } + else if ((id == harm->id && !mob->isInvertedHealAndHarm()) || (id == heal->id && mob->isInvertedHealAndHarm())) + { + mob->hurt(DamageSource::magic, 6 << amplification); + } +} + +void MobEffect::applyInstantenousEffect(shared_ptr source, shared_ptr mob, int amplification, double scale) +{ + if ((id == heal->id && !mob->isInvertedHealAndHarm()) || (id == harm->id && mob->isInvertedHealAndHarm())) + { + int amount = (int) (scale * (double) (6 << amplification) + .5); + mob->heal(amount); + } + else if ((id == harm->id && !mob->isInvertedHealAndHarm()) || (id == heal->id && mob->isInvertedHealAndHarm())) + { + int amount = (int) (scale * (double) (6 << amplification) + .5); + if (source == NULL) + { + mob->hurt(DamageSource::magic, amount); + } + else + { + DamageSource *damageSource = DamageSource::indirectMagic(mob, source); + mob->hurt(damageSource, amount); + delete damageSource; + } + } +} + +bool MobEffect::isInstantenous() +{ + return false; +} + +/** +* This parameter says if the applyEffect method should be called depending +* on the remaining duration ticker. For instance, the regeneration will be +* activated every 8 ticks, healing one point of health. +* +* @param remainingDuration +* @param amplification +* Effect amplification, starts at 0 (weakest) +* @return +*/ +bool MobEffect::isDurationEffectTick(int remainingDuration, int amplification) +{ + + // Maybe move this to separate class implementations in the future? + if (id == regeneration->id || id == poison->id) + { + // tick intervals are 25, 12, 6.. + int interval = 25 >> amplification; + if (interval > 0) + { + return (remainingDuration % interval) == 0; + } + return true; + } + else if (id == hunger->id) + { + return true; + } + + return false; +} + +MobEffect *MobEffect::setDescriptionId(unsigned int id) +{ + descriptionId = id; + return this; +} + +unsigned int MobEffect::getDescriptionId(int iData) +{ + return descriptionId; +} + +MobEffect *MobEffect::setPostfixDescriptionId(unsigned int id) +{ + m_postfixDescriptionId = id; + return this; +} + +unsigned int MobEffect::getPostfixDescriptionId(int iData) +{ + return m_postfixDescriptionId; +} + +bool MobEffect::hasIcon() +{ + return icon != e_MobEffectIcon_None; +} + +MobEffect::EMobEffectIcon MobEffect::getIcon() +{ + return icon; +} + +bool MobEffect::isHarmful() +{ + return _isHarmful; +} + +wstring MobEffect::formatDuration(MobEffectInstance *instance) +{ + int duration = instance->getDuration(); + + int seconds = duration / SharedConstants::TICKS_PER_SECOND; + int minutes = seconds / 60; + seconds %= 60; + + wchar_t temp[8]; + ZeroMemory(&temp,8*(sizeof(wchar_t))); + + if (seconds < 10) + { + swprintf(temp, 8, L"%d:0%d",minutes,seconds); + //return minutes + ":0" + seconds; + } + else + { + swprintf(temp, 8, L"%d:%d",minutes,seconds); + //return minutes + ":" + seconds; + } + + return temp; +} + +MobEffect *MobEffect::setDurationModifier(double durationModifier) +{ + this->durationModifier = durationModifier; + return this; +} + +double MobEffect::getDurationModifier() +{ + return durationModifier; +} + +MobEffect *MobEffect::setDisabled() +{ + this->_isDisabled = true; + return this; +} + +bool MobEffect::isDisabled() +{ + return _isDisabled; +} + +eMinecraftColour MobEffect::getColor() +{ + return color; +} \ No newline at end of file diff --git a/Minecraft.World/MobEffect.h b/Minecraft.World/MobEffect.h new file mode 100644 index 00000000..b0460bf1 --- /dev/null +++ b/Minecraft.World/MobEffect.h @@ -0,0 +1,114 @@ +#pragma once +using namespace std; + +class Mob; +class MobEffectInstance; + +class MobEffect +{ +public: + enum EMobEffectIcon + { + e_MobEffectIcon_None, + e_MobEffectIcon_Blindness, + e_MobEffectIcon_FireResistance, + e_MobEffectIcon_Haste, + e_MobEffectIcon_Hunger, + e_MobEffectIcon_Invisiblity, + e_MobEffectIcon_JumpBoost, + e_MobEffectIcon_MiningFatigue, + e_MobEffectIcon_Nausea, + e_MobEffectIcon_NightVision, + e_MobEffectIcon_Poison, + e_MobEffectIcon_Regeneration, + e_MobEffectIcon_Resistance, + e_MobEffectIcon_Slowness, + e_MobEffectIcon_Speed, + e_MobEffectIcon_Strength, + e_MobEffectIcon_WaterBreathing, + e_MobEffectIcon_Weakness, + + e_MobEffectIcon_COUNT, + }; + + static const int NUM_EFFECTS = 32; + static MobEffect *effects[NUM_EFFECTS]; + + static MobEffect *voidEffect; + static MobEffect *movementSpeed; + static MobEffect *movementSlowdown; + static MobEffect *digSpeed; + static MobEffect *digSlowdown; + static MobEffect *damageBoost; + static MobEffect *heal; + static MobEffect *harm; + static MobEffect *jump; + static MobEffect *confusion; + static MobEffect *regeneration; + static MobEffect *damageResistance; + static MobEffect *fireResistance; + static MobEffect *waterBreathing; + static MobEffect *invisibility; + static MobEffect *blindness; + static MobEffect *nightVision; + static MobEffect *hunger; + static MobEffect *weakness; + static MobEffect *poison; + static MobEffect *reserved_20; + static MobEffect *reserved_21; + static MobEffect *reserved_22; + static MobEffect *reserved_23; + static MobEffect *reserved_24; + static MobEffect *reserved_25; + static MobEffect *reserved_26; + static MobEffect *reserved_27; + static MobEffect *reserved_28; + static MobEffect *reserved_29; + static MobEffect *reserved_30; + static MobEffect *reserved_31; + + const int id; + +private: + int descriptionId; + int m_postfixDescriptionId; // 4J added + EMobEffectIcon icon; // 4J changed type + const bool _isHarmful; + double durationModifier; + bool _isDisabled; + const eMinecraftColour color; + +protected: + MobEffect(int id, bool isHarmful, eMinecraftColour color); + + //MobEffect *setIcon(int xPos, int yPos); + MobEffect *setIcon(EMobEffectIcon icon); + +public: + int getId(); + void applyEffectTick(shared_ptr mob, int amplification); + void applyInstantenousEffect(shared_ptr source, shared_ptr mob, int amplification, double scale); + virtual bool isInstantenous(); + virtual bool isDurationEffectTick(int remainingDuration, int amplification); + + MobEffect *setDescriptionId(unsigned int id); + unsigned int getDescriptionId(int iData = -1); + + // 4J Added + MobEffect *setPostfixDescriptionId(unsigned int id); + unsigned int getPostfixDescriptionId(int iData = -1); + + bool hasIcon(); + EMobEffectIcon getIcon(); // 4J changed return type + bool isHarmful(); + static wstring formatDuration(MobEffectInstance *instance); + +protected: + MobEffect *setDurationModifier(double durationModifier); + +public: + double getDurationModifier(); + MobEffect *setDisabled(); + bool isDisabled(); + eMinecraftColour getColor(); +}; \ No newline at end of file diff --git a/Minecraft.World/MobEffectInstance.cpp b/Minecraft.World/MobEffectInstance.cpp new file mode 100644 index 00000000..1e15e58c --- /dev/null +++ b/Minecraft.World/MobEffectInstance.cpp @@ -0,0 +1,139 @@ +#include "stdafx.h" +#include "net.minecraft.world.effect.h" + +void MobEffectInstance::_init(int id, int duration, int amplifier) +{ + this->id = id; + this->duration = duration; + this->amplifier = amplifier; +} + +MobEffectInstance::MobEffectInstance(int id) +{ + _init(id, 0, 0); +} + +MobEffectInstance::MobEffectInstance(int id, int duration) +{ + _init(id, duration, 0); +} + +MobEffectInstance::MobEffectInstance(int id, int duration, int amplifier) +{ + _init(id,duration,amplifier); +} + +MobEffectInstance::MobEffectInstance(MobEffectInstance *copy) +{ + this->id = copy->id; + this->duration = copy->duration; + this->amplifier = copy->amplifier; +} + +void MobEffectInstance::update(MobEffectInstance *takeOver) +{ + if (this->id != takeOver->id) + { + app.DebugPrintf("This method should only be called for matching effects!"); + } + if (takeOver->amplifier > this->amplifier) + { + this->amplifier = takeOver->amplifier; + this->duration = takeOver->duration; + } + else if (takeOver->amplifier == this->amplifier && this->duration < takeOver->duration) + { + this->duration = takeOver->duration; + } +} + +int MobEffectInstance::getId() +{ + return id; +} + +int MobEffectInstance::getDuration() +{ + return duration; +} + +int MobEffectInstance::getAmplifier() +{ + return amplifier; +} + +/** +* Runs the effect on a Mob target. +* +* @param target +* @return True if the effect is still active. +*/ +bool MobEffectInstance::tick(shared_ptr target) +{ + if (duration > 0) + { + if (MobEffect::effects[id]->isDurationEffectTick(duration, amplifier)) + { + applyEffect(target); + } + tickDownDuration(); + } + return duration > 0; +} + +int MobEffectInstance::tickDownDuration() +{ + return --duration; +} + +void MobEffectInstance::applyEffect(shared_ptr mob) +{ + if (duration > 0) + { + MobEffect::effects[id]->applyEffectTick(mob, amplifier); + } +} + +int MobEffectInstance::getDescriptionId() +{ + return MobEffect::effects[id]->getDescriptionId(); +} + +// 4J Added +int MobEffectInstance::getPostfixDescriptionId() +{ + return MobEffect::effects[id]->getPostfixDescriptionId(); +} + +int MobEffectInstance::hashCode() +{ + //return id; + + // 4J Stu - Changed this to return a value that represents id, amp and duration + return (id & 0xff) | ( (amplifier & 0xff) << 8) | ( (duration & 0xffff) << 16); +} + +wstring MobEffectInstance::toString() +{ + wstring result = L"MobEffectInstance::toString - NON IMPLEMENTED OR LOCALISED FUNCTION"; + //wstring result = ""; + //if (getAmplifier() > 0) + //{ + // result = getDescriptionId() + " x " + (getAmplifier() + 1) + ", Duration: " + getDuration(); + //} + //else + //{ + // result = getDescriptionId() + ", Duration: " + getDuration(); + //} + //if (MobEffect.effects[id].isDisabled()) + //{ + // return "(" + result + ")"; + //} + return result; +} + +// Was bool equals(Object obj) +bool MobEffectInstance::equals(MobEffectInstance *obj) +{ + return this->id == obj->id && this->amplifier == obj->amplifier && this->duration == obj->duration; +} \ No newline at end of file diff --git a/Minecraft.World/MobEffectInstance.h b/Minecraft.World/MobEffectInstance.h new file mode 100644 index 00000000..d1c716e6 --- /dev/null +++ b/Minecraft.World/MobEffectInstance.h @@ -0,0 +1,42 @@ +#pragma once + +class Mob; + +class MobEffectInstance +{ +private: + // sent as byte + int id; + // sent as short + int duration; + // sent as byte + int amplifier; + + void _init(int id, int duration, int amplifier); + +public: + MobEffectInstance(int id); + MobEffectInstance(int id, int duration); + MobEffectInstance(int id, int duration, int amplifier); + MobEffectInstance(MobEffectInstance *copy); + + void update(MobEffectInstance *takeOver); + int getId(); + int getDuration(); + int getAmplifier(); + bool tick(shared_ptr target); + +private: + int tickDownDuration(); + +public: + void applyEffect(shared_ptr mob); + int getDescriptionId(); + int getPostfixDescriptionId(); // 4J Added + int hashCode(); + + wstring toString(); + + // Was bool equals(Object obj) + bool equals(MobEffectInstance *obj); +}; \ No newline at end of file diff --git a/Minecraft.World/MobSpawner.cpp b/Minecraft.World/MobSpawner.cpp new file mode 100644 index 00000000..d2b4f6fd --- /dev/null +++ b/Minecraft.World/MobSpawner.cpp @@ -0,0 +1,651 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.level.pathfinder.h" +#include "net.minecraft.world.level.tile.h" +#include "Difficulty.h" +#include "WeighedRandom.h" +#include "Level.h" +#include "ChunkPos.h" +#include "TilePos.h" +#include "..\Minecraft.Client\ServerLevel.h" +#include "MobSpawner.h" +#include "Dimension.h" + +const int MobSpawner::MIN_SPAWN_DISTANCE = 24; + +TilePos MobSpawner::getRandomPosWithin(Level *level, int cx, int cz) +{ + // 4J Stu - Added 1.2.3 but we don't need it as it was only used to access sections + // Leaving here though to help explain why chunk coords are not passed in rather than full coords + //LevelChunk *chunk = level->getChunk(cx, cz); + int x = cx * 16 + level->random->nextInt(16); + int y = level->random->nextInt(level->getHeight()); + int z = cz * 16 + level->random->nextInt(16); + + return TilePos(x, y, z); +} + +#ifdef __PSVITA__ + // AP - See CustomMap.h for an explanation of this + CustomMap MobSpawner::chunksToPoll; +#else + unordered_map MobSpawner::chunksToPoll; +#endif + +const int MobSpawner::tick(ServerLevel *level, bool spawnEnemies, bool spawnFriendlies) +{ +#ifndef _CONTENT_PACKAGE + +#if 0 + // PIX output for mob counters - generally disabling as Entity::countFlagsForPIX is reasonably expensive + if( level->dimension->id == 0 ) + { + Entity::countFlagsForPIX(); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_WATERANIMAL ,false), "eTYPE_WATERANIMAL"); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_ANIMALS_SPAWN_LIMIT_CHECK ,false), "eTYPE_ANIMAL"); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_MONSTER ,false), "eTYPE_MONSTER"); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_SQUID ,true ), "eTYPE_SQUID"); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_VILLAGER ,true ), "eTYPE_VILLAGER"); + + unsigned int totalCount[4]; + unsigned int protectedCount[4]; + unsigned int unprotectedCount[4]; + unsigned int couldWanderCount[4]; + + totalCount[0] = level->countInstanceOf(eTYPE_COW ,true, &protectedCount[0], &couldWanderCount[0] ); + totalCount[1] = level->countInstanceOf(eTYPE_SHEEP ,true, &protectedCount[1], &couldWanderCount[1] ); + totalCount[2] = level->countInstanceOf(eTYPE_CHICKEN ,true, &protectedCount[2], &couldWanderCount[2] ); + totalCount[3] = level->countInstanceOf(eTYPE_PIG ,true, &protectedCount[3], &couldWanderCount[3] ); + + for( int i = 0; i < 4; i++ ) unprotectedCount[i] = totalCount[i] - protectedCount[i]; + + PIXAddNamedCounter( unprotectedCount[0], "eTYPE_COW (unprotected)"); + PIXAddNamedCounter( unprotectedCount[1], "eTYPE_SHEEP (unprotected)"); + PIXAddNamedCounter( unprotectedCount[2], "eTYPE_CHICKEN (unprotected)"); + PIXAddNamedCounter( unprotectedCount[3], "eTYPE_PIG (unprotected)"); + + PIXAddNamedCounter( protectedCount[0], "eTYPE_COW (protected)"); + PIXAddNamedCounter( protectedCount[1], "eTYPE_SHEEP (protected)"); + PIXAddNamedCounter( protectedCount[2], "eTYPE_CHICKEN (protected)"); + PIXAddNamedCounter( protectedCount[3], "eTYPE_PIG (protected)"); + + PIXAddNamedCounter( couldWanderCount[0], "eTYPE_COW (could wander)"); + PIXAddNamedCounter( couldWanderCount[1], "eTYPE_SHEEP (could wander)"); + PIXAddNamedCounter( couldWanderCount[2], "eTYPE_CHICKEN (could wander)"); + PIXAddNamedCounter( couldWanderCount[3], "eTYPE_PIG (could wander)"); + + PIXAddNamedCounter( level->countInstanceOf(eTYPE_WOLF ,true ), "eTYPE_WOLF"); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_CREEPER ,true ), "eTYPE_CREEPER"); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_GIANT ,true ), "eTYPE_GIANT"); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_SKELETON ,true ), "eTYPE_SKELETON"); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_SPIDER ,true ), "eTYPE_SPIDER"); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_ZOMBIE ,true ), "eTYPE_ZOMBIE"); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_PIGZOMBIE ,true ), "eTYPE_PIGZOMBIE"); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_SLIME ,true ), "eTYPE_SLIME"); + PIXAddNamedCounter( level->countInstanceOf(eTYPE_GHAST ,true ), "eTYPE_GHAST"); + } +#endif +#endif + + if (!spawnEnemies && !spawnFriendlies) + { + return 0; + } + MemSect(20); + chunksToPoll.clear(); + +#if 0 + AUTO_VAR(itEnd, level->players.end()); + for (AUTO_VAR(it, level->players.begin()); it != itEnd; it++) + { + shared_ptr player = *it; //level->players.at(i); + int xx = Mth::floor(player->x / 16); + int zz = Mth::floor(player->z / 16); + + int r = 128 / 16; + for (int x = -r; x <= r; x++) + for (int z = -r; z <= r; z++) + { + chunksToPoll.insert(ChunkPos(x + xx, z + zz)); + } + } +#else + // 4J - rewritten to add chunks interleaved by player, and to add them from the centre outwards. We're going to be + // potentially adding less creatures than the original so that our count stays consistent with number of players added, so + // we want to make sure as best we can that the ones we do add are near the active players + int playerCount = (int)level->players.size(); + int *xx = new int[playerCount]; + int *zz = new int[playerCount]; + for (int i = 0; i < playerCount; i++) + { + shared_ptr player = level->players[i]; + xx[i] = Mth::floor(player->x / 16); + zz[i] = Mth::floor(player->z / 16); +#ifdef __PSVITA__ + chunksToPoll.insert(ChunkPos(xx[i], zz[i] ),false); +#else + chunksToPoll.insert(std::pair(ChunkPos(xx[i], zz[i] ),false)); +#endif + } + + for( int r = 1; r <= 8; r++ ) + { + for( int l = 0; l < ( r * 2 ) ; l++ ) + { + for( int i = 0; i < playerCount; i++ ) + { + bool edgeChunk = ( r == 8 ); + + // If this chunk isn't at the edge of the region for this player, then always store with a flag of false + // so that if it was at the edge of another player, then this will remove that + if( !edgeChunk ) + { +#ifdef __PSVITA__ + chunksToPoll.insert(ChunkPos( ( xx[i] - r ) + l , ( zz[i] - r ) ), false); + chunksToPoll.insert(ChunkPos( ( xx[i] + r ) , ( zz[i] - r ) + l ), false); + chunksToPoll.insert(ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r ) ), false); + chunksToPoll.insert(ChunkPos( ( xx[i] - r ) , ( zz[i] + r ) - l ), false); +#else + chunksToPoll.insert(std::pair(ChunkPos( ( xx[i] - r ) + l , ( zz[i] - r ) ), false)); + chunksToPoll.insert(std::pair(ChunkPos( ( xx[i] + r ) , ( zz[i] - r ) + l ), false)); + chunksToPoll.insert(std::pair(ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r ) ), false)); + chunksToPoll.insert(std::pair(ChunkPos( ( xx[i] - r ) , ( zz[i] + r ) - l ), false)); +#endif + } + else + { +#ifdef __PSVITA__ + ChunkPos cp = ChunkPos( ( xx[i] - r ) + l , ( zz[i] - r )); + if( chunksToPoll.find( cp ) ) chunksToPoll.insert(cp, true); + cp = ChunkPos( ( xx[i] + r ), ( zz[i] - r ) + l ); + if( chunksToPoll.find( cp ) ) chunksToPoll.insert(cp, true); + cp = ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r )); + if( chunksToPoll.find( cp ) ) chunksToPoll.insert(cp, true); + cp = ChunkPos( ( xx[i] - r ), ( zz[i] + r ) - l); + if( chunksToPoll.find( cp ) ) chunksToPoll.insert(cp, true); +#else + ChunkPos cp = ChunkPos( ( xx[i] - r ) + l , ( zz[i] - r )); + if( chunksToPoll.find( cp ) == chunksToPoll.end() ) chunksToPoll.insert(std::pair(cp, true)); + cp = ChunkPos( ( xx[i] + r ), ( zz[i] - r ) + l ); + if( chunksToPoll.find( cp ) == chunksToPoll.end() ) chunksToPoll.insert(std::pair(cp, true)); + cp = ChunkPos( ( xx[i] + r ) - l , ( zz[i] + r )); + if( chunksToPoll.find( cp ) == chunksToPoll.end() ) chunksToPoll.insert(std::pair(cp, true)); + cp = ChunkPos( ( xx[i] - r ), ( zz[i] + r ) - l); + if( chunksToPoll.find( cp ) == chunksToPoll.end() ) chunksToPoll.insert(std::pair(cp, true)); +#endif + + } + } + } + } + delete [] xx; + delete [] zz; +#endif + MemSect(0); + int count = 0; + MemSect(31); + Pos *spawnPos = level->getSharedSpawnPos(); + MemSect(0); + + for (unsigned int i = 0; i < MobCategory::values.length; i++) + { + MobCategory *mobCategory = MobCategory::values[i]; + if ((mobCategory->isFriendly() && !spawnFriendlies) || (!mobCategory->isFriendly() && !spawnEnemies)) + { + continue; + } + + // 4J - this is now quite different to the java version. We just have global max counts for the level whereas the original has a max per chunk that + // scales with the number of chunks to be polled. + int categoryCount = level->countInstanceOf( mobCategory->getEnumBaseClass(), mobCategory->isSingleType()); + if( categoryCount >= mobCategory->getMaxInstancesPerLevel()) + { + continue; + } + +#ifdef __PSVITA__ + for( int i = 0;i < chunksToPoll.end();i += 1 ) + { + SCustomMapNode *it = chunksToPoll.get(i); +#else + AUTO_VAR(itEndCTP, chunksToPoll.end()); + for (AUTO_VAR(it, chunksToPoll.begin()); it != itEndCTP; it++) + { +#endif + if( it->second ) + { + // don't add mobs to edge chunks, to prevent adding mobs + // "outside" of the active playground + continue; + } + ChunkPos *cp = (ChunkPos *) (&it->first); + + // 4J - don't let this actually create/load a chunk that isn't here already - we'll let the normal updateDirtyChunks etc. processes do that, so it can happen on another thread + if( !level->hasChunk(cp->x,cp->z) ) continue; + + TilePos start = getRandomPosWithin(level, cp->x, cp->z); + int xStart = start.x; + int yStart = start.y; + int zStart = start.z; + + if (level->isSolidBlockingTile(xStart, yStart, zStart)) continue; + if (level->getMaterial(xStart, yStart, zStart) != mobCategory->getSpawnPositionMaterial()) continue; + int clusterSize = 0; + + for (int dd = 0; dd < 3; dd++) + { + int x = xStart; + int y = yStart; + int z = zStart; + int ss = 6; + + Biome::MobSpawnerData *currentMobType = NULL; + + for (int ll = 0; ll < 4; ll++) + { + x += level->random->nextInt(ss) - level->random->nextInt(ss); + y += level->random->nextInt(1) - level->random->nextInt(1); + z += level->random->nextInt(ss) - level->random->nextInt(ss); + // int y = heightMap[x + z * w] + 1; + + // 4J - don't let this actually create/load a chunk that isn't here already - we'll let the normal updateDirtyChunks etc. processes do that, so it can happen on another thread + if( !level->hasChunkAt( x, y, z ) ) continue; + + if (isSpawnPositionOk(mobCategory, level, x, y, z)) + { + float xx = x + 0.5f; + float yy = (float) y; + float zz = z + 0.5f; + if (level->getNearestPlayer(xx, yy, zz, MIN_SPAWN_DISTANCE) != NULL) + { + continue; + } + else + { + float xd = xx - spawnPos->x; + float yd = yy - spawnPos->y; + float zd = zz - spawnPos->z; + float sd = xd * xd + yd * yd + zd * zd; + if (sd < MIN_SPAWN_DISTANCE * MIN_SPAWN_DISTANCE) + { + continue; + } + } + + if (currentMobType == NULL) + { + currentMobType = level->getRandomMobSpawnAt(mobCategory, x, y, z); + if (currentMobType == NULL) + { + break; + } + } + + shared_ptr mob; + // 4J - removed try/catch +// try +// { + MemSect(29); + //mob = type.mobClass.getConstructor(Level.class).newInstance(level); + mob = dynamic_pointer_cast(EntityIO::newByEnumType(currentMobType->mobClass, level)); + MemSect(0); +// } +// catch (exception e) +// { +// // TODO 4J We can't print a stack trace, and the newInstance function doesn't throw an exception just now anyway +// //e.printStackTrace(); +// return count; +// } + + // 4J - If it is an animal or a monster, don't let any one type of mob represent more than 50% of the total amount of these things. This + // was added initially to stop flat lands being totally populated with slimes but seems like a generally good rule. + eINSTANCEOF mobType = mob->GetType(); + + if( ( mobType & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK ) || ( mobType & eTYPE_MONSTER ) ) + { + // even more special rule for ghasts, because filling up the nether with 25 of them is a bit unpleasant. In the java version they are + // only limited by the fact that the world fills up with pig zombies (the only other type of enemy mob in the nether) before them - they + // aren't actually even counted properly themselves + if( mobType == eTYPE_GHAST ) + { + if( level->countInstanceOf(mobType, true) >= 4 ) continue; + } + else if( mobType == eTYPE_ENDERMAN && level->dimension->id == 1 ) + { + // Special rule for the end, as we only have Endermen (plus the dragon). Increase the spawnable counts based on level difficulty + int maxEndermen = mobCategory->getMaxInstancesPerLevel(); + + if( level->difficulty == Difficulty::NORMAL ) + { + maxEndermen -= mobCategory->getMaxInstancesPerLevel()/4; + } + else if( level->difficulty <= Difficulty::EASY) + { + maxEndermen -= mobCategory->getMaxInstancesPerLevel()/2; + } + + if( level->countInstanceOf(mobType, true) >= maxEndermen ) continue; + } + else if( level->countInstanceOf(mobType, true) >= ( mobCategory->getMaxInstancesPerLevel() / 2 ) ) continue; + } + + mob->moveTo(xx, yy, zz, level->random->nextFloat() * 360, 0); + + if (mob->canSpawn()) + { + // 4J - check if we are going to despawn straight away too, and don't add if we will - otherwise we'll be sending + // network packets for adding & removal that we don't need + mob->checkDespawn(); + if( !mob->removed ) + { + clusterSize++; + categoryCount++; + mob->setDespawnProtected(); // 4J added - default to protected against despawning + level->addEntity(mob); + finalizeMobSettings(mob, level, xx, yy, zz); + mob->finalizeMobSpawn(); + // 4J - change here so that we can't ever make more than the desired amount of entities in each priority. In the original java version + // depending on the random spawn positions being considered the only limit as to the number of entities created per category is the number + // of chunks to poll. + if (categoryCount >= mobCategory->getMaxInstancesPerLevel() ) goto categoryLoop; + if (clusterSize >= mob->getMaxSpawnClusterSize()) goto chunkLoop; + } + } + count += clusterSize; + } + } + } + chunkLoop: continue; + } + categoryLoop: continue; + } + delete spawnPos; + + return count; +} + +bool MobSpawner::isSpawnPositionOk(MobCategory *category, Level *level, int x, int y, int z) +{ + // 4J - don't let this actually create/load a chunk that isn't here already - we'll let the normal updateDirtyChunks etc. processes do that, so it can happen on another thread + if( !level->hasChunkAt(x, y, z ) ) return false; + +#ifdef __PSVITA__ + // AP - added this for Vita. Make sure a new spawn point has 2 chunks around it. This will make sure monsters don't keep getting spawned on the edge preventing other new monsters + // from being spawned + int r = 32; + if( !level->hasChunksAt(x - r, 0, z - r, x + r, 0, z + r)) + { + return false; + } +#endif + + if (category->getSpawnPositionMaterial() == Material::water) + { + // 4J - changed to spawn water things only in deep water + int yo = 0; + int liquidCount = 0; + + while( ( y - yo ) >= 0 && ( yo < 5 ) ) + { + if( level->getMaterial(x, y - yo, z)->isLiquid() ) liquidCount++; + yo++; + } + + // 4J - Sometimes deep water could be just a waterfall, so check that it's wide as well + bool inEnoughWater = false; + if( liquidCount == 5 ) + { + if( level->getMaterial(x+5, y, z)->isLiquid() && + level->getMaterial(x-5, y, z)->isLiquid() && + level->getMaterial(x, y, z+5)->isLiquid() && + level->getMaterial(x, y, z-5)->isLiquid() + ) + { + inEnoughWater = true; + } + } + + return inEnoughWater && !level->isSolidBlockingTile(x, y + 1, z); + } + else + { + if (!level->isTopSolidBlocking(x, y - 1, z)) return false; + int tt = level->getTile(x, y - 1, z); + return tt != Tile::unbreakable_Id && !level->isSolidBlockingTile(x, y, z) && !level->getMaterial(x, y, z)->isLiquid() && !level->isSolidBlockingTile(x, y + 1, z); + } +} + + +void MobSpawner::finalizeMobSettings(shared_ptr mob, Level *level, float xx, float yy, float zz) +{ + if (dynamic_pointer_cast( mob ) != NULL && level->random->nextInt(100) == 0) + { + shared_ptr skeleton = shared_ptr( new Skeleton(level) ); + skeleton->moveTo(xx, yy, zz, mob->yRot, 0); + level->addEntity(skeleton); + skeleton->ride(mob); + } + else if (dynamic_pointer_cast( mob ) != NULL) + { + (dynamic_pointer_cast( mob ))->setColor(Sheep::getSheepColor(level->random)); + } + else if (dynamic_pointer_cast( mob ) != NULL) + { + if (level->random->nextInt(7) == 0) + { + for (int kitten = 0; kitten < 2; kitten++) + { + shared_ptr ozelot = shared_ptr(new Ozelot(level)); + ozelot->moveTo(xx, yy, zz, mob->yRot, 0); + ozelot->setAge(-20 * 60 * 20); + level->addEntity(ozelot); + } + } + } +} + + +// 4J Stu TODO This was an array of Class type. I haven't made a base Class type yet, but don't need to +// as this can be an array of Mob type? +eINSTANCEOF MobSpawner::bedEnemies[bedEnemyCount] = { + eTYPE_SPIDER, eTYPE_ZOMBIE, eTYPE_SKELETON +}; + + +bool MobSpawner::attackSleepingPlayers(Level *level, vector > *players) +{ + + bool somebodyWokeUp = false; + + PathFinder finder = PathFinder(level, true, false, false, true); + + AUTO_VAR(itEnd, players->end()); + for (AUTO_VAR(it, players->begin()); it != itEnd; it++) + { + shared_ptr player = (*it); + + bool nextPlayer = false; + + for (int attemptCount = 0; attemptCount < 20 && !nextPlayer; attemptCount++)\ + { + + + // limit position within the range of the player + int x = Mth::floor(player->x) + level->random->nextInt(32) - level->random->nextInt(32); + int z = Mth::floor(player->z) + level->random->nextInt(32) - level->random->nextInt(32); + int yStart = Mth::floor(player->y) + level->random->nextInt(16) - level->random->nextInt(16); + if (yStart < 1) + { + yStart = 1; + } + else if (yStart > Level::maxBuildHeight) + { + yStart = Level::maxBuildHeight; + } + + { + int type = level->random->nextInt(bedEnemyCount); + int y = yStart; + + while (y > 2 && !level->isTopSolidBlocking(x, y - 1, z)) + { + y--; + } + + while (!isSpawnPositionOk( (MobCategory *) MobCategory::monster, level, x, y, z) && y < (yStart + 16) && y < Level::maxBuildHeight) + { + y++; + } + if (y >= (yStart + 16) || y >= Level::maxBuildHeight) + { + y = yStart; + continue; + } + else + { + float xx = x + 0.5f; + float yy = (float) y; + float zz = z + 0.5f; + + shared_ptr mob; +// 4J - removed try/catch +// try +// { + //mob = classes[type].getConstructor(Level.class).newInstance(level); + // 4J - there was a classes array here which duplicated the bedEnemies array but have removed it + mob = dynamic_pointer_cast(EntityIO::newByEnumType(bedEnemies[type], level )); +// } +// catch (exception e) +// { +// // TODO 4J Stu - We can't print a stack trace, and newInstance doesn't currently throw an exception anyway +// //e.printStackTrace(); +// return somebodyWokeUp; +// } + + // System.out.println("Placing night mob"); + mob->moveTo(xx, yy, zz, level->random->nextFloat() * 360, 0); + // check if the mob can spawn at this location + if (!mob->canSpawn()) + { + continue; + } + Pos *bedPos = BedTile::findStandUpPosition(level, Mth::floor(player->x), Mth::floor(player->y), Mth::floor(player->z), 1); + if (bedPos == NULL) + { + // an unlikely case where the bed is + // completely blocked + bedPos = new Pos(x, y + 1, z); + } + + // 4J Stu - TU-1 hotfix + // Fix for #13152 - If the player sleeps in a bed next to a wall in an enclosed, well lit area they will be awoken by a monster + // The pathfinder should attempt to get close to the position that we will move the mob to, + // instead of the player who could be next to a wall. Otherwise the paths gets to the other + // side of the the wall, then moves the mob inside the building + //Path *findPath = finder.findPath(mob.get(), player.get(), 32.0f); + Path *findPath = finder.findPath(mob.get(), bedPos->x, bedPos->y, bedPos->z, 32.0f); + if (findPath != NULL && findPath->getSize() > 1) + { + Node *last = findPath->last(); + + if (abs(last->x - bedPos->x) < 1.5 && abs(last->z - bedPos->z) < 1.5 && abs(last->y - bedPos->y) < 1.5) + { + // System.out.println("Found path!"); + + mob->moveTo(bedPos->x + 0.5f, bedPos->y, bedPos->z + 0.5f, 0, 0); + // the mob would maybe not be able to + // spawn here, but we ignore that now (we assume + // it walked here) + { + level->addEntity(mob); + finalizeMobSettings(mob, level, bedPos->x + 0.5f, (float) bedPos->y, bedPos->z + 0.5f); + mob->finalizeMobSpawn(); + player->stopSleepInBed(true, false, false); + // play a sound effect to scare the player + mob->playAmbientSound(); + somebodyWokeUp = true; + nextPlayer = true; + } + } + delete findPath; + } + delete bedPos; + } + } + } + } + + + return somebodyWokeUp; +} + +void MobSpawner::postProcessSpawnMobs(Level *level, Biome *biome, int xo, int zo, int cellWidth, int cellHeight, Random *random) +{ + // 4J - not for our version. Creates a few too many mobs. +#if 0 + vector *mobs = biome->getMobs(MobCategory::creature); + if (mobs->empty()) + { + return; + } + + while (random->nextFloat() < biome->getCreatureProbability()) + { + Biome::MobSpawnerData *type = (Biome::MobSpawnerData *) WeighedRandom::getRandomItem(level->random, ((vector *)mobs)); + int count = type->minCount + random->nextInt(1 + type->maxCount - type->minCount); + + int x = xo + random->nextInt(cellWidth); + int z = zo + random->nextInt(cellHeight); + int startX = x, startZ = z; + + for (int c = 0; c < count; c++) + { + bool success = false; + for (int attempts = 0; !success && attempts < 4; attempts++) + { + // these mobs always spawn at the topmost position + int y = level->getTopSolidBlock(x, z); + if (isSpawnPositionOk(MobCategory::creature, level, x, y, z)) + { + + float xx = x + 0.5f; + float yy = (float)y; + float zz = z + 0.5f; + + shared_ptr mob; + //try { + mob = dynamic_pointer_cast( EntityIO::newByEnumType(type->mobClass, level ) ); + //} catch (Exception e) { + // e.printStackTrace(); + // continue; + //} + + // System.out.println("Placing night mob"); + mob->moveTo(xx, yy, zz, random->nextFloat() * 360, 0); + + mob->setDespawnProtected(); + + level->addEntity(mob); + finalizeMobSettings(mob, level, xx, yy, zz); + success = true; + } + + x += random->nextInt(5) - random->nextInt(5); + z += random->nextInt(5) - random->nextInt(5); + while (x < xo || x >= (xo + cellWidth) || z < zo || z >= (zo + cellWidth)) + { + x = startX + random->nextInt(5) - random->nextInt(5); + z = startZ + random->nextInt(5) - random->nextInt(5); + } + } + } + } +#endif +} diff --git a/Minecraft.World/MobSpawner.h b/Minecraft.World/MobSpawner.h new file mode 100644 index 00000000..b5022a77 --- /dev/null +++ b/Minecraft.World/MobSpawner.h @@ -0,0 +1,46 @@ +#pragma once + +#include "Mob.h" + +#ifdef __PSVITA__ +#include "..\Minecraft.Client\PSVita\PSVitaExtras\CustomMap.h" +#endif + +class Player; +class Level; + +class MobSpawner +{ +private: + static const int MIN_SPAWN_DISTANCE; + +protected: + static TilePos getRandomPosWithin(Level *level, int cx, int cz); + +private: +#ifdef __PSVITA__ + // AP - See CustomMap.h for an explanation of this + static CustomMap chunksToPoll; +#else + static unordered_map chunksToPoll; +#endif + +public: + static const int tick(ServerLevel *level, bool spawnEnemies, bool spawnFriendlies); + static bool isSpawnPositionOk(MobCategory *category, Level *level, int x, int y, int z); + +private: + static void finalizeMobSettings(shared_ptr mob, Level *level, float xx, float yy, float zz); + +protected: + // 4J Stu TODO This was an array of Class type. I haven't made a base Class type yet, but don't need to + // as this can be an array of Mob type? + static const int bedEnemyCount = 3; + static eINSTANCEOF bedEnemies[bedEnemyCount]; + + +public: + static bool attackSleepingPlayers(Level *level, vector > *players); + + static void postProcessSpawnMobs(Level *level, Biome *biome, int xo, int zo, int cellWidth, int cellHeight, Random *random); +}; \ No newline at end of file diff --git a/Minecraft.World/MobSpawnerTile.cpp b/Minecraft.World/MobSpawnerTile.cpp new file mode 100644 index 00000000..d926fc24 --- /dev/null +++ b/Minecraft.World/MobSpawnerTile.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "MobSpawnerTile.h" + +MobSpawnerTile::MobSpawnerTile(int id) : EntityTile(id, Material::stone, isSolidRender() ) +{ +} + +shared_ptr MobSpawnerTile::newTileEntity(Level *level) +{ + return shared_ptr( new MobSpawnerTileEntity() ); +} + +int MobSpawnerTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return 0; +} + +int MobSpawnerTile::getResourceCount(Random *random) +{ + return 0; +} + +bool MobSpawnerTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool MobSpawnerTile::blocksLight() +{ + return false; +} + +void MobSpawnerTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel) +{ + Tile::spawnResources(level, x, y, z, data, odds, playerBonusLevel); + + // also spawn experience if the block is broken + { + int magicCount = 15 + level->random->nextInt(15) + level->random->nextInt(15); + popExperience(level, x, y, z, magicCount); + } +} + +int MobSpawnerTile::cloneTileId(Level *level, int x, int y, int z) +{ + return 0; +} \ No newline at end of file diff --git a/Minecraft.World/MobSpawnerTile.h b/Minecraft.World/MobSpawnerTile.h new file mode 100644 index 00000000..edf3c5e7 --- /dev/null +++ b/Minecraft.World/MobSpawnerTile.h @@ -0,0 +1,19 @@ +#pragma once +#include "EntityTile.h" + +class Random; + +class MobSpawnerTile : public EntityTile +{ + friend class Tile; +protected: + MobSpawnerTile(int id); +public: + virtual shared_ptr newTileEntity(Level *level); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int getResourceCount(Random *random); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool blocksLight(); + virtual void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel); + virtual int cloneTileId(Level *level, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/MobSpawnerTileEntity.cpp b/Minecraft.World/MobSpawnerTileEntity.cpp new file mode 100644 index 00000000..ce7ae1dc --- /dev/null +++ b/Minecraft.World/MobSpawnerTileEntity.cpp @@ -0,0 +1,233 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "TileEntity.h" +#include "LevelEvent.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.network.packet.h" +#include "SharedConstants.h" +#include "MobSpawnerTileEntity.h" + + + +const int MobSpawnerTileEntity::MAX_DIST = 16; + +MobSpawnerTileEntity::MobSpawnerTileEntity() : TileEntity() +{ + spin = 0; + oSpin = 0; + + // entityId = "Skeleton"; + entityId = L"Pig"; + m_bEntityIdUpdated = false; + + spawnData = NULL; + + spawnDelay = 20; + + minSpawnDelay = SharedConstants::TICKS_PER_SECOND * 10; + maxSpawnDelay = SharedConstants::TICKS_PER_SECOND * 40; + spawnCount = 4; + displayEntity = nullptr; +} + +wstring MobSpawnerTileEntity::getEntityId() +{ + return entityId; +} + +void MobSpawnerTileEntity::setEntityId(const wstring& entityId) +{ + this->entityId = entityId; +} + +bool MobSpawnerTileEntity::isNearPlayer() +{ + return level->getNearestPlayer(x + 0.5, y + 0.5, z + 0.5, MAX_DIST) != NULL; +} + +void MobSpawnerTileEntity::tick() +{ + if (!isNearPlayer()) + { + return; + } + + if (level->isClientSide) + { + double xP = x + level->random->nextFloat(); + double yP = y + level->random->nextFloat(); + double zP = z + level->random->nextFloat(); + level->addParticle(eParticleType_smoke, xP, yP, zP, 0, 0, 0); + level->addParticle(eParticleType_flame, xP, yP, zP, 0, 0, 0); + + oSpin = spin; + spin += 1000 / 220.0f; + while (spin > 360) + { + spin -= 360; + } + while(oSpin > 360) + { + oSpin -= 360; + } + } + else + { + + if (spawnDelay == -1) delay(); + + if (spawnDelay > 0) + { + spawnDelay--; + return; + } + + for (int c = 0; c < spawnCount; c++) + { + shared_ptr entity = dynamic_pointer_cast (EntityIO::newEntity(entityId, level)); + if (entity == NULL) return; + + vector > *vecNearby = level->getEntitiesOfClass(typeid(*entity), AABB::newTemp(x, y, z, x + 1, y + 1, z + 1)->grow(8, 4, 8)); + int nearBy = (int)vecNearby->size(); //4J - IB, TODO, Mob contains no getClass + delete vecNearby; + + if (nearBy >= 6) + { + delay(); + return; + } + + // 4J added - our mobspawner tiles should only be spawning monsters. Also respect the global limits we have for those so we don't go + // creating silly numbers of them. Have set this limit slightly higher than the main spawner has so that this tile entity is more likely to + // actually make something (60 rather than 50) + if(level->countInstanceOf( eTYPE_MONSTER, false) >= 60 ) + { + return; + } + + if (entity != NULL) + { + double xp = x + (level->random->nextDouble() - level->random->nextDouble()) * 4; + double yp = y + level->random->nextInt(3) - 1; + double zp = z + (level->random->nextDouble() - level->random->nextDouble()) * 4; + shared_ptr mob = dynamic_pointer_cast( entity ); + + entity->moveTo(xp, yp, zp, level->random->nextFloat() * 360, 0); + + if (mob == NULL || mob->canSpawn()) + { + fillExtraData(entity); + + level->addEntity(entity); + + level->levelEvent(LevelEvent::PARTICLES_MOBTILE_SPAWN, x, y, z, 0); + + if (mob != NULL) mob->spawnAnim(); + delay(); + } + } + } + } + + TileEntity::tick(); +} + +void MobSpawnerTileEntity::fillExtraData(shared_ptr entity) +{ + if (spawnData != NULL) + { + CompoundTag *data = new CompoundTag(); + entity->save(data); + + vector *allTags = spawnData->getAllTags(); + for(AUTO_VAR(it, allTags->begin()); it != allTags->end(); ++it) + { + Tag *tag = *it; + data->put((wchar_t *)tag->getName().c_str(), tag->copy()); + } + if(allTags != NULL) delete allTags; + + entity->load(data); + } +} + +void MobSpawnerTileEntity::delay() +{ + spawnDelay = minSpawnDelay + level->random->nextInt(maxSpawnDelay - minSpawnDelay); +} + +void MobSpawnerTileEntity::load(CompoundTag *tag) +{ + TileEntity::load(tag); + entityId = tag->getString(L"EntityId"); + m_bEntityIdUpdated = true; + + spawnDelay = tag->getShort(L"Delay"); + + if (tag->contains(L"SpawnData")) + { + spawnData = tag->getCompound(L"SpawnData"); + } + else + { + spawnData = NULL; + } + + if (tag->contains(L"MinSpawnDelay")) + { + minSpawnDelay = tag->getShort(L"MinSpawnDelay"); + maxSpawnDelay = tag->getShort(L"MaxSpawnDelay"); + spawnCount = tag->getShort(L"SpawnCount"); + } +} + +void MobSpawnerTileEntity::save(CompoundTag *tag) +{ + TileEntity::save(tag); + tag->putString(L"EntityId", entityId ); + tag->putShort(L"Delay", (short) spawnDelay); + tag->putShort(L"MinSpawnDelay", (short) minSpawnDelay); + tag->putShort(L"MaxSpawnDelay", (short) maxSpawnDelay); + tag->putShort(L"SpawnCount", (short) spawnCount); + + if (spawnData != NULL) + { + tag->putCompound(L"SpawnData", spawnData); + } +} + +shared_ptr MobSpawnerTileEntity::getDisplayEntity() +{ + if (displayEntity == NULL || m_bEntityIdUpdated) + { + shared_ptr e = EntityIO::newEntity(getEntityId(), NULL); + fillExtraData(e); + displayEntity = e; + m_bEntityIdUpdated = false; + } + + return displayEntity; +} + +shared_ptr MobSpawnerTileEntity::getUpdatePacket() +{ + CompoundTag *tag = new CompoundTag(); + save(tag); + return shared_ptr( new TileEntityDataPacket(x, y, z, TileEntityDataPacket::TYPE_MOB_SPAWNER, tag) ); +} + +// 4J Added +shared_ptr MobSpawnerTileEntity::clone() +{ + shared_ptr result = shared_ptr( new MobSpawnerTileEntity() ); + TileEntity::clone(result); + + result->entityId = entityId; + result->spawnDelay = spawnDelay; + return result; +} \ No newline at end of file diff --git a/Minecraft.World/MobSpawnerTileEntity.h b/Minecraft.World/MobSpawnerTileEntity.h new file mode 100644 index 00000000..9cfb9f2f --- /dev/null +++ b/Minecraft.World/MobSpawnerTileEntity.h @@ -0,0 +1,59 @@ +#pragma once +using namespace std; + +#include "TileEntity.h" + +class Packet; +class Entity; + +class MobSpawnerTileEntity : public TileEntity +{ +public: + eINSTANCEOF GetType() { return eTYPE_MOBSPAWNERTILEENTITY; } + static TileEntity *create() { return new MobSpawnerTileEntity(); } + +using TileEntity::setChanged; + +private: + static const int MAX_DIST; + +public: + int spawnDelay; + +private: + wstring entityId; + CompoundTag *spawnData; + + bool m_bEntityIdUpdated; // 4J Added + +public: + double spin, oSpin; + +private: + int minSpawnDelay; + int maxSpawnDelay; + int spawnCount; + shared_ptr displayEntity; + +public: + MobSpawnerTileEntity(); + + wstring getEntityId(); + void setEntityId(const wstring& entityId); + bool isNearPlayer(); + virtual void tick(); + void fillExtraData(shared_ptr entity); + +private: + void delay(); + +public: + virtual void load(CompoundTag *tag); + virtual void save(CompoundTag *tag); + + shared_ptr getDisplayEntity(); + virtual shared_ptr getUpdatePacket(); + + // 4J Added + virtual shared_ptr clone(); +}; diff --git a/Minecraft.World/MobType.h b/Minecraft.World/MobType.h new file mode 100644 index 00000000..40ba118b --- /dev/null +++ b/Minecraft.World/MobType.h @@ -0,0 +1,9 @@ +#pragma once + +enum MobType +{ + UNDEFINED, + + UNDEAD, + ARTHROPOD +}; \ No newline at end of file diff --git a/Minecraft.World/MockedLevelStorage.cpp b/Minecraft.World/MockedLevelStorage.cpp new file mode 100644 index 00000000..9a7a63c0 --- /dev/null +++ b/Minecraft.World/MockedLevelStorage.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.chunk.storage.h" +#include "net.minecraft.world.level.dimension.h" +#include "MockedLevelStorage.h" + +#include "ConsoleSaveFileIO.h" + +LevelData *MockedLevelStorage::prepareLevel() +{ + return NULL; +} + +void MockedLevelStorage::checkSession() +{ +} + +ChunkStorage *MockedLevelStorage::createChunkStorage(Dimension *dimension) +{ + return NULL; +} + +void MockedLevelStorage::saveLevelData(LevelData *levelData, vector > *players) +{ +} + +void MockedLevelStorage::saveLevelData(LevelData *levelData) +{ +} + +PlayerIO *MockedLevelStorage::getPlayerIO() +{ + return NULL; +} + +void MockedLevelStorage::closeAll() +{ +} + +ConsoleSavePath MockedLevelStorage::getDataFile(const wstring& id) +{ + return ConsoleSavePath(wstring(L"")); +} + +wstring MockedLevelStorage::getLevelId() +{ + return L"none"; +} \ No newline at end of file diff --git a/Minecraft.World/MockedLevelStorage.h b/Minecraft.World/MockedLevelStorage.h new file mode 100644 index 00000000..6a9eed70 --- /dev/null +++ b/Minecraft.World/MockedLevelStorage.h @@ -0,0 +1,22 @@ +#pragma once +using namespace std; + +#include "LevelStorage.h" + +#include "ConsoleSavePath.h" + +class MockedLevelStorage : public LevelStorage +{ +public: + virtual LevelData *prepareLevel(); + virtual void checkSession(); + virtual ChunkStorage *createChunkStorage(Dimension *dimension); + virtual void saveLevelData(LevelData *levelData, vector > *players); + virtual void saveLevelData(LevelData *levelData); + virtual PlayerIO *getPlayerIO(); + virtual void closeAll(); + virtual ConsoleSavePath getDataFile(const wstring& id); + virtual wstring getLevelId(); +public: + virtual ConsoleSaveFile *getSaveFile() { return NULL; } +}; \ No newline at end of file diff --git a/Minecraft.World/Monster.cpp b/Minecraft.World/Monster.cpp new file mode 100644 index 00000000..627ce8d1 --- /dev/null +++ b/Minecraft.World/Monster.cpp @@ -0,0 +1,159 @@ +#include "stdafx.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.effect.h" +#include "net.minecraft.world.item.enchantment.h" +#include "Monster.h" + +#include "..\Minecraft.Client\Minecraft.h" + + + +void Monster::_init() +{ + attackDamage = 2; +} + +Monster::Monster(Level *level) : PathfinderMob( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + + // 4J Stu - Only the most derived classes should call this + //this->defineSynchedData(); + + _init(); + + xpReward = Enemy::XP_REWARD_MEDIUM; +} + +void Monster::aiStep() +{ + float br = getBrightness(1); + if (br > 0.5f) + { + noActionTime += 2; + } + + PathfinderMob::aiStep(); +} + +void Monster::tick() +{ + PathfinderMob::tick(); + if (!level->isClientSide && (level->difficulty == Difficulty::PEACEFUL || Minecraft::GetInstance()->isTutorial() ) ) remove(); +} + +shared_ptr Monster::findAttackTarget() +{ +#ifndef _FINAL_BUILD + if(app.GetMobsDontAttackEnabled()) + { + return shared_ptr(); + } +#endif + + shared_ptr player = level->getNearestAttackablePlayer(shared_from_this(), 16); + if (player != NULL && canSee(player) ) return player; + return shared_ptr(); +} + +bool Monster::hurt(DamageSource *source, int dmg) +{ + if (PathfinderMob::hurt(source, dmg)) + { + shared_ptr sourceEntity = source->getEntity(); + if (rider.lock() == sourceEntity || riding == sourceEntity) return true; + + if (sourceEntity != shared_from_this()) + { + this->attackTarget = sourceEntity; + } + return true; + } + return false; +} + +/** +* Performs hurt action, returns if successful +* +* @param target +* @return +*/ +bool Monster::doHurtTarget(shared_ptr target) +{ + int dmg = attackDamage; + if (hasEffect(MobEffect::damageBoost)) + { + dmg += (3 << getEffect(MobEffect::damageBoost)->getAmplifier()); + } + if (hasEffect(MobEffect::weakness)) + { + dmg -= (2 << getEffect(MobEffect::weakness)->getAmplifier()); + } + + DamageSource *damageSource = DamageSource::mobAttack(dynamic_pointer_cast( shared_from_this() ) ); + bool didHurt = target->hurt(damageSource, dmg); + delete damageSource; + + if (didHurt) + { + int fireAspect = EnchantmentHelper::getFireAspect(dynamic_pointer_cast(shared_from_this())); + if (fireAspect > 0) + { + target->setOnFire(fireAspect * 4); + } + + shared_ptr mob = dynamic_pointer_cast(target); + if (mob != NULL) + { + ThornsEnchantment::doThornsAfterAttack(shared_from_this(), mob, random); + } + } + + return didHurt; +} + +void Monster::checkHurtTarget(shared_ptr target, float distance) +{ + if (attackTime <= 0 && distance < 2.0f && target->bb->y1 > bb->y0 && target->bb->y0 < bb->y1) + { + attackTime = 20; + doHurtTarget(target); + } +} + +float Monster::getWalkTargetValue(int x, int y, int z) +{ + return 0.5f - level->getBrightness(x, y, z); +} + +bool Monster::isDarkEnoughToSpawn() +{ + int xt = Mth::floor(x); + int yt = Mth::floor(bb->y0); + int zt = Mth::floor(z); + if (level->getBrightness(LightLayer::Sky, xt, yt, zt) > random->nextInt(32)) return false; + + int br = level->getRawBrightness(xt, yt, zt); + + if (level->isThundering()) + { + int tmp = level->skyDarken; + level->skyDarken = 10; + br = level->getRawBrightness(xt, yt, zt); + level->skyDarken = tmp; + } + + return br <= random->nextInt(8); +} + +bool Monster::canSpawn() +{ + // 4J Stu + // Fix for #8265 - AI: Monsters will flash briefly on the screen around Monster Spawners when the game settings are set to Peaceful. + return isDarkEnoughToSpawn() && PathfinderMob::canSpawn() && level->difficulty > Difficulty::PEACEFUL; +} \ No newline at end of file diff --git a/Minecraft.World/Monster.h b/Minecraft.World/Monster.h new file mode 100644 index 00000000..2bad48da --- /dev/null +++ b/Minecraft.World/Monster.h @@ -0,0 +1,48 @@ +#pragma once +using namespace std; + +#include "PathfinderMob.h" +#include "Enemy.h" + +class Level; +class CompoundTag; +class DamageSource; + +class Monster : public PathfinderMob, public Enemy +{ +public: + eINSTANCEOF GetType() { return eTYPE_MONSTER; } + static Entity *create(Level *level) { return NULL; } + +protected: + int attackDamage; + +private: + void _init(); + +public: + Monster(Level *level); + + virtual void aiStep(); + virtual void tick(); + +protected: + virtual shared_ptr findAttackTarget(); + +public: + virtual bool hurt(DamageSource *source, int dmg); + virtual bool doHurtTarget(shared_ptr target); + +protected: + virtual void checkHurtTarget(shared_ptr target, float distance); + +public: + virtual float getWalkTargetValue(int x, int y, int z); + +protected: + virtual bool isDarkEnoughToSpawn(); + +public: + virtual bool canSpawn(); +}; + diff --git a/Minecraft.World/MonsterPlacerItem.cpp b/Minecraft.World/MonsterPlacerItem.cpp new file mode 100644 index 00000000..a2970033 --- /dev/null +++ b/Minecraft.World/MonsterPlacerItem.cpp @@ -0,0 +1,291 @@ +#include "stdafx.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "net.minecraft.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.npc.h" +#include "net.minecraft.world.h" +#include "MonsterPlacerItem.h" +#include "Difficulty.h" + + +MonsterPlacerItem::MonsterPlacerItem(int id) : Item(id) +{ + setMaxStackSize(16); // 4J-PB brought forward. It is 64 on PC, but we'll never be able to place that many + setStackedByData(true); + overlay = NULL; +} + +wstring MonsterPlacerItem::getHoverName(shared_ptr itemInstance) +{ + wstring elementName = getDescription(); + + int nameId = EntityIO::getNameId(itemInstance->getAuxValue()); + if (nameId >= 0) + { + elementName = replaceAll(elementName,L"{*CREATURE*}",app.GetString(nameId)); + //elementName += " " + I18n.get("entity." + encodeId + ".name"); + } + else + { + elementName = replaceAll(elementName,L"{*CREATURE*}",L""); + } + + return elementName; +} + +int MonsterPlacerItem::getColor(shared_ptr item, int spriteLayer) +{ + AUTO_VAR(it, EntityIO::idsSpawnableInCreative.find(item->getAuxValue())); + if (it != EntityIO::idsSpawnableInCreative.end()) + { + EntityIO::SpawnableMobInfo *spawnableMobInfo = it->second; + if (spriteLayer == 0) { + return Minecraft::GetInstance()->getColourTable()->getColor( spawnableMobInfo->eggColor1 ); + } + return Minecraft::GetInstance()->getColourTable()->getColor( spawnableMobInfo->eggColor2 ); + } + return 0xffffff; +} + +bool MonsterPlacerItem::hasMultipleSpriteLayers() +{ + return true; +} + +Icon *MonsterPlacerItem::getLayerIcon(int auxValue, int spriteLayer) +{ + if (spriteLayer > 0) + { + return overlay; + } + return Item::getLayerIcon(auxValue, spriteLayer); +} + +// 4J-PB - added for dispenser +shared_ptr MonsterPlacerItem::canSpawn(int iAuxVal, Level *level, int *piResult) +{ + shared_ptr newEntity = EntityIO::newById(iAuxVal, level); + if (newEntity != NULL) + { + bool canSpawn = false; + + switch(newEntity->GetType()) + { + case eTYPE_CHICKEN: + if(level->canCreateMore( eTYPE_CHICKEN, Level::eSpawnType_Egg) ) + { + canSpawn = true; + } + else + { + *piResult=eSpawnResult_FailTooManyChickens; + } + break; + case eTYPE_WOLF: + if(level->canCreateMore( eTYPE_WOLF, Level::eSpawnType_Egg) ) + { + canSpawn = true; + } + else + { + *piResult=eSpawnResult_FailTooManyWolves; + } + break; + case eTYPE_VILLAGER: + if(level->canCreateMore( eTYPE_VILLAGER, Level::eSpawnType_Egg) ) + { + canSpawn = true; + } + else + { + *piResult=eSpawnResult_FailTooManyVillagers; + } + break; + case eTYPE_MUSHROOMCOW: + if(level->canCreateMore(eTYPE_MUSHROOMCOW, Level::eSpawnType_Egg) ) + { + canSpawn = true; + } + else + { + *piResult=eSpawnResult_FailTooManyMooshrooms; + } + break; + case eTYPE_SQUID: + if(level->canCreateMore( eTYPE_SQUID, Level::eSpawnType_Egg) ) + { + canSpawn = true; + } + else + { + *piResult=eSpawnResult_FailTooManySquid; + } + break; + default: + if( (newEntity->GetType() & eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) == eTYPE_ANIMALS_SPAWN_LIMIT_CHECK) + { + if( level->canCreateMore( newEntity->GetType(), Level::eSpawnType_Egg ) ) + { + canSpawn = true; + } + else + { + // different message for each animal + + *piResult=eSpawnResult_FailTooManyPigsCowsSheepCats; + } + } + else if( (newEntity->GetType() & eTYPE_MONSTER) == eTYPE_MONSTER) + { + // 4J-PB - check if the player is trying to spawn an enemy in peaceful mode + if(level->difficulty==Difficulty::PEACEFUL) + { + *piResult=eSpawnResult_FailCantSpawnInPeaceful; + } + else if(level->canCreateMore( newEntity->GetType(), Level::eSpawnType_Egg) ) + { + canSpawn = true; + } + else + { + *piResult=eSpawnResult_FailTooManyMonsters; + } + } + break; + } + + if(canSpawn) + { + return newEntity; + } + } + + return nullptr; +} + +bool MonsterPlacerItem::useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + if (level->isClientSide) + { + return true; + } + + int tile = level->getTile(x, y, z); + +#ifndef _CONTENT_PACKAGE + if(app.DebugSettingsOn() && tile == Tile::mobSpawner_Id) + { + // 4J Stu - Force adding this as a tile update + level->setTile(x,y,z,0); + level->setTile(x,y,z,Tile::mobSpawner_Id); + shared_ptr mste = dynamic_pointer_cast( level->getTileEntity(x,y,z) ); + if(mste != NULL) + { + mste->setEntityId( EntityIO::getEncodeId(itemInstance->getAuxValue()) ); + return true; + } + } +#endif + + x += Facing::STEP_X[face]; + y += Facing::STEP_Y[face]; + z += Facing::STEP_Z[face]; + + double yOff = 0; + // 4J-PB - missing parentheses added + if (face == Facing::UP && (tile == Tile::fence_Id || tile == Tile::netherFence_Id)) + { + // special case + yOff = .5; + } + + int iResult=0; + bool spawned = spawnMobAt(level, itemInstance->getAuxValue(), x + .5, y + yOff, z + .5, &iResult) != NULL; + + if(bTestUseOnOnly) + { + return spawned; + } + + if (spawned) + { + if (!player->abilities.instabuild) + { + itemInstance->count--; + } + } + else + { + // some negative sound effect? + //level->levelEvent(LevelEvent::SOUND_CLICK_FAIL, x, y, z, 0); + switch(iResult) + { + case eSpawnResult_FailTooManyPigsCowsSheepCats: + player->displayClientMessage(IDS_MAX_PIGS_SHEEP_COWS_CATS_SPAWNED ); + break; + case eSpawnResult_FailTooManyChickens: + player->displayClientMessage(IDS_MAX_CHICKENS_SPAWNED ); + break; + case eSpawnResult_FailTooManySquid: + player->displayClientMessage(IDS_MAX_SQUID_SPAWNED ); + break; + case eSpawnResult_FailTooManyWolves: + player->displayClientMessage(IDS_MAX_WOLVES_SPAWNED ); + break; + case eSpawnResult_FailTooManyMooshrooms: + player->displayClientMessage(IDS_MAX_MOOSHROOMS_SPAWNED ); + break; + case eSpawnResult_FailTooManyMonsters: + player->displayClientMessage(IDS_MAX_ENEMIES_SPAWNED ); + break; + case eSpawnResult_FailTooManyVillagers: + player->displayClientMessage(IDS_MAX_VILLAGERS_SPAWNED ); + break; + case eSpawnResult_FailCantSpawnInPeaceful: + player->displayClientMessage(IDS_CANT_SPAWN_IN_PEACEFUL ); + break; + + } + } + + return true; +} + +shared_ptr MonsterPlacerItem::spawnMobAt(Level *level, int mobId, double x, double y, double z, int *piResult) +{ + if (EntityIO::idsSpawnableInCreative.find(mobId) == EntityIO::idsSpawnableInCreative.end()) + { + return nullptr; + } + + shared_ptr newEntity = nullptr; + + for (int i = 0; i < SPAWN_COUNT; i++) + { + newEntity = canSpawn(mobId, level, piResult); + + shared_ptr mob = dynamic_pointer_cast(newEntity); + if (mob) + { + newEntity->moveTo(x, y, z, Mth::wrapDegrees(level->random->nextFloat() * 360), 0); + newEntity->setDespawnProtected(); // 4J added, default to being protected against despawning (has to be done after initial position is set) + mob->yHeadRot = mob->yRot; + mob->yBodyRot = mob->yRot; + + mob->finalizeMobSpawn(); + level->addEntity(newEntity); + mob->playAmbientSound(); + } + } + + return newEntity; +} + +void MonsterPlacerItem::registerIcons(IconRegister *iconRegister) +{ + Item::registerIcons(iconRegister); + overlay = iconRegister->registerIcon(L"monsterPlacer_overlay"); +} diff --git a/Minecraft.World/MonsterPlacerItem.h b/Minecraft.World/MonsterPlacerItem.h new file mode 100644 index 00000000..f1cead6d --- /dev/null +++ b/Minecraft.World/MonsterPlacerItem.h @@ -0,0 +1,42 @@ +#pragma once + +#include "Item.h" + +class MonsterPlacerItem : public Item +{ +private: + static const int SPAWN_COUNT = 1; + + Icon *overlay; + +public: + + enum _eSpawnResult + { + eSpawnResult_OK=0, + eSpawnResult_FailTooManyPigsCowsSheepCats, + eSpawnResult_FailTooManyChickens, + eSpawnResult_FailTooManySquid, + eSpawnResult_FailTooManyWolves, + eSpawnResult_FailTooManyMooshrooms, + eSpawnResult_FailTooManyAnimals, + eSpawnResult_FailTooManyMonsters, + eSpawnResult_FailTooManyVillagers, + eSpawnResult_FailCantSpawnInPeaceful, + }; + + MonsterPlacerItem(int id); + + virtual wstring getHoverName(shared_ptr itemInstance); + virtual int getColor(shared_ptr item, int spriteLayer); + virtual bool hasMultipleSpriteLayers(); + virtual Icon *getLayerIcon(int auxValue, int spriteLayer); + virtual bool useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + static shared_ptr spawnMobAt(Level *level, int mobId, double x, double y, double z, int *piResult); // 4J Added piResult param + + // 4J-PB added for dispenser + static shared_ptr canSpawn(int iAuxVal, Level *level, int *piResult); + + //@Override + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/MonsterRoomFeature.cpp b/Minecraft.World/MonsterRoomFeature.cpp new file mode 100644 index 00000000..cd44376a --- /dev/null +++ b/Minecraft.World/MonsterRoomFeature.cpp @@ -0,0 +1,140 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.item.h" +#include "MonsterRoomFeature.h" + +bool MonsterRoomFeature::place(Level *level, Random *random, int x, int y, int z) +{ + int hr = 3; + int xr = random->nextInt(2) + 2; + int zr = random->nextInt(2) + 2; + + int holeCount = 0; + for (int xx = x - xr - 1; xx <= x + xr + 1; xx++) + { + for (int yy = y - 1; yy <= y + hr + 1; yy++) + { + for (int zz = z - zr - 1; zz <= z + zr + 1; zz++) + { + Material *m = level->getMaterial(xx, yy, zz); + if (yy == y - 1 && !m->isSolid()) return false; + if (yy == y + hr + 1 && !m->isSolid()) return false; + + if (xx == x - xr - 1 || xx == x + xr + 1 || zz == z - zr - 1 || zz == z + zr + 1) + { + if (yy == y && level->isEmptyTile(xx, yy, zz) && level->isEmptyTile(xx, yy + 1, zz)) + { + holeCount++; + } + } + + } + } + } + + if (holeCount < 1 || holeCount > 5) return false; + + for (int xx = x - xr - 1; xx <= x + xr + 1; xx++) + { + for (int yy = y + hr; yy >= y - 1; yy--) + { + for (int zz = z - zr - 1; zz <= z + zr + 1; zz++) + { + + if (xx == x - xr - 1 || yy == y - 1 || zz == z - zr - 1 || xx == x + xr + 1 || yy == y + hr + 1 || zz == z + zr + 1) + { + if (yy >= 0 && !level->getMaterial(xx, yy - 1, zz)->isSolid()) + { + level->setTile(xx, yy, zz, 0); + } else if (level->getMaterial(xx, yy, zz)->isSolid()) + { + if (yy == y - 1 && random->nextInt(4) != 0) + { + level->setTile(xx, yy, zz, Tile::mossStone_Id); + } else { + level->setTile(xx, yy, zz, Tile::stoneBrick_Id); + } + } + } else + { + level->setTile(xx, yy, zz, 0); + } + } + } + } + + for (int cc = 0; cc < 2; cc++) + { + for (int i = 0; i < 3; i++) + { + int xc = x + random->nextInt(xr * 2 + 1) - xr; + int yc = y; + int zc = z + random->nextInt(zr * 2 + 1) - zr; + if (!level->isEmptyTile(xc, yc, zc)) continue; + + int count = 0; + if (level->getMaterial(xc - 1, yc, zc)->isSolid()) count++; + if (level->getMaterial(xc + 1, yc, zc)->isSolid()) count++; + if (level->getMaterial(xc, yc, zc - 1)->isSolid()) count++; + if (level->getMaterial(xc, yc, zc + 1)->isSolid()) count++; + + if (count != 1) continue; + + level->setTile(xc, yc, zc, Tile::chest_Id); + shared_ptr chest = dynamic_pointer_cast( level->getTileEntity(xc, yc, zc) ); + if (chest != NULL ) + { + for (int j = 0; j < 8; j++) + { + shared_ptr item = randomItem(random); + if (item != NULL) chest->setItem(random->nextInt(chest->getContainerSize()), item); + } + } + + break; + } + } + + + level->setTile(x, y, z, Tile::mobSpawner_Id); + shared_ptr entity = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + if( entity != NULL ) + { + entity->setEntityId(randomEntityId(random)); + } + + return true; + +} + +shared_ptr MonsterRoomFeature::randomItem(Random *random) +{ + int type = random->nextInt(12); + if (type == 0) return shared_ptr( new ItemInstance(Item::saddle) ); + if (type == 1) return shared_ptr( new ItemInstance(Item::ironIngot, random->nextInt(4) + 1) ); + if (type == 2) return shared_ptr( new ItemInstance(Item::bread) ); + if (type == 3) return shared_ptr( new ItemInstance(Item::wheat, random->nextInt(4) + 1) ); + if (type == 4) return shared_ptr( new ItemInstance(Item::sulphur, random->nextInt(4) + 1) ); + if (type == 5) return shared_ptr( new ItemInstance(Item::string, random->nextInt(4) + 1) ); + if (type == 6) return shared_ptr( new ItemInstance(Item::bucket_empty) ); + if (type == 7 && random->nextInt(100) == 0) return shared_ptr( new ItemInstance(Item::apple_gold) ); + if (type == 8 && random->nextInt(2) == 0) return shared_ptr( new ItemInstance(Item::redStone, random->nextInt(4) + 1) ); + if (type == 9 && random->nextInt(10) == 0) return shared_ptr( new ItemInstance( Item::items[Item::record_01->id + random->nextInt(2)]) ); + if (type == 10) return shared_ptr( new ItemInstance(Item::dye_powder, 1, DyePowderItem::BROWN) ); + if (type == 11) return Item::enchantedBook->createForRandomLoot(random); + + return shared_ptr(); +} + +wstring MonsterRoomFeature::randomEntityId(Random *random) +{ + int id = random->nextInt(4); + if (id == 0) return wstring(L"Skeleton"); + if (id == 1) return wstring(L"Zombie"); + if (id == 2) return wstring(L"Zombie"); + if (id == 3) return wstring(L"Spider"); + return wstring(L""); +} \ No newline at end of file diff --git a/Minecraft.World/MonsterRoomFeature.h b/Minecraft.World/MonsterRoomFeature.h new file mode 100644 index 00000000..88304ad6 --- /dev/null +++ b/Minecraft.World/MonsterRoomFeature.h @@ -0,0 +1,16 @@ +#pragma once +#include "Feature.h" +#include "Material.h" + +class MonsterRoomFeature : public Feature +{ +private: + //int tile; + +public: + virtual bool place(Level *level, Random *random, int x, int y, int z); + +private: + shared_ptr randomItem(Random *random); + wstring randomEntityId(Random *random); +}; diff --git a/Minecraft.World/MouseInventoryClickHandler.h b/Minecraft.World/MouseInventoryClickHandler.h new file mode 100644 index 00000000..ab451687 --- /dev/null +++ b/Minecraft.World/MouseInventoryClickHandler.h @@ -0,0 +1,84 @@ +#pragma once + +// 4J The body of this class was commented out in Java. Copying here for completeness + +class MouseInventoryClickHandler +{ +/* public static void handle(int buttonNum, boolean clickedOutside, int slotId, Player player) { + Inventory inventory = player.inventory; + Slot slot = player.getInventorySlot(slotId); + if (slot != null) { + ItemInstance clicked = slot.getItem(); + if (clicked == null && inventory.carried == null) { + } else if (clicked != null && inventory.carried == null) { + int c = buttonNum == 0 ? clicked.count : (clicked.count + 1) / 2; + inventory.carried = slot.container.removeItem(slot.slot, c); + if (clicked.count == 0) slot.set(null); + slot.onTake(); + } else if (clicked == null && inventory.carried != null && slot.mayPlace(inventory.carried)) { + int c = buttonNum == 0 ? inventory.carried.count : 1; + if (c > slot.getMaxStackSize()) c = slot.getMaxStackSize(); + slot.set(inventory.carried.remove(c)); + if (inventory.carried.count == 0) inventory.carried = null; + } else if (clicked != null && inventory.carried != null) { + + if (slot.mayPlace(inventory.carried)) { + if (clicked.id != inventory.carried.id) { + if (inventory.carried.count <= slot.getMaxStackSize()) { + ItemInstance tmp = clicked; + slot.set(inventory.carried); + inventory.carried = tmp; + } + } else if (clicked.id == inventory.carried.id) { + if (buttonNum == 0) { + int c = inventory.carried.count; + if (c > slot.getMaxStackSize() - clicked.count) c = slot.getMaxStackSize() - clicked.count; + if (c > inventory.carried.getMaxStackSize() - clicked.count) c = inventory.carried.getMaxStackSize() - clicked.count; + inventory.carried.remove(c); + if (inventory.carried.count == 0) inventory.carried = null; + clicked.count += c; + } else if (buttonNum == 1) { + int c = 1; + if (c > slot.getMaxStackSize() - clicked.count) c = slot.getMaxStackSize() - clicked.count; + if (c > inventory.carried.getMaxStackSize() - clicked.count) c = inventory.carried.getMaxStackSize() - clicked.count; + inventory.carried.remove(c); + if (inventory.carried.count == 0) inventory.carried = null; + clicked.count += c; + } + } + } else { + if (clicked.id == inventory.carried.id && inventory.carried.getMaxStackSize() > 1) { + int c = clicked.count; + if (c > 0 && c + inventory.carried.count <= inventory.carried.getMaxStackSize()) { + inventory.carried.count += c; + clicked.remove(c); + if (clicked.count == 0) slot.set(null); + slot.onTake(); + } + } + } + } + slot.setChanged(); + } else if (inventory.carried != null) { + if (clickedOutside) { + if (buttonNum == 0) { + player.drop(inventory.carried); + inventory.carried = null; + } + if (buttonNum == 1) { + player.drop(inventory.carried.remove(1)); + if (inventory.carried.count == 0) inventory.carried = null; + } + } + } + } + + public static void handleClose(Player player) { + Inventory inventory = player.inventory; + if (inventory.carried != null) { + player.drop(inventory.carried); + inventory.carried = null; + } + }*/ + +}; \ No newline at end of file diff --git a/Minecraft.World/MoveControl.cpp b/Minecraft.World/MoveControl.cpp new file mode 100644 index 00000000..30e94e68 --- /dev/null +++ b/Minecraft.World/MoveControl.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.phys.h" +#include "MoveControl.h" + +const float MoveControl::MIN_SPEED = 0.0005f; +const float MoveControl::MIN_SPEED_SQR = MIN_SPEED * MIN_SPEED; + +MoveControl::MoveControl(Mob *mob) +{ + this->mob = mob; + wantedX = mob->x; + wantedY = mob->y; + wantedZ = mob->z; + + speed = 0.0f; + + _hasWanted = false; +} + +bool MoveControl::hasWanted() +{ + return _hasWanted; +} + +float MoveControl::getSpeed() +{ + return speed; +} + +void MoveControl::setWantedPosition(double x, double y, double z, float speed) +{ + wantedX = x; + wantedY = y; + wantedZ = z; + this->speed = speed; + _hasWanted = true; +} + +void MoveControl::tick() +{ + mob->setYya(0); + if (!_hasWanted) return; + _hasWanted = false; + + int yFloor = floor(mob->bb->y0 + .5f); + + double xd = wantedX - mob->x; + double zd = wantedZ - mob->z; + double yd = wantedY - yFloor; + double dd = xd * xd + yd * yd + zd * zd; + if (dd < MIN_SPEED_SQR) return; + + float yRotD = (float) (atan2(zd, xd) * 180 / PI) - 90; + + mob->yRot = rotlerp(mob->yRot, yRotD, MAX_TURN); + mob->setSpeed(speed * mob->getWalkingSpeedModifier()); + + if (yd > 0 && xd * xd + zd * zd < 1) mob->getJumpControl()->jump(); +} + +float MoveControl::rotlerp(float a, float b, float max) +{ + float diff = Mth::wrapDegrees(b - a); + if (diff > max) + { + diff = max; + } + if (diff < -max) + { + diff = -max; + } + return a + diff; +} \ No newline at end of file diff --git a/Minecraft.World/MoveControl.h b/Minecraft.World/MoveControl.h new file mode 100644 index 00000000..4138280e --- /dev/null +++ b/Minecraft.World/MoveControl.h @@ -0,0 +1,34 @@ +#pragma once + +#include "Control.h" + +class Mob; + +class MoveControl : public Control +{ +public: + static const float MIN_SPEED; + static const float MIN_SPEED_SQR; + +private: + static const int MAX_TURN = 30; + + Mob *mob; + double wantedX; + double wantedY; + double wantedZ; + float speed; + bool _hasWanted; + +public: + MoveControl(Mob *mob); + + bool hasWanted(); + float getSpeed(); + void setWantedPosition(double x, double y, double z, float speed); + void setSpeed(float speed); + virtual void tick(); + +private: + float rotlerp(float a, float b, float max); +}; \ No newline at end of file diff --git a/Minecraft.World/MoveEntityPacket.cpp b/Minecraft.World/MoveEntityPacket.cpp new file mode 100644 index 00000000..b52482f5 --- /dev/null +++ b/Minecraft.World/MoveEntityPacket.cpp @@ -0,0 +1,168 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "MoveEntityPacket.h" + +MoveEntityPacket::MoveEntityPacket() +{ + hasRot = false; + + id = -1; + xa = 0; + ya = 0; + za = 0; + yRot = 0; + xRot = 0; +} + +MoveEntityPacket::MoveEntityPacket(int id) +{ + this->id = id; + hasRot = false; + + xa = 0; + ya = 0; + za = 0; + yRot = 0; + xRot = 0; +} + +void MoveEntityPacket::read(DataInputStream *dis) //throws IOException +{ + id = dis->readShort(); +} + +void MoveEntityPacket::write(DataOutputStream *dos) //throws IOException +{ + if( (id < 0 ) || (id >= 2048 ) ) + { + // We shouln't be tracking an entity that doesn't have a short type of id + __debugbreak(); + } + dos->writeShort((short)id); +} + +void MoveEntityPacket::handle(PacketListener *listener) +{ + listener->handleMoveEntity(shared_from_this()); +} + +int MoveEntityPacket::getEstimatedSize() +{ + return 2; +} + +bool MoveEntityPacket::canBeInvalidated() +{ + return true; +} + +bool MoveEntityPacket::isInvalidatedBy(shared_ptr packet) +{ + shared_ptr target = dynamic_pointer_cast(packet); + return target != NULL && target->id == id; +} + +MoveEntityPacket::PosRot::PosRot() +{ + hasRot = true; +} + +MoveEntityPacket::PosRot::PosRot(int id, char xa, char ya, char za, char yRot, char xRot) : MoveEntityPacket( id ) +{ + this->xa = xa; + this->ya = ya; + this->za = za; + this->yRot = yRot; + this->xRot = xRot; + hasRot = true; +} + +void MoveEntityPacket::PosRot::read(DataInputStream *dis) //throws IOException +{ + MoveEntityPacket::read(dis); + xa = dis->readByte(); + ya = dis->readByte(); + za = dis->readByte(); + yRot = dis->readByte(); + xRot = dis->readByte(); +} + +void MoveEntityPacket::PosRot::write(DataOutputStream *dos) //throws IOException +{ + MoveEntityPacket::write(dos); + dos->writeByte(xa); + dos->writeByte(ya); + dos->writeByte(za); + dos->writeByte(yRot); + dos->writeByte(xRot); +} + +int MoveEntityPacket::PosRot::getEstimatedSize() +{ + return 2+5; +} + +MoveEntityPacket::Pos::Pos() +{ +} + +MoveEntityPacket::Pos::Pos(int id, char xa, char ya, char za) : MoveEntityPacket(id) +{ + this->xa = xa; + this->ya = ya; + this->za = za; +} + +void MoveEntityPacket::Pos::read(DataInputStream *dis) //throws IOException +{ + MoveEntityPacket::read(dis); + xa = dis->readByte(); + ya = dis->readByte(); + za = dis->readByte(); +} + +void MoveEntityPacket::Pos::write(DataOutputStream *dos) //throws IOException +{ + MoveEntityPacket::write(dos); + dos->writeByte(xa); + dos->writeByte(ya); + dos->writeByte(za); +} + +int MoveEntityPacket::Pos::getEstimatedSize() +{ + return 2+3; +} + +MoveEntityPacket::Rot::Rot() +{ + hasRot = true; +} + +MoveEntityPacket::Rot::Rot(int id, char yRot, char xRot) : MoveEntityPacket(id) +{ + this->yRot = yRot; + this->xRot = xRot; + hasRot = true; +} + +void MoveEntityPacket::Rot::read(DataInputStream *dis) //throws IOException +{ + MoveEntityPacket::read(dis); + yRot = dis->readByte(); + xRot = dis->readByte(); +} + +void MoveEntityPacket::Rot::write(DataOutputStream *dos) //throws IOException +{ + MoveEntityPacket::write(dos); + dos->writeByte(yRot); + dos->writeByte(xRot); +} + +int MoveEntityPacket::Rot::getEstimatedSize() +{ + return 2+2; +} diff --git a/Minecraft.World/MoveEntityPacket.h b/Minecraft.World/MoveEntityPacket.h new file mode 100644 index 00000000..ab16ac3a --- /dev/null +++ b/Minecraft.World/MoveEntityPacket.h @@ -0,0 +1,78 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class MoveEntityPacket : public Packet, public enable_shared_from_this +{ + + // 4J JEV, static inner/sub classes +public: + class PosRot; + class Pos; + class Rot; + + int id; + char xa, ya, za, yRot, xRot; + bool hasRot; + + MoveEntityPacket(); + MoveEntityPacket(int id); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + +public: + static shared_ptr create() { return shared_ptr(new MoveEntityPacket()); } + virtual int getId() { return 30; } +}; + +class MoveEntityPacket::PosRot : public MoveEntityPacket +{ +public: + PosRot(); + PosRot(int id, char xa, char ya, char za, char yRot, char xRot); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new MoveEntityPacket::PosRot()); } + virtual int getId() { return 33; } +}; + +class MoveEntityPacket::Pos : public MoveEntityPacket +{ +public: + Pos(); + Pos(int id, char xa, char ya, char za); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new MoveEntityPacket::Pos()); } + virtual int getId() { return 31; } +}; + +class MoveEntityPacket::Rot : public MoveEntityPacket +{ +public: + Rot(); + Rot(int id, char yRot, char xRot); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new MoveEntityPacket::Rot()); } + virtual int getId() { return 32; } + +}; \ No newline at end of file diff --git a/Minecraft.World/MoveEntityPacketSmall.cpp b/Minecraft.World/MoveEntityPacketSmall.cpp new file mode 100644 index 00000000..8216478f --- /dev/null +++ b/Minecraft.World/MoveEntityPacketSmall.cpp @@ -0,0 +1,191 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "MoveEntityPacketSmall.h" + + +MoveEntityPacketSmall::MoveEntityPacketSmall() +{ + hasRot = false; + + id = -1; + xa = 0; + ya = 0; + za = 0; + yRot = 0; + xRot = 0; +} + +MoveEntityPacketSmall::MoveEntityPacketSmall(int id) +{ + if( (id < 0 ) || (id >= 2048 ) ) + { + // We shouln't be tracking an entity that doesn't have a short type of id + __debugbreak(); + } + + this->id = id; + hasRot = false; + + xa = 0; + ya = 0; + za = 0; + yRot = 0; + xRot = 0; +} + +void MoveEntityPacketSmall::read(DataInputStream *dis) //throws IOException +{ + id = dis->readShort(); +} + +void MoveEntityPacketSmall::write(DataOutputStream *dos) //throws IOException +{ + if( (id < 0 ) || (id >= 2048 ) ) + { + // We shouln't be tracking an entity that doesn't have a short type of id + __debugbreak(); + } + dos->writeShort((short)id); +} + +void MoveEntityPacketSmall::handle(PacketListener *listener) +{ + listener->handleMoveEntitySmall(shared_from_this()); +} + +int MoveEntityPacketSmall::getEstimatedSize() +{ + return 2; +} + +bool MoveEntityPacketSmall::canBeInvalidated() +{ + return true; +} + +bool MoveEntityPacketSmall::isInvalidatedBy(shared_ptr packet) +{ + shared_ptr target = dynamic_pointer_cast(packet); + return target != NULL && target->id == id; +} + +MoveEntityPacketSmall::PosRot::PosRot() +{ + hasRot = true; +} + +MoveEntityPacketSmall::PosRot::PosRot(int id, char xa, char ya, char za, char yRot, char xRot) : MoveEntityPacketSmall( id ) +{ + this->xa = xa; + this->ya = ya; + this->za = za; + this->yRot = yRot; + this->xRot = xRot; + hasRot = true; +} + +void MoveEntityPacketSmall::PosRot::read(DataInputStream *dis) //throws IOException +{ + int idAndRot = dis->readShort(); + this->id = idAndRot & 0x07ff; + this->yRot = idAndRot >> 11; + int xAndYAndZ = (int)dis->readShort(); + this->xa = xAndYAndZ >> 11; + this->ya = (xAndYAndZ << 21 ) >> 26; + this->za = (xAndYAndZ << 27 ) >> 27; +} + +void MoveEntityPacketSmall::PosRot::write(DataOutputStream *dos) //throws IOException +{ + if( (id < 0 ) || (id >= 2048 ) ) + { + // We shouln't be tracking an entity that doesn't have a short type of id + __debugbreak(); + } + short idAndRot = id | yRot << 11; + dos->writeShort(idAndRot); + short xAndYAndZ = ( xa << 11 ) | ( ( ya & 0x3f ) << 5 ) | ( za & 0x1f ); + dos->writeShort(xAndYAndZ); +} + +int MoveEntityPacketSmall::PosRot::getEstimatedSize() +{ + return 4; +} + +MoveEntityPacketSmall::Pos::Pos() +{ +} + +MoveEntityPacketSmall::Pos::Pos(int id, char xa, char ya, char za) : MoveEntityPacketSmall(id) +{ + this->xa = xa; + this->ya = ya; + this->za = za; +} + +void MoveEntityPacketSmall::Pos::read(DataInputStream *dis) //throws IOException +{ + int idAndY = dis->readShort(); + this->id = idAndY & 0x07ff; + this->ya = idAndY >> 11; + int XandZ = (int)((signed char)(dis->readByte())); + xa = XandZ >> 4; + za = ( XandZ << 28 ) >> 28; +} + +void MoveEntityPacketSmall::Pos::write(DataOutputStream *dos) //throws IOException +{ + if( (id < 0 ) || (id >= 2048 ) ) + { + // We shouln't be tracking an entity that doesn't have a short type of id + __debugbreak(); + } + short idAndY = id | ya << 11; + dos->writeShort(idAndY); + char XandZ = ( xa << 4 ) | ( za & 0x0f ); + dos->writeByte(XandZ); +} + +int MoveEntityPacketSmall::Pos::getEstimatedSize() +{ + return 3; +} + +MoveEntityPacketSmall::Rot::Rot() +{ + hasRot = true; +} + +MoveEntityPacketSmall::Rot::Rot(int id, char yRot, char xRot) : MoveEntityPacketSmall(id) +{ + + this->yRot = yRot; + this->xRot = xRot; + hasRot = true; +} + +void MoveEntityPacketSmall::Rot::read(DataInputStream *dis) //throws IOException +{ + int idAndRot = (int)dis->readShort(); + this->id = idAndRot & 0x07ff; + this->yRot = idAndRot >> 11; +} + +void MoveEntityPacketSmall::Rot::write(DataOutputStream *dos) //throws IOException +{ + if( (id < 0 ) || (id >= 2048 ) ) + { + // We shouln't be tracking an entity that doesn't have a short type of id + __debugbreak(); + } + short idAndRot = id | yRot << 11; + dos->writeShort(idAndRot); +} + +int MoveEntityPacketSmall::Rot::getEstimatedSize() +{ + return 2; +} diff --git a/Minecraft.World/MoveEntityPacketSmall.h b/Minecraft.World/MoveEntityPacketSmall.h new file mode 100644 index 00000000..4218f11b --- /dev/null +++ b/Minecraft.World/MoveEntityPacketSmall.h @@ -0,0 +1,79 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class MoveEntityPacketSmall : public Packet, public enable_shared_from_this +{ + + // 4J JEV, static inner/sub classes +public: + class PosRot; + class Pos; + class Rot; + + int id; + char xa, ya, za, yRot, xRot; + bool hasRot; + + MoveEntityPacketSmall(); + MoveEntityPacketSmall(int id); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + +public: + static shared_ptr create() { return shared_ptr(new MoveEntityPacketSmall()); } + virtual int getId() { return 162; } +}; + +class MoveEntityPacketSmall::PosRot : public MoveEntityPacketSmall +{ +public: + PosRot(); + PosRot(int id, char xa, char ya, char za, char yRot, char xRot); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new MoveEntityPacketSmall::PosRot()); } + virtual int getId() { return 165; } +}; + +class MoveEntityPacketSmall::Pos : public MoveEntityPacketSmall +{ +public: + Pos(); + Pos(int id, char xa, char ya, char za); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new MoveEntityPacketSmall::Pos()); } + virtual int getId() { return 163; } + +}; + +class MoveEntityPacketSmall::Rot : public MoveEntityPacketSmall +{ +public: + Rot(); + Rot(int id, char yRot, char xRot); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new MoveEntityPacketSmall::Rot()); } + virtual int getId() { return 164; } + +}; \ No newline at end of file diff --git a/Minecraft.World/MoveIndoorsGoal.cpp b/Minecraft.World/MoveIndoorsGoal.cpp new file mode 100644 index 00000000..215f9a29 --- /dev/null +++ b/Minecraft.World/MoveIndoorsGoal.cpp @@ -0,0 +1,65 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.util.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.village.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.dimension.h" +#include "MoveIndoorsGoal.h" + +MoveIndoorsGoal::MoveIndoorsGoal(PathfinderMob *mob) +{ + insideX = insideZ = -1; + + this->mob = mob; + setRequiredControlFlags(Control::MoveControlFlag); +} + +bool MoveIndoorsGoal::canUse() +{ + if ((mob->level->isDay() && !mob->level->isRaining()) || mob->level->dimension->hasCeiling) return false; + if (mob->getRandom()->nextInt(50) != 0) return false; + if (insideX != -1 && mob->distanceToSqr(insideX, mob->y, insideZ) < 2 * 2) return false; + shared_ptr village = mob->level->villages->getClosestVillage(Mth::floor(mob->x), Mth::floor(mob->y), Mth::floor(mob->z), 14); + if (village == NULL) return false; + shared_ptr _doorInfo = village->getBestDoorInfo(Mth::floor(mob->x), Mth::floor(mob->y), Mth::floor(mob->z)); + doorInfo = _doorInfo; + return _doorInfo != NULL; +} + +bool MoveIndoorsGoal::canContinueToUse() +{ + return !mob->getNavigation()->isDone(); +} + +void MoveIndoorsGoal::start() +{ + insideX = -1; + shared_ptr _doorInfo = doorInfo.lock(); + if( _doorInfo == NULL ) + { + doorInfo = weak_ptr(); + return; + } + if (mob->distanceToSqr(_doorInfo->getIndoorX(), _doorInfo->y, _doorInfo->getIndoorZ()) > 16 * 16) + { + Vec3 *pos = RandomPos::getPosTowards(dynamic_pointer_cast(mob->shared_from_this()), 14, 3, Vec3::newTemp(_doorInfo->getIndoorX() + 0.5, _doorInfo->getIndoorY(), _doorInfo->getIndoorZ() + 0.5)); + if (pos != NULL) mob->getNavigation()->moveTo(pos->x, pos->y, pos->z, 0.3f); + } + else mob->getNavigation()->moveTo(_doorInfo->getIndoorX() + 0.5, _doorInfo->getIndoorY(), _doorInfo->getIndoorZ() + 0.5, 0.3f); +} + +void MoveIndoorsGoal::stop() +{ + shared_ptr _doorInfo = doorInfo.lock(); + if( _doorInfo == NULL ) + { + doorInfo = weak_ptr(); + return; + } + + insideX = _doorInfo->getIndoorX(); + insideZ = _doorInfo->getIndoorZ(); + doorInfo = weak_ptr(); +} \ No newline at end of file diff --git a/Minecraft.World/MoveIndoorsGoal.h b/Minecraft.World/MoveIndoorsGoal.h new file mode 100644 index 00000000..4aab62fd --- /dev/null +++ b/Minecraft.World/MoveIndoorsGoal.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Goal.h" + +class PathfinderMob; +class DoorInfo; + +class MoveIndoorsGoal : public Goal +{ +private: + PathfinderMob *mob; + weak_ptr doorInfo; + int insideX, insideZ; + +public: + MoveIndoorsGoal(PathfinderMob *mob); + + bool canUse(); + bool canContinueToUse(); + void start(); + void stop(); +}; \ No newline at end of file diff --git a/Minecraft.World/MovePlayerPacket.cpp b/Minecraft.World/MovePlayerPacket.cpp new file mode 100644 index 00000000..9f0a8867 --- /dev/null +++ b/Minecraft.World/MovePlayerPacket.cpp @@ -0,0 +1,188 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "MovePlayerPacket.h" + +MovePlayerPacket::MovePlayerPacket() +{ + x = 0; + y = 0; + z = 0; + yView = 0; + yRot = 0; + xRot = 0; + onGround = false; + hasPos = false; + hasRot = false; + isFlying = false; +} + +MovePlayerPacket::MovePlayerPacket(bool onGround, bool isFlying) +{ + x = 0; + y = 0; + z = 0; + yView = 0; + yRot = 0; + xRot = 0; + hasPos = false; + hasRot = false; + + this->onGround = onGround; + this->isFlying = isFlying; +} + +void MovePlayerPacket::handle(PacketListener *listener) +{ + listener->handleMovePlayer(shared_from_this()); +} + +void MovePlayerPacket::read(DataInputStream *dis) //throws IOException +{ + char value = dis->read(); + onGround = (value & 0x1) != 0; + isFlying = (value & 0x2) != 0; + //onGround = dis->read() != 0; +} + +void MovePlayerPacket::write(DataOutputStream *dos) //throws IOException +{ + char value = (onGround ? 0x1 : 0) | (isFlying ? 0x2 : 0); + dos->write(value); + //dos->write(onGround ? 1 : 0); +} + +int MovePlayerPacket::getEstimatedSize() +{ + return 1; +} + +bool MovePlayerPacket::canBeInvalidated() +{ + return true; +} + +bool MovePlayerPacket::isInvalidatedBy(shared_ptr packet) +{ + return true; +} + +MovePlayerPacket::PosRot::PosRot() +{ + hasRot = true; + hasPos = true; +} + +MovePlayerPacket::PosRot::PosRot(double x, double y, double yView, double z, float yRot, float xRot, bool onGround, bool isFlying) +{ + this->x = x; + this->y = y; + this->yView = yView; + this->z = z; + this->yRot = yRot; + this->xRot = xRot; + this->onGround = onGround; + hasRot = true; + hasPos = true; + this->isFlying = isFlying; +} + +void MovePlayerPacket::PosRot::read(DataInputStream *dis) //throws IOException +{ + x = dis->readDouble(); + y = dis->readDouble(); + yView = dis->readDouble(); + z = dis->readDouble(); + yRot = dis->readFloat(); + xRot = dis->readFloat(); + MovePlayerPacket::read(dis); +} + +void MovePlayerPacket::PosRot::write(DataOutputStream *dos) //throws IOException +{ + dos->writeDouble(x); + dos->writeDouble(y); + dos->writeDouble(yView); + dos->writeDouble(z); + dos->writeFloat(yRot); + dos->writeFloat(xRot); + MovePlayerPacket::write(dos); +} + +int MovePlayerPacket::PosRot::getEstimatedSize() +{ + return 8 * 5 + 1; +} + +MovePlayerPacket::Pos::Pos() +{ + hasPos = true; +} + +MovePlayerPacket::Pos::Pos(double x, double y, double yView, double z, bool onGround, bool isFlying) +{ + this->x = x; + this->y = y; + this->yView = yView; + this->z = z; + this->onGround = onGround; + hasPos = true; + this->isFlying = isFlying; +} + +void MovePlayerPacket::Pos::read(DataInputStream *dis) //throws IOException +{ + x = dis->readDouble(); + y = dis->readDouble(); + yView = dis->readDouble(); + z = dis->readDouble(); + MovePlayerPacket::read(dis); +} + +void MovePlayerPacket::Pos::write(DataOutputStream *dos) //throws IOException +{ + dos->writeDouble(x); + dos->writeDouble(y); + dos->writeDouble(yView); + dos->writeDouble(z); + MovePlayerPacket::write(dos); +} + +int MovePlayerPacket::Pos::getEstimatedSize() +{ + return 8 * 4 + 1; +} + +MovePlayerPacket::Rot::Rot() +{ + hasRot = true; +} + +MovePlayerPacket::Rot::Rot(float yRot, float xRot, bool onGround, bool isFlying) +{ + this->yRot = yRot; + this->xRot = xRot; + this->onGround = onGround; + hasRot = true; + this->isFlying = isFlying; +} + +void MovePlayerPacket::Rot::read(DataInputStream *dis) //throws IOException +{ + yRot = dis->readFloat(); + xRot = dis->readFloat(); + MovePlayerPacket::read(dis); +} + +void MovePlayerPacket::Rot::write(DataOutputStream *dos) //throws IOException +{ + dos->writeFloat(yRot); + dos->writeFloat(xRot); + MovePlayerPacket::write(dos); +} + +int MovePlayerPacket::Rot::getEstimatedSize() +{ + return 8 + 1; +} diff --git a/Minecraft.World/MovePlayerPacket.h b/Minecraft.World/MovePlayerPacket.h new file mode 100644 index 00000000..26ae8392 --- /dev/null +++ b/Minecraft.World/MovePlayerPacket.h @@ -0,0 +1,78 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class MovePlayerPacket : public Packet, public enable_shared_from_this +{ +public: + class PosRot; + class Pos; + class Rot; + + double x, y, z, yView; + float yRot, xRot; + bool onGround; + bool hasPos, hasRot; + bool isFlying; // 4J Added + + MovePlayerPacket(); + MovePlayerPacket(bool onGround, bool isFlying); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + +public: + static shared_ptr create() { return shared_ptr(new MovePlayerPacket()); } + virtual int getId() { return 10; } +}; + +class MovePlayerPacket::PosRot : public MovePlayerPacket +{ +public: + PosRot(); + PosRot(double x, double y, double yView, double z, float yRot, float xRot, bool onGround, bool isFlying); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new MovePlayerPacket::PosRot()); } + virtual int getId() { return 13; } +}; + +class MovePlayerPacket::Pos : public MovePlayerPacket +{ +public: + Pos(); + Pos(double x, double y, double yView, double z, bool onGround, bool isFlying); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new MovePlayerPacket::Pos()); } + virtual int getId() { return 11; } + +}; + +class MovePlayerPacket::Rot : public MovePlayerPacket +{ +public: + Rot(); + Rot(float yRot, float xRot, bool onGround, bool isFlying); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); +public: + static shared_ptr create() { return shared_ptr(new MovePlayerPacket::Rot()); } + virtual int getId() { return 12; } + +}; \ No newline at end of file diff --git a/Minecraft.World/MoveThroughVillageGoal.cpp b/Minecraft.World/MoveThroughVillageGoal.cpp new file mode 100644 index 00000000..2405374f --- /dev/null +++ b/Minecraft.World/MoveThroughVillageGoal.cpp @@ -0,0 +1,127 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.util.h" +#include "net.minecraft.world.entity.ai.village.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "BasicTypeContainers.h" +#include "MoveThroughVillageGoal.h" +#include "Path.h" + +MoveThroughVillageGoal::MoveThroughVillageGoal(PathfinderMob *mob, float speed, bool onlyAtNight) +{ + path = NULL; + doorInfo = weak_ptr(); + + this->mob = mob; + this->speed = speed; + this->onlyAtNight = onlyAtNight; + setRequiredControlFlags(Control::MoveControlFlag); +} + +MoveThroughVillageGoal::~MoveThroughVillageGoal() +{ + if(path != NULL) delete path; +} + +bool MoveThroughVillageGoal::canUse() +{ + updateVisited(); + + if (onlyAtNight && mob->level->isDay()) return false; + + shared_ptr village = mob->level->villages->getClosestVillage(Mth::floor(mob->x), Mth::floor(mob->y), Mth::floor(mob->z), 0); + if (village == NULL) return false; + + shared_ptr _doorInfo = getNextDoorInfo(village); + if (_doorInfo == NULL) return false; + doorInfo = _doorInfo; + + bool oldCanOpenDoors = mob->getNavigation()->canOpenDoors(); + mob->getNavigation()->setCanOpenDoors(false); + delete path; + + path = mob->getNavigation()->createPath(_doorInfo->x, _doorInfo->y, _doorInfo->z); + mob->getNavigation()->setCanOpenDoors(oldCanOpenDoors); + if (path != NULL) return true; + + Vec3 *pos = RandomPos::getPosTowards(dynamic_pointer_cast(mob->shared_from_this()), 10, 7, Vec3::newTemp(_doorInfo->x, _doorInfo->y, _doorInfo->z)); + if (pos == NULL) return false; + mob->getNavigation()->setCanOpenDoors(false); + delete path; + path = mob->getNavigation()->createPath(pos->x, pos->y, pos->z); + mob->getNavigation()->setCanOpenDoors(oldCanOpenDoors); + return path != NULL; +} + +bool MoveThroughVillageGoal::canContinueToUse() +{ + if (mob->getNavigation()->isDone()) return false; + float dist = mob->bbWidth + 4.f; + shared_ptr _doorInfo = doorInfo.lock(); + if( _doorInfo == NULL ) return false; + + return mob->distanceToSqr(_doorInfo->x, _doorInfo->y, _doorInfo->z) > dist * dist; +} + +void MoveThroughVillageGoal::start() +{ + mob->getNavigation()->moveTo(path, speed); + path = NULL; +} + +void MoveThroughVillageGoal::stop() +{ + shared_ptr _doorInfo = doorInfo.lock(); + if( _doorInfo == NULL ) return; + + if (mob->getNavigation()->isDone() || mob->distanceToSqr(_doorInfo->x, _doorInfo->y, _doorInfo->z) < 4 * 4) + { + visited.push_back(doorInfo); + } +} + +shared_ptr MoveThroughVillageGoal::getNextDoorInfo(shared_ptr village) +{ + shared_ptr closest = nullptr; + int closestDistSqr = Integer::MAX_VALUE; + vector > *doorInfos = village->getDoorInfos(); + //for (DoorInfo di : doorInfos) + for(AUTO_VAR(it, doorInfos->begin()); it != doorInfos->end(); ++it) + { + shared_ptr di = *it; + int distSqr = di->distanceToSqr(Mth::floor(mob->x), Mth::floor(mob->y), Mth::floor(mob->z)); + if (distSqr < closestDistSqr) + { + if (hasVisited(di)) continue; + closest = di; + closestDistSqr = distSqr; + } + } + return closest; +} + +bool MoveThroughVillageGoal::hasVisited(shared_ptrdi) +{ + //for (DoorInfo di2 : visited) + for(AUTO_VAR(it, visited.begin()); it != visited.end(); ) + { + shared_ptr di2 = (*it).lock(); + if( di2 == NULL ) + { + it = visited.erase(it); + } + else + { + if (di->x == di2->x && di->y == di2->y && di->z == di2->z) return true; + ++it; + } + } + return false; +} + +void MoveThroughVillageGoal::updateVisited() +{ + if (visited.size() > 15) visited.erase(visited.begin()); +} \ No newline at end of file diff --git a/Minecraft.World/MoveThroughVillageGoal.h b/Minecraft.World/MoveThroughVillageGoal.h new file mode 100644 index 00000000..a087e522 --- /dev/null +++ b/Minecraft.World/MoveThroughVillageGoal.h @@ -0,0 +1,32 @@ +#pragma once + +#include "Goal.h" + +class PathfinderMob; +class Path; +class DoorInfo; + +class MoveThroughVillageGoal : public Goal +{ +private: + PathfinderMob *mob; + float speed; + Path *path; + weak_ptr doorInfo; + bool onlyAtNight; + vector< weak_ptr > visited; + +public: + MoveThroughVillageGoal(PathfinderMob *mob, float speed, bool onlyAtNight); + ~MoveThroughVillageGoal(); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + virtual void stop(); + +private: + shared_ptr getNextDoorInfo(shared_ptr village); + bool hasVisited(shared_ptr di); + void updateVisited(); +}; \ No newline at end of file diff --git a/Minecraft.World/MoveTowardsRestrictionGoal.cpp b/Minecraft.World/MoveTowardsRestrictionGoal.cpp new file mode 100644 index 00000000..a1848d46 --- /dev/null +++ b/Minecraft.World/MoveTowardsRestrictionGoal.cpp @@ -0,0 +1,38 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.util.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "MoveTowardsRestrictionGoal.h" + +MoveTowardsRestrictionGoal::MoveTowardsRestrictionGoal(PathfinderMob *mob, float speed) +{ + wantedX = wantedY = wantedZ = 0.0; + + this->mob = mob; + this->speed = speed; + setRequiredControlFlags(Control::MoveControlFlag); +} + +bool MoveTowardsRestrictionGoal::canUse() +{ + if (mob->isWithinRestriction()) return false; + Pos *towards = mob->getRestrictCenter(); + Vec3 *pos = RandomPos::getPosTowards(dynamic_pointer_cast(mob->shared_from_this()), 16, 7, Vec3::newTemp(towards->x, towards->y, towards->z)); + if (pos == NULL) return false; + wantedX = pos->x; + wantedY = pos->y; + wantedZ = pos->z; + return true; +} + +bool MoveTowardsRestrictionGoal::canContinueToUse() +{ + return !mob->getNavigation()->isDone(); +} + +void MoveTowardsRestrictionGoal::start() +{ + mob->getNavigation()->moveTo(wantedX, wantedY, wantedZ, speed); +} \ No newline at end of file diff --git a/Minecraft.World/MoveTowardsRestrictionGoal.h b/Minecraft.World/MoveTowardsRestrictionGoal.h new file mode 100644 index 00000000..859b5ee8 --- /dev/null +++ b/Minecraft.World/MoveTowardsRestrictionGoal.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Goal.h" + +class MoveTowardsRestrictionGoal : public Goal +{ +private: + PathfinderMob *mob; + double wantedX, wantedY, wantedZ; + float speed; + +public: + MoveTowardsRestrictionGoal(PathfinderMob *mob, float speed); + + bool canUse(); + bool canContinueToUse(); + void start(); +}; \ No newline at end of file diff --git a/Minecraft.World/MoveTowardsTargetGoal.cpp b/Minecraft.World/MoveTowardsTargetGoal.cpp new file mode 100644 index 00000000..6d9810b5 --- /dev/null +++ b/Minecraft.World/MoveTowardsTargetGoal.cpp @@ -0,0 +1,43 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.util.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.phys.h" +#include "MoveTowardsTargetGoal.h" + +MoveTowardsTargetGoal::MoveTowardsTargetGoal(PathfinderMob *mob, float speed, float within) +{ + this->mob = mob; + this->speed = speed; + this->within = within; + setRequiredControlFlags(Control::MoveControlFlag); +} + +bool MoveTowardsTargetGoal::canUse() +{ + target = weak_ptr(mob->getTarget()); + if (target.lock() == NULL) return false; + if (target.lock()->distanceToSqr(mob->shared_from_this()) > within * within) return false; + Vec3 *pos = RandomPos::getPosTowards(dynamic_pointer_cast(mob->shared_from_this()), 16, 7, Vec3::newTemp(target.lock()->x, target.lock()->y, target.lock()->z)); + if (pos == NULL) return false; + wantedX = pos->x; + wantedY = pos->y; + wantedZ = pos->z; + return true; +} + +bool MoveTowardsTargetGoal::canContinueToUse() +{ + return target.lock() != NULL && !mob->getNavigation()->isDone() && target.lock()->isAlive() && target.lock()->distanceToSqr(mob->shared_from_this()) < within * within; +} + +void MoveTowardsTargetGoal::stop() +{ + target = weak_ptr(); +} + +void MoveTowardsTargetGoal::start() +{ + mob->getNavigation()->moveTo(wantedX, wantedY, wantedZ, speed); +} \ No newline at end of file diff --git a/Minecraft.World/MoveTowardsTargetGoal.h b/Minecraft.World/MoveTowardsTargetGoal.h new file mode 100644 index 00000000..5186b752 --- /dev/null +++ b/Minecraft.World/MoveTowardsTargetGoal.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Goal.h" + +class MoveTowardsTargetGoal : public Goal +{ +private: + PathfinderMob *mob; + weak_ptr target; + double wantedX, wantedY, wantedZ; + float speed, within; + +public: + MoveTowardsTargetGoal(PathfinderMob *mob, float speed, float within); + + bool canUse(); + bool canContinueToUse(); + void stop(); + void start(); +}; \ No newline at end of file diff --git a/Minecraft.World/Mth.cpp b/Minecraft.World/Mth.cpp new file mode 100644 index 00000000..eccd828e --- /dev/null +++ b/Minecraft.World/Mth.cpp @@ -0,0 +1,172 @@ +#include "stdafx.h" +#include "Mth.h" +#include "Random.h" + +const int Mth::BIG_ENOUGH_INT = 1024; +const float Mth::BIG_ENOUGH_FLOAT = BIG_ENOUGH_INT; +const float Mth::RAD_TO_GRAD = PI / 180.0f; +const float Mth::DEGRAD = PI / 180.0f; +const float Mth::RADDEG = 180.0f / PI; + +float *Mth::_sin = NULL; + +const float Mth::sinScale = 65536.0f / (float) (PI * 2); + +// 4J - added - was in static constructor +void Mth::init() +{ + _sin = new float[65536]; + for (int i = 0; i < 65536; i++) + { + _sin[i] = (float) ::sin(i * PI * 2 / 65536.0f); + } +} + +float Mth::sin(float i) +{ + if(_sin == NULL) init(); // 4J - added + return _sin[(int) (i * sinScale) & 65535]; +} + +float Mth::cos(float i) +{ + if(_sin == NULL) init(); // 4J - added + return _sin[(int) (i * sinScale + 65536 / 4) & 65535]; +} + +float Mth::sqrt(float x) +{ + return (float) ::sqrt(x); +} + +float Mth::sqrt(double x) +{ + return (float) ::sqrt(x); +} + +int Mth::floor(float v) +{ + int i = (int) v; + return v < i ? i - 1 : i; +} + +__int64 Mth::lfloor(double v) +{ + __int64 i = (__int64) v; + return v < i ? i - 1 : i; +} + +int Mth::fastFloor(double x) +{ + return (int) (x + BIG_ENOUGH_FLOAT) - BIG_ENOUGH_INT; +} + +int Mth::floor(double v) +{ + int i = (int) v; + return v < i ? i - 1 : i; +} + +int Mth::absFloor(double v) +{ + return (int) (v >= 0 ? v : -v + 1); +} + +float Mth::abs(float v) +{ + return v >= 0 ? v : -v; +} + +int Mth::abs(int v) +{ + return v >= 0 ? v : -v; +} + +int Mth::ceil(float v) +{ + int i = (int) v; + return v > i ? i + 1 : i; +} + +int Mth::clamp(int value, int min, int max) +{ + if (value < min) + { + return min; + } + if (value > max) + { + return max; + } + return value; +} + +float Mth::clamp(float value, float min, float max) +{ + if (value < min) + { + return min; + } + if (value > max) + { + return max; + } + return value; +} + +double Mth::asbMax(double a, double b) +{ + if (a < 0) a = -a; + if (b < 0) b = -b; + return a > b ? a : b; +} + +int Mth::intFloorDiv(int a, int b) +{ + if (a < 0) return -((-a - 1) / b) - 1; + return a / b; +} + + +int Mth::nextInt(Random *random, int minInclusive, int maxInclusive) +{ + if (minInclusive >= maxInclusive) + { + return minInclusive; + } + return random->nextInt(maxInclusive - minInclusive + 1) + minInclusive; +} + +float Mth::wrapDegrees(float input) +{ + //input %= 360; + while (input >= 180) + { + input -= 360; + } + while (input < -180) + { + input += 360; + } + return input; +} + +double Mth::wrapDegrees(double input) +{ + //input %= 360; + while (input >= 180) + { + input -= 360; + } + while (input < -180) + { + input += 360; + } + return input; +} + +// 4J Added +bool Mth::almostEquals( double double1, double double2, double precision) +{ + return (std::abs(double1 - double2) <= precision); +} \ No newline at end of file diff --git a/Minecraft.World/Mth.h b/Minecraft.World/Mth.h new file mode 100644 index 00000000..17119fb1 --- /dev/null +++ b/Minecraft.World/Mth.h @@ -0,0 +1,42 @@ +#pragma once +class Random; +class Mth +{ +private: + static const int BIG_ENOUGH_INT; + static const float BIG_ENOUGH_FLOAT; + +public: + static const float RAD_TO_GRAD; +public: + static const float DEGRAD; + static const float RADDEG; +private: + static float *_sin; +private: + static const float sinScale; +public : + static void init(); // 4J added + static float sin(float i); + static float cos(float i); + static float sqrt(float x); + static float sqrt(double x); + static int floor(float v); + static __int64 lfloor(double v); + static int fastFloor(double x); + static int floor(double v); + static int absFloor(double v); + static float abs(float v); + static int abs(int v); + static int ceil(float v); + static int clamp(int value, int min, int max) ; + static float clamp(float value, float min, float max); + static double asbMax(double a, double b); + static int intFloorDiv(int a, int b); + static int nextInt(Random *random, int minInclusive, int maxInclusive); + static float wrapDegrees(float input); + static double wrapDegrees(double input); + + // 4J Added + static bool almostEquals( double double1, double double2, double precision); +}; \ No newline at end of file diff --git a/Minecraft.World/MultiTextureTileItem.cpp b/Minecraft.World/MultiTextureTileItem.cpp new file mode 100644 index 00000000..a41a896f --- /dev/null +++ b/Minecraft.World/MultiTextureTileItem.cpp @@ -0,0 +1,46 @@ +#include "stdafx.h" +#include "Tile.h" +#include "MultiTextureTileItem.h" + + +MultiTextureTileItem::MultiTextureTileItem(int id, Tile *parentTile, int *nameExtensions, int iLength) : TileItem(id) +{ + this->parentTile = parentTile; + this->nameExtensions = nameExtensions; + this->m_iNameExtensionsLength=iLength; + + setMaxDamage(0); + setStackedByData(true); +} + +Icon *MultiTextureTileItem::getIcon(int itemAuxValue) +{ + return parentTile->getTexture(2, itemAuxValue); +} + +int MultiTextureTileItem::getLevelDataForAuxValue(int auxValue) +{ + return auxValue; +} + +unsigned int MultiTextureTileItem::getDescriptionId(int iData) +{ + if (iData < 0 || iData >= m_iNameExtensionsLength) + { + iData = 0; + } + //return super.getDescriptionId() + "." + nameExtensions[auxValue]; + return nameExtensions[iData]; +} + +unsigned int MultiTextureTileItem::getDescriptionId(shared_ptr instance) +{ + int auxValue = instance->getAuxValue(); + if (auxValue < 0 || auxValue >= m_iNameExtensionsLength) + { + auxValue = 0; + } + //return super.getDescriptionId() + "." + nameExtensions[auxValue]; + return nameExtensions[auxValue]; +} + diff --git a/Minecraft.World/MultiTextureTileItem.h b/Minecraft.World/MultiTextureTileItem.h new file mode 100644 index 00000000..078357ea --- /dev/null +++ b/Minecraft.World/MultiTextureTileItem.h @@ -0,0 +1,22 @@ +#pragma once + +#include "TileItem.h" + +class Tile; + +class MultiTextureTileItem : public TileItem +{ +private: + Tile *parentTile; + //private final String[] nameExtensions; + int *nameExtensions; + int m_iNameExtensionsLength; + +public: + MultiTextureTileItem(int id, Tile *parentTile,int *nameExtensions, int iLength); + + virtual Icon *getIcon(int itemAuxValue); + virtual int getLevelDataForAuxValue(int auxValue); + virtual unsigned int getDescriptionId(int iData = -1); + virtual unsigned int getDescriptionId(shared_ptr instance); +}; diff --git a/Minecraft.World/Mushroom.cpp b/Minecraft.World/Mushroom.cpp new file mode 100644 index 00000000..1056cdec --- /dev/null +++ b/Minecraft.World/Mushroom.cpp @@ -0,0 +1,107 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.h" +#include "Mushroom.h" + +Mushroom::Mushroom(int id, const wstring &texture) : Bush(id) +{ + this->updateDefaultShape(); + this->setTicking(true); + this->texture = texture; +} + +// 4J Added override +void Mushroom::updateDefaultShape() +{ + float ss = 0.2f; + this->setShape(0.5f - ss, 0, 0.5f - ss, 0.5f + ss, ss * 2, 0.5f + ss); +} + +void Mushroom::tick(Level *level, int x, int y, int z, Random *random) +{ + if (random->nextInt(25) == 0) + { + int r = 4; + int max = 5; + for (int xx = x - r; xx <= x + r; xx++) + for (int zz = z - r; zz <= z + r; zz++) + for (int yy = y - 1; yy <= y + 1; yy++) + { + if (level->getTile(xx, yy, zz) == id && --max <= 0) return; + } + + int x2 = x + random->nextInt(3) - 1; + int y2 = y + random->nextInt(2) - random->nextInt(2); + int z2 = z + random->nextInt(3) - 1; + for (int i = 0; i < 4; i++) + { + if (level->isEmptyTile(x2, y2, z2) && canSurvive(level, x2, y2, z2)) + { + x = x2; + y = y2; + z = z2; + } + x2 = x + random->nextInt(3) - 1; + y2 = y + random->nextInt(2) - random->nextInt(2); + z2 = z + random->nextInt(3) - 1; + } + + if (level->isEmptyTile(x2, y2, z2) && canSurvive(level, x2, y2, z2)) + { + level->setTile(x2, y2, z2, id); + } + } +} + +bool Mushroom::mayPlace(Level *level, int x, int y, int z) +{ + return Bush::mayPlace(level, x, y, z) && canSurvive(level, x, y, z); +} + +bool Mushroom::mayPlaceOn(int tile) +{ + return Tile::solid[tile]; +} + +bool Mushroom::canSurvive(Level *level, int x, int y, int z) +{ + if (y < 0 || y >= Level::maxBuildHeight) return false; + + int below = level->getTile(x, y - 1, z); + + return below == Tile::mycel_Id || (level->getDaytimeRawBrightness(x, y, z) < 13 && mayPlaceOn(below)); +} + +bool Mushroom::growTree(Level *level, int x, int y, int z, Random *random) +{ + int data = level->getData(x, y, z); + + level->setTileNoUpdate(x, y, z, 0); + Feature *f = NULL; + + if (id == Tile::mushroom1_Id) + { + f = new HugeMushroomFeature(0); + } + else if (id == Tile::mushroom2_Id) + { + f = new HugeMushroomFeature(1); + } + + if (f == NULL || !f->place(level, random, x, y, z)) + { + level->setTileAndDataNoUpdate(x, y, z, this->id, data); + if( f != NULL ) + delete f; + return false; + } + if( f != NULL ) + delete f; + return true; +} + +void Mushroom::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(texture); +} diff --git a/Minecraft.World/Mushroom.h b/Minecraft.World/Mushroom.h new file mode 100644 index 00000000..260ba048 --- /dev/null +++ b/Minecraft.World/Mushroom.h @@ -0,0 +1,23 @@ +#pragma once +#include "Bush.h" + +class Random; + +class Mushroom : public Bush +{ + friend class Tile; +private: + wstring texture; +protected: + Mushroom(int id, const wstring &texture); +public: + virtual void updateDefaultShape(); // 4J Added override + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual bool mayPlace(Level *level, int x, int y, int z); +protected: + virtual bool mayPlaceOn(int tile); +public: + virtual bool canSurvive(Level *level, int x, int y, int z); + bool growTree(Level *level, int x, int y, int z, Random *random); + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/MushroomCow.cpp b/Minecraft.World/MushroomCow.cpp new file mode 100644 index 00000000..a2aef158 --- /dev/null +++ b/Minecraft.World/MushroomCow.cpp @@ -0,0 +1,84 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.item.h" +#include "..\Minecraft.Client\Textures.h" +#include "MushroomCow.h" +#include "MobCategory.h" +#include "AABB.h" + + + +MushroomCow::MushroomCow(Level *level) : Cow(level) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_RED_COW;// 4J was "/mob/redcow.png"; + this->setSize(0.9f, 1.3f); +} + +bool MushroomCow::interact(shared_ptr player) +{ + shared_ptr item = player->inventory->getSelected(); + if (item != NULL && item->id == Item::bowl_Id && getAge() >= 0) + { + if (item->count == 1) + { + player->inventory->setItem(player->inventory->selected, shared_ptr( new ItemInstance(Item::mushroomStew) ) ); + return true; + } + + if (player->inventory->add(shared_ptr(new ItemInstance(Item::mushroomStew))) && !player->abilities.instabuild) + { + player->inventory->removeItem(player->inventory->selected, 1); + return true; + } + } + if (item != NULL && item->id == Item::shears_Id && getAge() >= 0) + { + remove(); + level->addParticle(eParticleType_largeexplode, x, y + bbHeight / 2, z, 0, 0, 0); + if(!level->isClientSide) + { + // 4J Stu - We don't need to check spawn limits when adding the new cow, as we are removing the MushroomCow + remove(); + shared_ptr cow = shared_ptr( new Cow(level) ); + cow->moveTo(x, y, z, yRot, xRot); + cow->setHealth(getHealth()); + cow->yBodyRot = yBodyRot; + level->addEntity(cow); + for (int i = 0; i < 5; i++) + { + level->addEntity( shared_ptr( new ItemEntity(level, x, y + bbHeight, z, shared_ptr( new ItemInstance(Tile::mushroom2))) )); + } + return true; + } + return true; + } + return Cow::interact(player); +} + +// 4J - added so that mushroom cows have more of a chance of spawning, they can now spawn on mycelium as well as grass - seems a bit odd that they don't already really +bool MushroomCow::canSpawn() +{ + int xt = Mth::floor(x); + int yt = Mth::floor(bb->y0); + int zt = Mth::floor(z); + return ( level->getTile(xt, yt - 1, zt) == Tile::grass_Id || level->getTile(xt, yt - 1, zt) == Tile::mycel_Id ) && level->getDaytimeRawBrightness(xt, yt, zt) > 8 && PathfinderMob::canSpawn(); +} + +shared_ptr MushroomCow::getBreedOffspring(shared_ptr target) +{ + // 4J - added limit to number of animals that can be bred + if( level->canCreateMore( GetType(), Level::eSpawnType_Breed) ) + { + return shared_ptr( new MushroomCow(level) ); + } + else + { + return nullptr; + } +} diff --git a/Minecraft.World/MushroomCow.h b/Minecraft.World/MushroomCow.h new file mode 100644 index 00000000..4ec97476 --- /dev/null +++ b/Minecraft.World/MushroomCow.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Cow.h" + +class MushroomCow : public Cow +{ +public: + eINSTANCEOF GetType() { return eTYPE_MUSHROOMCOW; } + static Entity *create(Level *level) { return new MushroomCow(level); } + +public: + MushroomCow(Level *level); + + virtual bool interact(shared_ptr player); + virtual bool canSpawn(); // 4J added + virtual shared_ptr getBreedOffspring(shared_ptr target); +}; \ No newline at end of file diff --git a/Minecraft.World/MushroomIslandBiome.cpp b/Minecraft.World/MushroomIslandBiome.cpp new file mode 100644 index 00000000..fa7eafdb --- /dev/null +++ b/Minecraft.World/MushroomIslandBiome.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "MushroomIslandBiome.h" +#include "BiomeDecorator.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.level.tile.h" + +MushroomIslandBiome::MushroomIslandBiome(int id) : Biome(id) +{ + decorator->treeCount = -100; + decorator->flowerCount = -100; + decorator->grassCount = -100; + + decorator->mushroomCount = 1; + decorator->hugeMushrooms = 1; + + topMaterial = (byte) Tile::mycel_Id; + + enemies.clear(); + friendlies.clear(); + friendlies_chicken.clear(); // 4J added + friendlies_wolf.clear(); // 4J added + waterFriendlies.clear(); + + friendlies_mushroomcow.push_back(new MobSpawnerData(eTYPE_MUSHROOMCOW, 8, 4, 8)); // 4J moved to own category +} \ No newline at end of file diff --git a/Minecraft.World/MushroomIslandBiome.h b/Minecraft.World/MushroomIslandBiome.h new file mode 100644 index 00000000..41cb463c --- /dev/null +++ b/Minecraft.World/MushroomIslandBiome.h @@ -0,0 +1,8 @@ +#pragma once +#include "Biome.h" + +class MushroomIslandBiome : public Biome +{ +public: + MushroomIslandBiome(int id); +}; \ No newline at end of file diff --git a/Minecraft.World/MusicTile.cpp b/Minecraft.World/MusicTile.cpp new file mode 100644 index 00000000..ebdb9680 --- /dev/null +++ b/Minecraft.World/MusicTile.cpp @@ -0,0 +1,84 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "MusicTile.h" +#include "SoundTypes.h" + +MusicTile::MusicTile(int id) : EntityTile(id, Material::wood) +{ +} + +void MusicTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + app.DebugPrintf("-------- Neighbour changed type %d\n", type); + bool signal = level->hasNeighborSignal(x, y, z); + shared_ptr mte = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + app.DebugPrintf("-------- Signal is %s, tile is currently %s\n",signal?"TRUE":"FALSE", mte->on?"ON":"OFF"); + if (mte != NULL && mte->on != signal) + { + if (signal) + { + mte->playNote(level, x, y, z); + } + mte->on = signal; + } +} + +// 4J-PB - Adding a TestUse for tooltip display +bool MusicTile::TestUse() +{ + return true; +} + +bool MusicTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if (soundOnly) return false; + if (level->isClientSide) return true; + shared_ptr mte = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + if (mte != NULL ) + { + mte->tune(); + mte->playNote(level, x, y, z); + } + return true; +} + +void MusicTile::attack(Level *level, int x, int y, int z, shared_ptr player) +{ + if (level->isClientSide) return; + shared_ptr mte = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + if( mte != NULL ) mte->playNote(level, x, y, z); +} + +shared_ptr MusicTile::newTileEntity(Level *level) +{ + return shared_ptr( new MusicTileEntity() ); +} + +void MusicTile::triggerEvent(Level *level, int x, int y, int z, int i, int note) +{ + float pitch = (float) pow(2, (note - 12) / 12.0); + + int iSound; + switch(i) + { + case 1: + iSound=eSoundType_NOTE_BD; + break; + case 2: + iSound=eSoundType_NOTE_SNARE; + break; + case 3: + iSound=eSoundType_NOTE_HAT; + break; + case 4: + iSound=eSoundType_NOTE_BASSATTACK; + break; + default: + iSound=eSoundType_NOTE_HARP; + break; + } + app.DebugPrintf("MusicTile::triggerEvent - playSound - pitch = %f\n",pitch); + level->playSound(x + 0.5, y + 0.5, z + 0.5, iSound, 3, pitch); + level->addParticle(eParticleType_note, x + 0.5, y + 1.2, z + 0.5, note / 24.0, 0, 0); +} diff --git a/Minecraft.World/MusicTile.h b/Minecraft.World/MusicTile.h new file mode 100644 index 00000000..47333870 --- /dev/null +++ b/Minecraft.World/MusicTile.h @@ -0,0 +1,16 @@ +#pragma once +#include "EntityTile.h" + +class Player; + +class MusicTile : public EntityTile +{ +public: + MusicTile(int id); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual bool TestUse(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + virtual void attack(Level *level, int x, int y, int z, shared_ptr player); + virtual shared_ptr newTileEntity(Level *level); + virtual void triggerEvent(Level *level, int x, int y, int z, int i, int note); +}; \ No newline at end of file diff --git a/Minecraft.World/MusicTileEntity.cpp b/Minecraft.World/MusicTileEntity.cpp new file mode 100644 index 00000000..4c2d2615 --- /dev/null +++ b/Minecraft.World/MusicTileEntity.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.level.tile.h" + +#include "MusicTileEntity.h" + + + +MusicTileEntity::MusicTileEntity() : TileEntity() +{ + note = 0; + + on = false; +} + +void MusicTileEntity::save(CompoundTag *tag) +{ + TileEntity::save(tag); + tag->putByte(L"note", note); +} + +void MusicTileEntity::load(CompoundTag *tag) +{ + TileEntity::load(tag); + note = tag->getByte(L"note"); + if (note < 0) note = 0; + if (note > 24) note = 24; +} + +void MusicTileEntity::tune() +{ + note = (byte) ((note + 1) % 25); + setChanged(); +} + +void MusicTileEntity::playNote(Level *level, int x, int y, int z) +{ + if (level->getMaterial(x, y + 1, z) != Material::air) return; + + Material *m = level->getMaterial(x, y - 1, z); + + int i = 0; + if (m == Material::stone) i = 1; + if (m == Material::sand) i = 2; + if (m == Material::glass) i = 3; + if (m == Material::wood) i = 4; + + level->tileEvent(x, y, z, Tile::musicBlock_Id, i, note); +} + +// 4J Added +shared_ptr MusicTileEntity::clone() +{ + shared_ptr result = shared_ptr( new MusicTileEntity() ); + TileEntity::clone(result); + + result->note = note; + return result; +} + diff --git a/Minecraft.World/MusicTileEntity.h b/Minecraft.World/MusicTileEntity.h new file mode 100644 index 00000000..a4941ff1 --- /dev/null +++ b/Minecraft.World/MusicTileEntity.h @@ -0,0 +1,26 @@ +#pragma once +using namespace std; + +#include "TileEntity.h" + +class MusicTileEntity : public TileEntity +{ +public: + eINSTANCEOF GetType() { return eTYPE_MUSICTILEENTITY; } + static TileEntity *create() { return new MusicTileEntity(); } + +public: + byte note; + + bool on; + + MusicTileEntity(); + + virtual void save(CompoundTag *tag); + virtual void load(CompoundTag *tag); + void tune(); + void playNote(Level *level, int x, int y, int z); + + // 4J Added + virtual shared_ptr clone(); +}; diff --git a/Minecraft.World/MycelTile.cpp b/Minecraft.World/MycelTile.cpp new file mode 100644 index 00000000..7b8a90e1 --- /dev/null +++ b/Minecraft.World/MycelTile.cpp @@ -0,0 +1,74 @@ +#include "stdafx.h" +#include "MycelTile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.h" +#include "net.minecraft.world.h" + +MycelTile::MycelTile(int id) : Tile(id, Material::grass) +{ + iconTop = NULL; + iconSnowSide = NULL; + setTicking(true); +} + +Icon *MycelTile::getTexture(int face, int data) +{ + if (face == Facing::UP) return iconTop; + if (face == Facing::DOWN) return Tile::dirt->getTexture(face); + return icon; +} + +Icon *MycelTile::getTexture(LevelSource *level, int x, int y, int z, int face) +{ + if (face == Facing::UP) return iconTop; + if (face == Facing::DOWN) return Tile::dirt->getTexture(face); + Material *above = level->getMaterial(x, y + 1, z); + if (above == Material::topSnow || above == Material::snow) return iconSnowSide; + else return icon; +} + +void MycelTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"mycel_side"); + iconTop = iconRegister->registerIcon(L"mycel_top"); + iconSnowSide = iconRegister->registerIcon(L"snow_side"); +} + +void MycelTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (level->isClientSide) return; + + if (level->getRawBrightness(x, y + 1, z) < MIN_BRIGHTNESS && Tile::lightBlock[level->getTile(x, y + 1, z)] > 2) + { + level->setTile(x, y, z, Tile::dirt_Id); + } + else + { + if (level->getRawBrightness(x, y + 1, z) >= Level::MAX_BRIGHTNESS - 6) + { + for (int i=0; i<4; i++) + { + int xt = x + random->nextInt(3) - 1; + int yt = y + random->nextInt(5) - 3; + int zt = z + random->nextInt(3) - 1; + int above = level->getTile(xt, yt + 1, zt); + if (level->getTile(xt, yt, zt) == Tile::dirt_Id && level->getRawBrightness(xt, yt + 1, zt) >= MIN_BRIGHTNESS && Tile::lightBlock[above] <= 2) + { + level->setTile(xt, yt, zt, id); + } + } + } + } +} + +void MycelTile::animateTick(Level *level, int x, int y, int z, Random *random) +{ + Tile::animateTick(level, x, y, z, random); + if (random->nextInt(10) == 0) level->addParticle(eParticleType_townaura, x + random->nextFloat(), y + 1.1f, z + random->nextFloat(), 0, 0, 0); + +} + +int MycelTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::dirt->getResource(0, random, playerBonusLevel); +} \ No newline at end of file diff --git a/Minecraft.World/MycelTile.h b/Minecraft.World/MycelTile.h new file mode 100644 index 00000000..d36b536f --- /dev/null +++ b/Minecraft.World/MycelTile.h @@ -0,0 +1,23 @@ +#pragma once +#include "Tile.h" + +class ChunkRebuildData; +class MycelTile : public Tile +{ + friend class ChunkRebuildData; +public: + static const int MIN_BRIGHTNESS = 4; + +private: + Icon *iconTop; + Icon *iconSnowSide; +public: + MycelTile(int id); + + virtual Icon *getTexture(int face, int data); + virtual Icon *getTexture(LevelSource *level, int x, int y, int z, int face); + void registerIcons(IconRegister *iconRegister); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual void animateTick(Level *level, int x, int y, int z, Random *random); + virtual int getResource(int data, Random *random, int playerBonusLevel); +}; diff --git a/Minecraft.World/NbtIo.cpp b/Minecraft.World/NbtIo.cpp new file mode 100644 index 00000000..b6c63277 --- /dev/null +++ b/Minecraft.World/NbtIo.cpp @@ -0,0 +1,65 @@ +#include "stdafx.h" +#include "InputOutputStream.h" +#include "NbtIo.h" + +CompoundTag *NbtIo::readCompressed(InputStream *in) +{ + MemSect(26); + // 4J - this was using a try/finally block + DataInputStream dis = DataInputStream(in); // 4J - was new GZIPInputStream as well + CompoundTag *ret = NbtIo::read((DataInput *)&dis); + dis.close(); + MemSect(0); + return ret; +} + +void NbtIo::writeCompressed(CompoundTag *tag, OutputStream *out) +{ + // 4J - this was using a try/finally block + // 4J Stu - Buffer output in 1024 byte chunks so that we can allocate properly in the save file + BufferedOutputStream bos = BufferedOutputStream( out, 1024 ); + DataOutputStream dos = DataOutputStream(&bos); // 4J - was new GZIPOutputStream as well + NbtIo::write(tag, &dos); + dos.close(); +} + +// Reads tags from a stream created from the input buffer. Doesn't free the data in the source buffer. +CompoundTag *NbtIo::decompress(byteArray buffer) +{ + ByteArrayInputStream bais = ByteArrayInputStream(buffer); + // 4J - this was using a try/finally block + DataInputStream in = DataInputStream(&bais); // 4J - was new GZIPInputStream as well + CompoundTag *ret = NbtIo::read((DataInput *)&in); + bais.reset(); // This stops the buffer referenced by the input stream from being freed when it goes out of context + in.close(); + return ret; +} + +byteArray NbtIo::compress(CompoundTag *tag) +{ + // 4J - this was using a try/finally block + ByteArrayOutputStream baos = ByteArrayOutputStream(); + DataOutputStream dos = DataOutputStream(&baos); // 4J - was new GZIPOutputStream as well + NbtIo::write(tag, &dos); + + byteArray ret(baos.buf.length); + System::arraycopy(baos.buf,0,&ret,0,baos.buf.length); + dos.close(); + return ret; +} + +CompoundTag *NbtIo::read(DataInput *dis) +{ + Tag *tag = Tag::readNamedTag(dis); + + if( tag->getId() == Tag::TAG_Compound ) return (CompoundTag *)tag; + + if(tag!=NULL) delete tag; + // Root tag must be a named compound tag + return NULL; +} + +void NbtIo::write(CompoundTag *tag, DataOutput *dos) +{ + Tag::writeNamedTag(tag, dos); +} \ No newline at end of file diff --git a/Minecraft.World/NbtIo.h b/Minecraft.World/NbtIo.h new file mode 100644 index 00000000..c389afc4 --- /dev/null +++ b/Minecraft.World/NbtIo.h @@ -0,0 +1,17 @@ +#pragma once +#include "CompoundTag.h" +#include "Tag.h" +#include "CompoundTag.h" + +class InputStream; + +class NbtIo +{ +public: + static CompoundTag *readCompressed(InputStream *in); + static void writeCompressed(CompoundTag *tag, OutputStream *out); + static CompoundTag *decompress(byteArray buffer); + static byteArray compress(CompoundTag *tag); + static CompoundTag *read(DataInput *dis); + static void write(CompoundTag *tag, DataOutput *dos); +}; diff --git a/Minecraft.World/NbtSlotFile.cpp b/Minecraft.World/NbtSlotFile.cpp new file mode 100644 index 00000000..81880cfb --- /dev/null +++ b/Minecraft.World/NbtSlotFile.cpp @@ -0,0 +1,250 @@ +#include "stdafx.h" +#include "File.h" +#include "NbtSlotFile.h" + + +byteArray NbtSlotFile::READ_BUFFER(1024*1024); +__int64 NbtSlotFile::largest = 0; + +NbtSlotFile::NbtSlotFile(File file) +{ + totalFileSlots = 0; + fileSlotMapLength = ZonedChunkStorage::CHUNKS_PER_ZONE * ZonedChunkStorage::CHUNKS_PER_ZONE; + fileSlotMap = new vector *[fileSlotMapLength]; + + if ( !file.exists() || file.length() ) + { + raf = CreateFile(wstringtofilename(file.getPath()), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + writeHeader(); + } + else + { + raf = CreateFile(wstringtofilename(file.getPath()), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + + readHeader(); + + for (int i = 0; i < fileSlotMapLength; i++) + { + fileSlotMap[i] = new vector; + } + + DWORD numberofBytesRead; + for (int fileSlot = 0; fileSlot < totalFileSlots; fileSlot++) + { + seekSlotHeader(fileSlot); + short slot; + ReadFile(raf,&slot,2,&numberofBytesRead,NULL); + if (slot == 0) + { + freeFileSlots.push_back(fileSlot); + } else if (slot < 0) + { + fileSlotMap[(-slot) - 1]->push_back(fileSlot); + } else { + fileSlotMap[slot - 1]->push_back(fileSlot); + } + } +} + +void NbtSlotFile::readHeader() +{ + DWORD numberOfBytesRead; + SetFilePointer(raf,0,0,FILE_BEGIN); + int magic; + ReadFile(raf,&magic,4,&numberOfBytesRead,NULL); +// if (magic != MAGIC_NUMBER) throw new IOException("Bad magic number: " + magic); // 4J - TODO + short version; + ReadFile(raf,&version,2,&numberOfBytesRead,NULL); +// if (version != 0) throw new IOException("Bad version number: " + version); // 4J - TODO + ReadFile(raf,&totalFileSlots,4,&numberOfBytesRead,NULL); +} + +void NbtSlotFile::writeHeader() +{ + DWORD numberOfBytesWritten; + short version = 0; + SetFilePointer(raf,0,0,FILE_BEGIN); + WriteFile(raf,&MAGIC_NUMBER,4,&numberOfBytesWritten,NULL); + WriteFile(raf,&version,2,&numberOfBytesWritten,NULL); + WriteFile(raf,&totalFileSlots,4,&numberOfBytesWritten,NULL); +} + +void NbtSlotFile::seekSlotHeader(int fileSlot) +{ + int target = FILE_HEADER_SIZE + fileSlot * (FILE_SLOT_SIZE + FILE_SLOT_HEADER_SIZE); + SetFilePointer(raf,target,0,FILE_BEGIN); +} + +void NbtSlotFile::seekSlot(int fileSlot) +{ + int target = FILE_HEADER_SIZE + fileSlot * (FILE_SLOT_SIZE + FILE_SLOT_HEADER_SIZE); + SetFilePointer(raf,target+FILE_SLOT_HEADER_SIZE,0,FILE_BEGIN); +} + +vector *NbtSlotFile::readAll(int slot) +{ + DWORD numberOfBytesRead; + vector *tags = new vector; + vector *fileSlots = fileSlotMap[slot]; + int skipped = 0; + + AUTO_VAR(itEnd, fileSlots->end()); + for (AUTO_VAR(it, fileSlots->begin()); it != itEnd; it++) + { + int c = *it; //fileSlots->at(i); + + int pos = 0; + int continuesAt = -1; + int expectedSlot = slot + 1; + do + { + seekSlotHeader(c); + short oldSlot; + ReadFile(raf,&oldSlot,2,&numberOfBytesRead,NULL); + short size; + ReadFile(raf,&size,2,&numberOfBytesRead,NULL); + ReadFile(raf,&continuesAt,4,&numberOfBytesRead,NULL); + int lastSlot; + ReadFile(raf,&lastSlot,4,&numberOfBytesRead,NULL); + + seekSlot(c); + if (expectedSlot > 0 && oldSlot == -expectedSlot) + { + skipped++; + goto fileSlotLoop; // 4J - used to be continue fileSlotLoop, with for loop labelled as fileSlotLoop + } + +// if (oldSlot != expectedSlot) throw new IOException("Wrong slot! Got " + oldSlot + ", expected " + expectedSlot); // 4J - TODO + + ReadFile(raf,READ_BUFFER.data + pos,size,&numberOfBytesRead,NULL); + + if (continuesAt >= 0) + { + pos += size; + c = continuesAt; + expectedSlot = -slot - 1; + } + } while (continuesAt >= 0); + tags->push_back(NbtIo::decompress(READ_BUFFER)); +fileSlotLoop: + continue; + } + + return tags; + +} + +int NbtSlotFile::getFreeSlot() +{ int fileSlot; + +// 4J - removed - don't see how toReplace can ever have anything in here, and might not be initialised +// if (toReplace->size() > 0) +// { +// fileSlot = toReplace->back(); +// toReplace->pop_back(); +// } else + + if (freeFileSlots.size() > 0) + { + fileSlot = freeFileSlots.back(); + freeFileSlots.pop_back(); + } + else + { + fileSlot = totalFileSlots++; + writeHeader(); + } + + return fileSlot; + +} +void NbtSlotFile::replaceSlot(int slot, vector *tags) +{ + DWORD numberOfBytesWritten; + toReplace = fileSlotMap[slot]; + fileSlotMap[slot] = new vector(); + + AUTO_VAR(itEndTags, tags->end()); + for (AUTO_VAR(it, tags->begin()); it != itEndTags; it++) + { + CompoundTag *tag = *it; //tags->at(i); + byteArray compressed = NbtIo::compress(tag); + if (compressed.length > largest) + { + wchar_t buf[256]; + largest = compressed.length; +#ifndef _CONTENT_PACKAGE + swprintf(buf, 256, L"New largest: %I64d (%ls)\n",largest,tag->getString(L"id").c_str() ); + OutputDebugStringW(buf); +#endif + } + + int pos = 0; + int remaining = compressed.length; + if (remaining == 0) continue; + + int nextFileSlot = getFreeSlot(); + short currentSlot = slot + 1; + int lastFileSlot = -1; + + while (remaining > 0) + { + int fileSlot = nextFileSlot; + fileSlotMap[slot]->push_back(fileSlot); + + short toWrite = remaining; + if (toWrite > FILE_SLOT_SIZE) + { + toWrite = FILE_SLOT_SIZE; + } + + remaining -= toWrite; + if (remaining > 0) + { + nextFileSlot = getFreeSlot(); + } + else + { + nextFileSlot = -1; + } + + seekSlotHeader(fileSlot); + WriteFile(raf,¤tSlot,2,&numberOfBytesWritten,NULL); + WriteFile(raf,&toWrite,2,&numberOfBytesWritten,NULL); + WriteFile(raf,&nextFileSlot,4,&numberOfBytesWritten,NULL); + WriteFile(raf,&lastFileSlot,4,&numberOfBytesWritten,NULL); + + seekSlot(fileSlot); + WriteFile(raf,compressed.data+pos,toWrite,&numberOfBytesWritten,NULL); + + if (remaining > 0) + { + lastFileSlot = fileSlot; + pos += toWrite; + currentSlot = -slot - 1; + } + } + delete[] compressed.data; + } + + AUTO_VAR(itEndToRep, toReplace->end()); + for (AUTO_VAR(it, toReplace->begin()); it != itEndToRep; it++) + { + int c = *it; //toReplace->at(i); + + freeFileSlots.push_back(c); + + seekSlotHeader(c); + short zero = 0; + WriteFile(raf,&zero,2,&numberOfBytesWritten,NULL); + } + + toReplace->clear(); + +} + +void NbtSlotFile::close() +{ + CloseHandle(raf); +} diff --git a/Minecraft.World/NbtSlotFile.h b/Minecraft.World/NbtSlotFile.h new file mode 100644 index 00000000..93b4fe57 --- /dev/null +++ b/Minecraft.World/NbtSlotFile.h @@ -0,0 +1,44 @@ +#pragma once +#include "CompoundTag.h" +#include "ZonedChunkStorage.h" +#include "com.mojang.nbt.h" + +class NbtSlotFile +{ +private: + static byteArray READ_BUFFER; + + static const int FILE_HEADER_SIZE = 1024; + static const int MAGIC_NUMBER = 0x13737001; + + static const int FILE_SLOT_HEADER_SIZE = 12; + static const int FILE_SLOT_SIZE = 500; + + HANDLE raf; + vector **fileSlotMap; + int fileSlotMapLength; + vector freeFileSlots; + int totalFileSlots; + static __int64 largest; + +public: + NbtSlotFile(File file); + +private: + void readHeader(); + void writeHeader(); + void seekSlotHeader(int fileSlot); + void seekSlot(int fileSlot); + +public: + vector *readAll(int slot); + +private: + vector *toReplace; + + int getFreeSlot(); + +public: + void replaceSlot(int slot, vector *tags); + void close(); +}; diff --git a/Minecraft.World/NearestAttackableTargetGoal.cpp b/Minecraft.World/NearestAttackableTargetGoal.cpp new file mode 100644 index 00000000..5ed9f4f1 --- /dev/null +++ b/Minecraft.World/NearestAttackableTargetGoal.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "NearestAttackableTargetGoal.h" + +NearestAttackableTargetGoal::DistComp::DistComp(Entity *source) +{ + this->source = source; +} + +bool NearestAttackableTargetGoal::DistComp::operator() (shared_ptr e1, shared_ptr e2) +{ + // Should return true if e1 comes before e2 in the sorted list + double distSqr1 = source->distanceToSqr(e1); + double distSqr2 = source->distanceToSqr(e2); + if (distSqr1 < distSqr2) return true; + if (distSqr1 > distSqr2) return false; + return true; +} + +NearestAttackableTargetGoal::NearestAttackableTargetGoal(Mob *mob, const type_info& targetType, float within, int randomInterval, bool mustSee, bool mustReach /*= false*/) : TargetGoal(mob, within, mustSee, mustReach), targetType(targetType) +{ + //this->targetType = targetType; + this->within = within; + this->randomInterval = randomInterval; + this->distComp = new DistComp(mob); + setRequiredControlFlags(TargetGoal::TargetFlag); +} + +NearestAttackableTargetGoal::~NearestAttackableTargetGoal() +{ + delete distComp; +} + +bool NearestAttackableTargetGoal::canUse() +{ + if (randomInterval > 0 && mob->getRandom()->nextInt(randomInterval) != 0) return false; + if (targetType == typeid(Player)) + { + shared_ptr potentialTarget = mob->level->getNearestAttackablePlayer(mob->shared_from_this(), within); + if (canAttack(potentialTarget, false)) + { + target = weak_ptr(potentialTarget); + return true; + } + } + else + { + vector > *entities = mob->level->getEntitiesOfClass(targetType, mob->bb->grow(within, 4, within)); + //Collections.sort(entities, distComp); + std::sort(entities->begin(), entities->end(), *distComp); + for(AUTO_VAR(it, entities->begin()); it != entities->end(); ++it) + { + shared_ptr potTarget = dynamic_pointer_cast(*it); + if (canAttack(potTarget, false)) + { + target = weak_ptr(potTarget); + return true; + } + } + delete entities; + } + return false; +} + +void NearestAttackableTargetGoal::start() +{ + mob->setTarget(target.lock()); + TargetGoal::start(); +} \ No newline at end of file diff --git a/Minecraft.World/NearestAttackableTargetGoal.h b/Minecraft.World/NearestAttackableTargetGoal.h new file mode 100644 index 00000000..a10e9264 --- /dev/null +++ b/Minecraft.World/NearestAttackableTargetGoal.h @@ -0,0 +1,36 @@ +#pragma once + +#include "TargetGoal.h" + +class NearestAttackableTargetGoal : public TargetGoal +{ +public: + class DistComp + { + private: + Entity *source; + + public: + DistComp(Entity *source); + + bool operator() (shared_ptr e1, shared_ptr e2); + }; + +private: + weak_ptr target; + const type_info& targetType; + int randomInterval; + DistComp *distComp; + +public: + //public NearestAttackableTargetGoal(Mob mob, const type_info& targetType, float within, int randomInterval, bool mustSee) + //{ + // this(mob, targetType, within, randomInterval, mustSee, false); + //} + + NearestAttackableTargetGoal(Mob *mob, const type_info& targetType, float within, int randomInterval, bool mustSee, bool mustReach = false); + virtual ~NearestAttackableTargetGoal(); + + virtual bool canUse(); + void start(); +}; \ No newline at end of file diff --git a/Minecraft.World/NetherBridgeFeature.cpp b/Minecraft.World/NetherBridgeFeature.cpp new file mode 100644 index 00000000..b8308af7 --- /dev/null +++ b/Minecraft.World/NetherBridgeFeature.cpp @@ -0,0 +1,119 @@ +#include "stdafx.h" +#include "Biome.h" +#include "NetherBridgeFeature.h" +#include "NetherBridgePieces.h" +#include "MobSpawner.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.dimension.h" + + +NetherBridgeFeature::NetherBridgeFeature() : StructureFeature() +{ + bridgeEnemies.push_back(new Biome::MobSpawnerData(eTYPE_BLAZE, 10, 2, 3)); + bridgeEnemies.push_back(new Biome::MobSpawnerData(eTYPE_PIGZOMBIE, 10, 4, 4)); + bridgeEnemies.push_back(new Biome::MobSpawnerData(eTYPE_LAVASLIME, 3, 4, 4)); + isSpotSelected=false; + netherFortressPos = NULL; + +} + +NetherBridgeFeature::~NetherBridgeFeature() +{ + if( netherFortressPos != NULL ) delete netherFortressPos; +} + +vector *NetherBridgeFeature::getBridgeEnemies() +{ + return &bridgeEnemies; +} + +bool NetherBridgeFeature::isFeatureChunk(int x, int z, bool bIsSuperflat) +{ + // 4J Stu - New implementation to force a nether fortress + if (!isSpotSelected) + { + // Set the random + random->setSeed(level->getSeed()); + random->nextInt(); + + // Due to our nether size we want to accept chunks in the range [(-3,-3),(3,3)] (7x7). This is 49 possible chunks that should give + // the fortress enough room to grow within our limited nether + int chunk = random->nextInt(49); + + int xCoord = chunk % 7; + int zCoord = chunk / 7; + + netherFortressPos = new ChunkPos(xCoord, zCoord); + + isSpotSelected = true; + } + + bool forcePlacement = false; + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + if( levelGenOptions != NULL ) + { + forcePlacement = levelGenOptions->isFeatureChunk(x,z,eFeature_NetherBridge); + } + + if(forcePlacement || (x == netherFortressPos->x && z == netherFortressPos->z) ) return true; + +#ifdef _LARGE_WORLDS + int xzSize = level->dimension->getXZSize(); + if(xzSize > 30) + { + // For large worlds, lets allow the PC version of the spawning to place nether fortresses (plus the one we forced above) + int cx = x >> 4; + int cz = z >> 4; + + random->setSeed(cx ^ (cz << 4) ^ level->getSeed()); + random->nextInt(); + + if (random->nextInt(3) != 0) + { + return false; + } + if (x != ((cx << 4) + 4 + random->nextInt(8))) + { + return false; + } + if (z != ((cz << 4) + 4 + random->nextInt(8))) + { + return false; + } + return true; + } +#endif + + return false; +} + +StructureStart *NetherBridgeFeature::createStructureStart(int x, int z) +{ + return new NetherBridgeStart(level, random, x, z); +} + +void NetherBridgeFeature::clearCachedBuildings() +{ + cachedStructures.clear(); +} + +NetherBridgeFeature::NetherBridgeStart::NetherBridgeStart(Level *level, Random *random, int chunkX, int chunkZ) : StructureStart() +{ + NetherBridgePieces::StartPiece *start = new NetherBridgePieces::StartPiece(random, (chunkX << 4) + 2, (chunkZ << 4) + 2, level); + pieces.push_back(start); + start->addChildren(start, &pieces, random); + + vector *pendingChildren = &start->pendingChildren; + while (!pendingChildren->empty()) + { + int pos = random->nextInt((int)pendingChildren->size()); + AUTO_VAR(it, pendingChildren->begin() + pos); + StructurePiece *structurePiece = *it; + pendingChildren->erase(it); + structurePiece->addChildren(start, &pieces, random); + } + + calculateBoundingBox(); + moveInsideHeights(level, random, 48, 70); +} diff --git a/Minecraft.World/NetherBridgeFeature.h b/Minecraft.World/NetherBridgeFeature.h new file mode 100644 index 00000000..cd7f315f --- /dev/null +++ b/Minecraft.World/NetherBridgeFeature.h @@ -0,0 +1,30 @@ +#pragma once +#include "StructureFeature.h" +#include "StructureStart.h" +#include "biome.h" +class Random; + +class NetherBridgeFeature : public StructureFeature +{ + +private: + vector bridgeEnemies; + bool isSpotSelected; + ChunkPos *netherFortressPos; + +public: + NetherBridgeFeature(); + ~NetherBridgeFeature(); + vector *getBridgeEnemies(); +protected: + virtual bool isFeatureChunk(int x, int z, bool bIsSuperflat); + virtual StructureStart *createStructureStart(int x, int z); +public: + void clearCachedBuildings(); +private: + class NetherBridgeStart : public StructureStart + { + public: + NetherBridgeStart(Level *level, Random *random, int chunkX, int chunkZ); + }; +}; diff --git a/Minecraft.World/NetherBridgePieces.cpp b/Minecraft.World/NetherBridgePieces.cpp new file mode 100644 index 00000000..9a795e78 --- /dev/null +++ b/Minecraft.World/NetherBridgePieces.cpp @@ -0,0 +1,1453 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.level.levelgen.h" +#include "net.minecraft.world.level.storage.h" +#include "NetherBridgePieces.h" +#include "Direction.h" + +NetherBridgePieces::PieceWeight::PieceWeight(EPieceClass pieceClass, int weight, int maxPlaceCount, bool allowInRow) : weight(weight) +{ + this->placeCount = 0; + this->pieceClass = pieceClass; + this->maxPlaceCount = maxPlaceCount; + this->allowInRow = allowInRow; +} + +NetherBridgePieces::PieceWeight::PieceWeight(EPieceClass pieceClass, int weight, int maxPlaceCount) : weight(weight) +{ + this->placeCount = 0; + this->pieceClass = pieceClass; + this->maxPlaceCount = maxPlaceCount; + this->allowInRow = false; +} + +bool NetherBridgePieces::PieceWeight::doPlace(int depth) +{ + return maxPlaceCount == 0 || placeCount < maxPlaceCount; +} + +bool NetherBridgePieces::PieceWeight::isValid() +{ + return maxPlaceCount == 0 || placeCount < maxPlaceCount; +} + +NetherBridgePieces::PieceWeight *NetherBridgePieces::bridgePieceWeights[NetherBridgePieces::BRIDGE_PIECEWEIGHTS_COUNT] = +{ + new PieceWeight(EPieceClass_BridgeStraight, 30, 0, true), + new PieceWeight(EPieceClass_BridgeCrossing, 10, 4), + new PieceWeight(EPieceClass_MonsterThrone, 15, 2), // 4J Stu - Increased weight to ensure that we have these (was 5), required for Blazes and therefore required for brewing + new PieceWeight(EPieceClass_CastleEntrance, 15, 1), // 4J Stu - Increased weight to ensure that we have these (was 5), required for CastleStalkRoom, and therefore required for brewing + new PieceWeight(EPieceClass_RoomCrossing, 10, 4), + new PieceWeight(EPieceClass_StairsRoom, 10, 3), +}; + +NetherBridgePieces::PieceWeight *NetherBridgePieces::castlePieceWeights[NetherBridgePieces::CASTLE_PIECEWEIGHTS_COUNT] = +{ + new PieceWeight(EPieceClass_CastleStalkRoom, 30, 2), // 4J Stu - Increased weight to ensure that we have these (was 5), required for Nether Wart, and therefore required for brewing + new PieceWeight(EPieceClass_CastleSmallCorridorPiece, 25, 0, true), + new PieceWeight(EPieceClass_CastleSmallCorridorCrossingPiece, 15, 5), + new PieceWeight(EPieceClass_CastleSmallCorridorRightTurnPiece, 5, 10), + new PieceWeight(EPieceClass_CastleSmallCorridorLeftTurnPiece, 5, 10), + new PieceWeight(EPieceClass_CastleCorridorStairsPiece, 10, 3, true), + new PieceWeight(EPieceClass_CastleCorridorTBalconyPiece, 7, 2), +}; + +NetherBridgePieces::NetherBridgePiece *NetherBridgePieces::findAndCreateBridgePieceFactory(NetherBridgePieces::PieceWeight *piece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth) +{ + EPieceClass pieceClass = piece->pieceClass; + NetherBridgePiece *structurePiece = NULL; + + if (pieceClass == EPieceClass_BridgeStraight) + { + structurePiece = BridgeStraight::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_BridgeCrossing) + { + structurePiece = BridgeCrossing::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_RoomCrossing) + { + structurePiece = RoomCrossing::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_StairsRoom) + { + structurePiece = StairsRoom::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_MonsterThrone) + { + structurePiece = MonsterThrone::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_CastleEntrance) + { + structurePiece = CastleEntrance::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_CastleSmallCorridorPiece) + { + structurePiece = CastleSmallCorridorPiece::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_CastleSmallCorridorRightTurnPiece) + { + structurePiece = CastleSmallCorridorRightTurnPiece::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_CastleSmallCorridorLeftTurnPiece) + { + structurePiece = CastleSmallCorridorLeftTurnPiece::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_CastleCorridorStairsPiece) + { + structurePiece = CastleCorridorStairsPiece::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_CastleCorridorTBalconyPiece) + { + structurePiece = CastleCorridorTBalconyPiece::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_CastleSmallCorridorCrossingPiece) + { + structurePiece = CastleSmallCorridorCrossingPiece::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_CastleStalkRoom) + { + structurePiece = CastleStalkRoom::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + return structurePiece; +} + +NetherBridgePieces::NetherBridgePiece::NetherBridgePiece(int genDepth) : StructurePiece(genDepth) +{ +} + +int NetherBridgePieces::NetherBridgePiece::updatePieceWeight(list *currentPieces) +{ + bool hasAnyPieces = false; + int totalWeight = 0; + for( AUTO_VAR(it, currentPieces->begin()); it != currentPieces->end(); it++ ) + { + PieceWeight *piece = *it; + + if (piece->maxPlaceCount > 0 && piece->placeCount < piece->maxPlaceCount) + { + hasAnyPieces = true; + } + totalWeight += piece->weight; + } + return (hasAnyPieces ? totalWeight : -1); +} + +NetherBridgePieces::NetherBridgePiece *NetherBridgePieces::NetherBridgePiece::generatePiece(StartPiece *startPiece, list *currentPieces, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth) +{ + int totalWeight = updatePieceWeight(currentPieces); + boolean doStuff = totalWeight > 0 && depth <= MAX_DEPTH; + + int numAttempts = 0; + while (numAttempts < 5 && doStuff) + { + numAttempts++; + + int weightSelection = random->nextInt(totalWeight); + for( AUTO_VAR(it, currentPieces->begin()); it != currentPieces->end(); it++ ) + { + PieceWeight *piece = *it; + weightSelection -= piece->weight; + if (weightSelection < 0) + { + if (!piece->doPlace(depth) || (piece == startPiece->previousPiece && !piece->allowInRow)) + { + break; + } + + NetherBridgePiece *structurePiece = findAndCreateBridgePieceFactory(piece, pieces, random, footX, footY, footZ, direction, depth); + if (structurePiece != NULL) + { + piece->placeCount++; + startPiece->previousPiece = piece; + + if (!piece->isValid()) + { + currentPieces->remove(piece); + } + return structurePiece; + } + } + } + } + { + return BridgeEndFiller::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + +} + +StructurePiece *NetherBridgePieces::NetherBridgePiece::generateAndAddPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth, bool isCastle) +{ + if (abs(footX - startPiece->getBoundingBox()->x0) > 7 * 16 || abs(footZ - startPiece->getBoundingBox()->z0) > 7 * 16) + { + return BridgeEndFiller::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + list *availablePieces = &startPiece->availableBridgePieces; + if (isCastle) + { + availablePieces = &startPiece->availableCastlePieces; + } + StructurePiece *newPiece = generatePiece(startPiece, availablePieces, pieces, random, footX, footY, footZ, direction, depth + 1); + if (newPiece != NULL) + { + pieces->push_back(newPiece); + startPiece->pendingChildren.push_back(newPiece); + } + return newPiece; +} + +StructurePiece *NetherBridgePieces::NetherBridgePiece::generateChildForward(StartPiece *startPiece, list *pieces, Random *random, int xOff, int yOff, bool isCastle) +{ + switch (orientation) + { + case Direction::NORTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + xOff, boundingBox->y0 + yOff, boundingBox->z0 - 1, orientation, getGenDepth(), isCastle); + case Direction::SOUTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + xOff, boundingBox->y0 + yOff, boundingBox->z1 + 1, orientation, getGenDepth(), isCastle); + case Direction::WEST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + yOff, boundingBox->z0 + xOff, orientation, getGenDepth(), isCastle); + case Direction::EAST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + yOff, boundingBox->z0 + xOff, orientation, getGenDepth(), isCastle); + } + return NULL; +} + +StructurePiece *NetherBridgePieces::NetherBridgePiece::generateChildLeft(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff, bool isCastle) +{ + switch (orientation) + { + case Direction::NORTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::WEST, getGenDepth(), isCastle); + case Direction::SOUTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::WEST, getGenDepth(), isCastle); + case Direction::WEST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z0 - 1, Direction::NORTH, getGenDepth(), isCastle); + case Direction::EAST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z0 - 1, Direction::NORTH, getGenDepth(), isCastle); + } + return NULL; +} + +StructurePiece *NetherBridgePieces::NetherBridgePiece::generateChildRight(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff, bool isCastle) +{ + switch (orientation) + { + case Direction::NORTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::EAST, getGenDepth(), isCastle); + case Direction::SOUTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::EAST, getGenDepth(), isCastle); + case Direction::WEST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z1 + 1, Direction::SOUTH, getGenDepth(), isCastle); + case Direction::EAST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z1 + 1, Direction::SOUTH, getGenDepth(), isCastle); + } + return NULL; +} + +bool NetherBridgePieces::NetherBridgePiece::isOkBox(BoundingBox *box, StartPiece *startPiece) +{ + bool bIsOk = false; + + if(box != NULL) + { + if( box->y0 > LOWEST_Y_POSITION ) bIsOk = true; + int xzSize = (startPiece->m_level->getLevelData()->getXZSize() / startPiece->m_level->getLevelData()->getHellScale()); //HellRandomLevelSource::XZSIZE; + int blockMin = -( (xzSize << 4) / 2) + 1; + int blockMax = ( (xzSize << 4) / 2 ) - 1; + + if(box->x0 <= blockMin) bIsOk = false; + if(box->z0 <= blockMin) bIsOk = false; + if(box->x1 >= blockMax) bIsOk = false; + if(box->z1 >= blockMax) bIsOk = false; + } + + return bIsOk; +} + +void NetherBridgePieces::NetherBridgePiece::generateLightPost(Level *level, Random *random, BoundingBox *chunkBB, int x, int y, int z, int xOff, int zOff) +{ + int worldX = getWorldX(x, z); + int worldY = getWorldY(y); + int worldZ = getWorldZ(x, z); + + if (!chunkBB->isInside(worldX, worldY, worldZ)) + { + return; + } + + if (level->isEmptyTile(worldX, worldY, worldZ) && level->isEmptyTile(worldX, worldY + 1, worldZ) && level->isEmptyTile(worldX, worldY + 2, worldZ) + && level->isEmptyTile(worldX, worldY + 3, worldZ)) + { + level->setTileAndDataNoUpdate(worldX, worldY, worldZ, Tile::netherFence_Id, 0); + level->setTileAndDataNoUpdate(worldX, worldY + 1, worldZ, Tile::netherFence_Id, 0); + level->setTileAndDataNoUpdate(worldX, worldY + 2, worldZ, Tile::netherFence_Id, 0); + level->setTileAndDataNoUpdate(worldX, worldY + 3, worldZ, Tile::netherFence_Id, 0); + placeBlock(level, Tile::netherFence_Id, 0, x + xOff, y + 3, z + zOff, chunkBB); + placeBlock(level, Tile::lightGem_Id, 0, x + xOff, y + 2, z + zOff, chunkBB); + } +} + +void NetherBridgePieces::NetherBridgePiece::generateLightPostFacingRight(Level *level, Random *random, BoundingBox *chunkBB, int x, int y, int z) +{ + generateLightPost(level, random, chunkBB, x, y, z, 1, 0); +} + +void NetherBridgePieces::NetherBridgePiece::generateLightPostFacingLeft(Level *level, Random *random, BoundingBox *chunkBB, int x, int y, int z) +{ + generateLightPost(level, random, chunkBB, x, y, z, -1, 0); +} + +void NetherBridgePieces::NetherBridgePiece::generateLightPostFacingUp(Level *level, Random *random, BoundingBox *chunkBB, int x, int y, int z) +{ + generateLightPost(level, random, chunkBB, x, y, z, 0, 1); +} + +void NetherBridgePieces::NetherBridgePiece::generateLightPostFacingDown(Level *level, Random *random, BoundingBox *chunkBB, int x, int y, int z) +{ + generateLightPost(level, random, chunkBB, x, y, z, 0, -1); +} + + +NetherBridgePieces::BridgeStraight::BridgeStraight(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void NetherBridgePieces::BridgeStraight::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateChildForward((StartPiece *) startPiece, pieces, random, 1, 3, false); +} + +NetherBridgePieces::BridgeStraight *NetherBridgePieces::BridgeStraight::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -3, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new BridgeStraight(genDepth, random, box, direction); +} + +bool NetherBridgePieces::BridgeStraight::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // floor + generateBox(level, chunkBB, 0, 3, 0, width - 1, 4, depth - 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // room air + generateBox(level, chunkBB, 1, 5, 0, 3, 7, depth - 1, 0, 0, false); + + // hand rails + generateBox(level, chunkBB, 0, 5, 0, 0, 5, depth - 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 4, 5, 0, 4, 5, depth - 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // supports + generateBox(level, chunkBB, 0, 2, 0, 4, 2, 5, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 2, 13, 4, 2, 18, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 0, 0, 4, 1, 3, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 0, 15, 4, 1, 18, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + for (int x = 0; x <= 4; x++) + { + for (int z = 0; z <= 2; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, 18 - z, chunkBB); + } + } + + generateBox(level, chunkBB, 0, 1, 1, 0, 4, 1, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 0, 3, 4, 0, 4, 4, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 0, 3, 14, 0, 4, 14, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 0, 1, 17, 0, 4, 17, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 4, 1, 1, 4, 4, 1, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 4, 3, 4, 4, 4, 4, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 4, 3, 14, 4, 4, 14, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 4, 1, 17, 4, 4, 17, Tile::netherFence_Id, Tile::netherFence_Id, false); + + return true; +} + +NetherBridgePieces::BridgeEndFiller::BridgeEndFiller(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = stairsBox; + selfSeed = random->nextInt(); +} + +NetherBridgePieces::BridgeEndFiller *NetherBridgePieces::BridgeEndFiller::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -3, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new BridgeEndFiller(genDepth, random, box, direction); +} + +bool NetherBridgePieces::BridgeEndFiller::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + Random *selfRandom = new Random(selfSeed); + + // floor + for (int x = 0; x <= 4; x++) + { + for (int y = 3; y <= 4; y++) + { + int z = selfRandom->nextInt(8); + generateBox(level, chunkBB, x, y, 0, x, y, z, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + } + } + + // hand rails + { + int z = selfRandom->nextInt(8); + generateBox(level, chunkBB, 0, 5, 0, 0, 5, z, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + } + { + int z = selfRandom->nextInt(8); + generateBox(level, chunkBB, 4, 5, 0, 4, 5, z, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + } + + // supports + for (int x = 0; x <= 4; x++) + { + int z = selfRandom->nextInt(5); + generateBox(level, chunkBB, x, 2, 0, x, 2, z, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + } + for (int x = 0; x <= 4; x++) + { + for (int y = 0; y <= 1; y++) + { + int z = selfRandom->nextInt(3); + generateBox(level, chunkBB, x, y, 0, x, y, z, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + } + } + + delete selfRandom; + + return true; +} + +NetherBridgePieces::BridgeCrossing::BridgeCrossing(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = stairsBox; +} + +NetherBridgePieces::BridgeCrossing::BridgeCrossing(Random *random, int west, int north) : NetherBridgePiece(0) +{ + orientation = random->nextInt(4); + + switch (orientation) + { + case Direction::NORTH: + case Direction::SOUTH: + boundingBox = new BoundingBox(west, 64, north, west + width - 1, 64 + height - 1, north + depth - 1); + break; + default: + boundingBox = new BoundingBox(west, 64, north, west + depth - 1, 64 + height - 1, north + width - 1); + break; + } +} + +void NetherBridgePieces::BridgeCrossing::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateChildForward((StartPiece *) startPiece, pieces, random, 8, 3, false); + generateChildLeft((StartPiece *) startPiece, pieces, random, 3, 8, false); + generateChildRight((StartPiece *) startPiece, pieces, random, 3, 8, false); +} + +NetherBridgePieces::BridgeCrossing *NetherBridgePieces::BridgeCrossing::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -8, -3, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new BridgeCrossing(genDepth, random, box, direction); +} + +bool NetherBridgePieces::BridgeCrossing::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // floor + generateBox(level, chunkBB, 7, 3, 0, 11, 4, 18, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 3, 7, 18, 4, 11, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // room air + generateBox(level, chunkBB, 8, 5, 0, 10, 7, 18, 0, 0, false); + generateBox(level, chunkBB, 0, 5, 8, 18, 7, 10, 0, 0, false); + // hand rails + generateBox(level, chunkBB, 7, 5, 0, 7, 5, 7, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 7, 5, 11, 7, 5, 18, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 11, 5, 0, 11, 5, 7, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 11, 5, 11, 11, 5, 18, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 5, 7, 7, 5, 7, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 11, 5, 7, 18, 5, 7, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 5, 11, 7, 5, 11, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 11, 5, 11, 18, 5, 11, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // supports + generateBox(level, chunkBB, 7, 2, 0, 11, 2, 5, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 7, 2, 13, 11, 2, 18, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 7, 0, 0, 11, 1, 3, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 7, 0, 15, 11, 1, 18, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + for (int x = 7; x <= 11; x++) + { + for (int z = 0; z <= 2; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, 18 - z, chunkBB); + } + } + + generateBox(level, chunkBB, 0, 2, 7, 5, 2, 11, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 13, 2, 7, 18, 2, 11, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 0, 7, 3, 1, 11, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 15, 0, 7, 18, 1, 11, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + for (int x = 0; x <= 2; x++) + { + for (int z = 7; z <= 11; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + fillColumnDown(level, Tile::netherBrick_Id, 0, 18 - x, -1, z, chunkBB); + } + } + + return true; +} + + +NetherBridgePieces::StartPiece::StartPiece(Random *random, int west, int north, Level *level) : BridgeCrossing(random, west, north) +{ + isLibraryAdded = false; + previousPiece = NULL; + m_level = level; + + for( int i = 0; i < BRIDGE_PIECEWEIGHTS_COUNT; i++ ) + { + PieceWeight *piece = bridgePieceWeights[i]; + piece->placeCount = 0; + availableBridgePieces.push_back(piece); + } + + for( int i = 0; i < CASTLE_PIECEWEIGHTS_COUNT; i++ ) + { + PieceWeight *piece = castlePieceWeights[i]; + + piece->placeCount = 0; + availableCastlePieces.push_back(piece); + } +} + + +NetherBridgePieces::RoomCrossing::RoomCrossing(int genDepth, Random *random, BoundingBox *box, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = box; +} + +void NetherBridgePieces::RoomCrossing::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateChildForward((StartPiece *) startPiece, pieces, random, 2, 0, false); + generateChildLeft((StartPiece *) startPiece, pieces, random, 0, 2, false); + generateChildRight((StartPiece *) startPiece, pieces, random, 0, 2, false); +} + +NetherBridgePieces::RoomCrossing *NetherBridgePieces::RoomCrossing::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -2, 0, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new RoomCrossing(genDepth, random, box, direction); +} + +bool NetherBridgePieces::RoomCrossing::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // floor + generateBox(level, chunkBB, 0, 0, 0, width - 1, 1, depth - 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // room air + generateBox(level, chunkBB, 0, 2, 0, 6, 7, 6, 0, 0, false); + + // walls + generateBox(level, chunkBB, 0, 2, 0, 1, 6, 0, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 2, 6, 1, 6, 6, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 5, 2, 0, 6, 6, 0, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 5, 2, 6, 6, 6, 6, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 2, 0, 0, 6, 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 2, 5, 0, 6, 6, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 6, 2, 0, 6, 6, 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 6, 2, 5, 6, 6, 6, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // entries + generateBox(level, chunkBB, 2, 6, 0, 4, 6, 0, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 2, 5, 0, 4, 5, 0, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 2, 6, 6, 4, 6, 6, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 2, 5, 6, 4, 5, 6, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 0, 6, 2, 0, 6, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 5, 2, 0, 5, 4, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 6, 6, 2, 6, 6, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 6, 5, 2, 6, 5, 4, Tile::netherFence_Id, Tile::netherFence_Id, false); + + for (int x = 0; x <= 6; x++) + { + for (int z = 0; z <= 6; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + } + } + + return true; +} + +NetherBridgePieces::StairsRoom::StairsRoom(int genDepth, Random *random, BoundingBox *box, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = box; +} + +void NetherBridgePieces::StairsRoom::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateChildRight((StartPiece *) startPiece, pieces, random, 6, 2, false); +} + +NetherBridgePieces::StairsRoom *NetherBridgePieces::StairsRoom::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -2, 0, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new StairsRoom(genDepth, random, box, direction); +} + +bool NetherBridgePieces::StairsRoom::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // floor + generateBox(level, chunkBB, 0, 0, 0, width - 1, 1, depth - 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // room air + generateBox(level, chunkBB, 0, 2, 0, 6, 10, 6, 0, 0, false); + + // walls + generateBox(level, chunkBB, 0, 2, 0, 1, 8, 0, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 5, 2, 0, 6, 8, 0, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 2, 1, 0, 8, 6, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 6, 2, 1, 6, 8, 6, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 2, 6, 5, 8, 6, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // wall decorations + generateBox(level, chunkBB, 0, 3, 2, 0, 5, 4, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 6, 3, 2, 6, 5, 2, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 6, 3, 4, 6, 5, 4, Tile::netherFence_Id, Tile::netherFence_Id, false); + + // stair + placeBlock(level, Tile::netherBrick_Id, 0, 5, 2, 5, chunkBB); + generateBox(level, chunkBB, 4, 2, 5, 4, 3, 5, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 3, 2, 5, 3, 4, 5, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 2, 2, 5, 2, 5, 5, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 2, 5, 1, 6, 5, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // top floor + generateBox(level, chunkBB, 1, 7, 1, 5, 7, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 6, 8, 2, 6, 8, 4, 0, 0, false); + + // entries + generateBox(level, chunkBB, 2, 6, 0, 4, 8, 0, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 2, 5, 0, 4, 5, 0, Tile::netherFence_Id, Tile::netherFence_Id, false); + + for (int x = 0; x <= 6; x++) + { + for (int z = 0; z <= 6; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + } + } + + return true; + +} + + +NetherBridgePieces::MonsterThrone::MonsterThrone(int genDepth, Random *random, BoundingBox *box, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = box; + hasPlacedMobSpawner = false; +} + +NetherBridgePieces::MonsterThrone *NetherBridgePieces::MonsterThrone::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -2, 0, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new MonsterThrone(genDepth, random, box, direction); +} + +bool NetherBridgePieces::MonsterThrone::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + generateBox(level, chunkBB, 0, 2, 0, 6, 7, 7, 0, 0, false); + + // floors + generateBox(level, chunkBB, 1, 0, 0, 5, 1, 7, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 2, 1, 5, 2, 7, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 3, 2, 5, 3, 7, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 4, 3, 5, 4, 7, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // rails + generateBox(level, chunkBB, 1, 2, 0, 1, 4, 2, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 5, 2, 0, 5, 4, 2, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 5, 2, 1, 5, 3, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 5, 5, 2, 5, 5, 3, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 5, 3, 0, 5, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 6, 5, 3, 6, 5, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 5, 8, 5, 5, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + placeBlock(level, Tile::netherFence_Id, 0, 1, 6, 3, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, 5, 6, 3, chunkBB); + generateBox(level, chunkBB, 0, 6, 3, 0, 6, 8, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 6, 6, 3, 6, 6, 8, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 1, 6, 8, 5, 7, 8, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 2, 8, 8, 4, 8, 8, Tile::netherFence_Id, Tile::netherFence_Id, false); + + + if (!hasPlacedMobSpawner) + { + int y = getWorldY(5), x = getWorldX(3, 5), z = getWorldZ(3, 5); + if (chunkBB->isInside(x, y, z)) + { + hasPlacedMobSpawner = true; + level->setTile(x, y, z, Tile::mobSpawner_Id); + shared_ptr entity = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + if (entity != NULL) entity->setEntityId(L"Blaze"); + } + } + + for (int x = 0; x <= 6; x++) + { + for (int z = 0; z <= 6; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + } + } + + return true; +} + + +NetherBridgePieces::CastleEntrance::CastleEntrance(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void NetherBridgePieces::CastleEntrance::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateChildForward((StartPiece *) startPiece, pieces, random, 5, 3, true); +} + +NetherBridgePieces::CastleEntrance *NetherBridgePieces::CastleEntrance::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -5, -3, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new CastleEntrance(genDepth, random, box, direction); +} + +bool NetherBridgePieces::CastleEntrance::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // floor + generateBox(level, chunkBB, 0, 3, 0, 12, 4, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // room air + generateBox(level, chunkBB, 0, 5, 0, 12, 13, 12, 0, 0, false); + + // walls + generateBox(level, chunkBB, 0, 5, 0, 1, 12, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 11, 5, 0, 12, 12, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 2, 5, 11, 4, 12, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 8, 5, 11, 10, 12, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 5, 9, 11, 7, 12, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 2, 5, 0, 4, 12, 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 8, 5, 0, 10, 12, 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 5, 9, 0, 7, 12, 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // roof + generateBox(level, chunkBB, 2, 11, 2, 10, 12, 10, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // entrance decoration + generateBox(level, chunkBB, 5, 8, 0, 7, 8, 0, Tile::netherFence_Id, Tile::netherFence_Id, false); + + // wall decorations + for (int i = 1; i <= 11; i += 2) + { + generateBox(level, chunkBB, i, 10, 0, i, 11, 0, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, i, 10, 12, i, 11, 12, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 0, 10, i, 0, 11, i, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 12, 10, i, 12, 11, i, Tile::netherFence_Id, Tile::netherFence_Id, false); + placeBlock(level, Tile::netherBrick_Id, 0, i, 13, 0, chunkBB); + placeBlock(level, Tile::netherBrick_Id, 0, i, 13, 12, chunkBB); + placeBlock(level, Tile::netherBrick_Id, 0, 0, 13, i, chunkBB); + placeBlock(level, Tile::netherBrick_Id, 0, 12, 13, i, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, i + 1, 13, 0, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, i + 1, 13, 12, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, 0, 13, i + 1, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, 12, 13, i + 1, chunkBB); + } + placeBlock(level, Tile::netherFence_Id, 0, 0, 13, 0, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, 0, 13, 12, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, 0, 13, 0, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, 12, 13, 0, chunkBB); + + // inside decorations + for (int z = 3; z <= 9; z += 2) + { + generateBox(level, chunkBB, 1, 7, z, 1, 8, z, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 11, 7, z, 11, 8, z, Tile::netherFence_Id, Tile::netherFence_Id, false); + } + + // supports + generateBox(level, chunkBB, 4, 2, 0, 8, 2, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 2, 4, 12, 2, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + generateBox(level, chunkBB, 4, 0, 0, 8, 1, 3, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 4, 0, 9, 8, 1, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 0, 4, 3, 1, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 9, 0, 4, 12, 1, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + for (int x = 4; x <= 8; x++) + { + for (int z = 0; z <= 2; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, 12 - z, chunkBB); + } + } + for (int x = 0; x <= 2; x++) + { + for (int z = 4; z <= 8; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + fillColumnDown(level, Tile::netherBrick_Id, 0, 12 - x, -1, z, chunkBB); + } + } + + // lava well + generateBox(level, chunkBB, 5, 5, 5, 7, 5, 7, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 6, 1, 6, 6, 4, 6, 0, 0, false); + placeBlock(level, Tile::netherBrick_Id, 0, 6, 0, 6, chunkBB); + placeBlock(level, Tile::lava_Id, 0, 6, 5, 6, chunkBB); + // tick lava well + int x = getWorldX(6, 6); + int y = getWorldY(5); + int z = getWorldZ(6, 6); + if (chunkBB->isInside(x, y, z)) + { + level->setInstaTick(true); + Tile::tiles[Tile::lava_Id]->tick(level, x, y, z, random); + level->setInstaTick(false); + } + + + return true; +} + + +NetherBridgePieces::CastleStalkRoom::CastleStalkRoom(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void NetherBridgePieces::CastleStalkRoom::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateChildForward((StartPiece *) startPiece, pieces, random, 5, 3, true); + generateChildForward((StartPiece *) startPiece, pieces, random, 5, 11, true); +} + +NetherBridgePieces::CastleStalkRoom *NetherBridgePieces::CastleStalkRoom::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -5, -3, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new CastleStalkRoom(genDepth, random, box, direction); +} + +bool NetherBridgePieces::CastleStalkRoom::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // floor + generateBox(level, chunkBB, 0, 3, 0, 12, 4, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // room air + generateBox(level, chunkBB, 0, 5, 0, 12, 13, 12, 0, 0, false); + + // walls + generateBox(level, chunkBB, 0, 5, 0, 1, 12, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 11, 5, 0, 12, 12, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 2, 5, 11, 4, 12, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 8, 5, 11, 10, 12, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 5, 9, 11, 7, 12, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 2, 5, 0, 4, 12, 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 8, 5, 0, 10, 12, 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 5, 9, 0, 7, 12, 1, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // roof + generateBox(level, chunkBB, 2, 11, 2, 10, 12, 10, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // wall decorations + for (int i = 1; i <= 11; i += 2) + { + generateBox(level, chunkBB, i, 10, 0, i, 11, 0, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, i, 10, 12, i, 11, 12, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 0, 10, i, 0, 11, i, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 12, 10, i, 12, 11, i, Tile::netherFence_Id, Tile::netherFence_Id, false); + placeBlock(level, Tile::netherBrick_Id, 0, i, 13, 0, chunkBB); + placeBlock(level, Tile::netherBrick_Id, 0, i, 13, 12, chunkBB); + placeBlock(level, Tile::netherBrick_Id, 0, 0, 13, i, chunkBB); + placeBlock(level, Tile::netherBrick_Id, 0, 12, 13, i, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, i + 1, 13, 0, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, i + 1, 13, 12, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, 0, 13, i + 1, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, 12, 13, i + 1, chunkBB); + } + placeBlock(level, Tile::netherFence_Id, 0, 0, 13, 0, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, 0, 13, 12, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, 0, 13, 0, chunkBB); + placeBlock(level, Tile::netherFence_Id, 0, 12, 13, 0, chunkBB); + + // inside decorations + for (int z = 3; z <= 9; z += 2) + { + generateBox(level, chunkBB, 1, 7, z, 1, 8, z, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 11, 7, z, 11, 8, z, Tile::netherFence_Id, Tile::netherFence_Id, false); + } + + // inside stair + int stairOrientation = getOrientationData(Tile::stairs_netherBricks_Id, 3); + for (int i = 0; i <= 6; i++) + { + int z = i + 4; + for (int x = 5; x <= 7; x++) + { + placeBlock(level, Tile::stairs_netherBricks_Id, stairOrientation, x, 5 + i, z, chunkBB); + } + if (z >= 5 && z <= 8) + { + generateBox(level, chunkBB, 5, 5, z, 7, i + 4, z, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + } + else if (z >= 9 && z <= 10) + { + generateBox(level, chunkBB, 5, 8, z, 7, i + 4, z, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + } + if (i >= 1) + { + generateBox(level, chunkBB, 5, 6 + i, z, 7, 9 + i, z, 0, 0, false); + } + } + for (int x = 5; x <= 7; x++) + { + placeBlock(level, Tile::stairs_netherBricks_Id, stairOrientation, x, 12, 11, chunkBB); + } + generateBox(level, chunkBB, 5, 6, 7, 5, 7, 7, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 7, 6, 7, 7, 7, 7, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 5, 13, 12, 7, 13, 12, 0, 0, false); + + // farmland catwalks + generateBox(level, chunkBB, 2, 5, 2, 3, 5, 3, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 2, 5, 9, 3, 5, 10, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 2, 5, 4, 2, 5, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 9, 5, 2, 10, 5, 3, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 9, 5, 9, 10, 5, 10, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 10, 5, 4, 10, 5, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + int eastOrientation = getOrientationData(Tile::stairs_netherBricks_Id, 0); + int westOrientation = getOrientationData(Tile::stairs_netherBricks_Id, 1); + placeBlock(level, Tile::stairs_netherBricks_Id, westOrientation, 4, 5, 2, chunkBB); + placeBlock(level, Tile::stairs_netherBricks_Id, westOrientation, 4, 5, 3, chunkBB); + placeBlock(level, Tile::stairs_netherBricks_Id, westOrientation, 4, 5, 9, chunkBB); + placeBlock(level, Tile::stairs_netherBricks_Id, westOrientation, 4, 5, 10, chunkBB); + placeBlock(level, Tile::stairs_netherBricks_Id, eastOrientation, 8, 5, 2, chunkBB); + placeBlock(level, Tile::stairs_netherBricks_Id, eastOrientation, 8, 5, 3, chunkBB); + placeBlock(level, Tile::stairs_netherBricks_Id, eastOrientation, 8, 5, 9, chunkBB); + placeBlock(level, Tile::stairs_netherBricks_Id, eastOrientation, 8, 5, 10, chunkBB); + + // farmlands + generateBox(level, chunkBB, 3, 4, 4, 4, 4, 8, Tile::hellSand_Id, Tile::hellSand_Id, false); + generateBox(level, chunkBB, 8, 4, 4, 9, 4, 8, Tile::hellSand_Id, Tile::hellSand_Id, false); + generateBox(level, chunkBB, 3, 5, 4, 4, 5, 8, Tile::netherStalk_Id, Tile::netherStalk_Id, false); + generateBox(level, chunkBB, 8, 5, 4, 9, 5, 8, Tile::netherStalk_Id, Tile::netherStalk_Id, false); + + // supports + generateBox(level, chunkBB, 4, 2, 0, 8, 2, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 2, 4, 12, 2, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + generateBox(level, chunkBB, 4, 0, 0, 8, 1, 3, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 4, 0, 9, 8, 1, 12, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 0, 4, 3, 1, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 9, 0, 4, 12, 1, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + for (int x = 4; x <= 8; x++) + { + for (int z = 0; z <= 2; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, 12 - z, chunkBB); + } + } + for (int x = 0; x <= 2; x++) + { + for (int z = 4; z <= 8; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + fillColumnDown(level, Tile::netherBrick_Id, 0, 12 - x, -1, z, chunkBB); + } + } + + return true; + +} + + +NetherBridgePieces::CastleSmallCorridorPiece::CastleSmallCorridorPiece(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = stairsBox; +} + + +void NetherBridgePieces::CastleSmallCorridorPiece::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateChildForward((StartPiece *) startPiece, pieces, random, 1, 0, true); +} + +NetherBridgePieces::CastleSmallCorridorPiece *NetherBridgePieces::CastleSmallCorridorPiece::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, 0, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new CastleSmallCorridorPiece(genDepth, random, box, direction); +} + +bool NetherBridgePieces::CastleSmallCorridorPiece::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // floor + generateBox(level, chunkBB, 0, 0, 0, 4, 1, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // room air + generateBox(level, chunkBB, 0, 2, 0, 4, 5, 4, 0, 0, false); + + // walls + generateBox(level, chunkBB, 0, 2, 0, 0, 5, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 4, 2, 0, 4, 5, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 3, 1, 0, 4, 1, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 0, 3, 3, 0, 4, 3, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 4, 3, 1, 4, 4, 1, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 4, 3, 3, 4, 4, 3, Tile::netherFence_Id, Tile::netherFence_Id, false); + + // roof + generateBox(level, chunkBB, 0, 6, 0, 4, 6, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // supports + for (int x = 0; x <= 4; x++) + { + for (int z = 0; z <= 4; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + } + } + + return true; +} + + +NetherBridgePieces::CastleSmallCorridorCrossingPiece::CastleSmallCorridorCrossingPiece(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void NetherBridgePieces::CastleSmallCorridorCrossingPiece::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateChildForward((StartPiece *) startPiece, pieces, random, 1, 0, true); + generateChildLeft((StartPiece *) startPiece, pieces, random, 0, 1, true); + generateChildRight((StartPiece *) startPiece, pieces, random, 0, 1, true); +} + +NetherBridgePieces::CastleSmallCorridorCrossingPiece *NetherBridgePieces::CastleSmallCorridorCrossingPiece::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, 0, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new CastleSmallCorridorCrossingPiece(genDepth, random, box, direction); +} + +bool NetherBridgePieces::CastleSmallCorridorCrossingPiece::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // floor + generateBox(level, chunkBB, 0, 0, 0, 4, 1, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // room air + generateBox(level, chunkBB, 0, 2, 0, 4, 5, 4, 0, 0, false); + + // walls + generateBox(level, chunkBB, 0, 2, 0, 0, 5, 0, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 4, 2, 0, 4, 5, 0, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 2, 4, 0, 5, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 4, 2, 4, 4, 5, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // roof + generateBox(level, chunkBB, 0, 6, 0, 4, 6, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // supports + for (int x = 0; x <= 4; x++) + { + for (int z = 0; z <= 4; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + } + } + + return true; +} + + +NetherBridgePieces::CastleSmallCorridorRightTurnPiece::CastleSmallCorridorRightTurnPiece(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void NetherBridgePieces::CastleSmallCorridorRightTurnPiece::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateChildRight((StartPiece *) startPiece, pieces, random, 0, 1, true); +} + +NetherBridgePieces::CastleSmallCorridorRightTurnPiece *NetherBridgePieces::CastleSmallCorridorRightTurnPiece::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, 0, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new CastleSmallCorridorRightTurnPiece(genDepth, random, box, direction); +} + +bool NetherBridgePieces::CastleSmallCorridorRightTurnPiece::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // floor + generateBox(level, chunkBB, 0, 0, 0, 4, 1, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // room air + generateBox(level, chunkBB, 0, 2, 0, 4, 5, 4, 0, 0, false); + + // walls + generateBox(level, chunkBB, 0, 2, 0, 0, 5, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 0, 3, 1, 0, 4, 1, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 0, 3, 3, 0, 4, 3, Tile::netherFence_Id, Tile::netherFence_Id, false); + + generateBox(level, chunkBB, 4, 2, 0, 4, 5, 0, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + generateBox(level, chunkBB, 1, 2, 4, 4, 5, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 3, 4, 1, 4, 4, Tile::netherFence_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 3, 3, 4, 3, 4, 4, Tile::netherFence_Id, Tile::netherBrick_Id, false); + + // roof + generateBox(level, chunkBB, 0, 6, 0, 4, 6, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // supports + for (int x = 0; x <= 4; x++) + { + for (int z = 0; z <= 4; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + } + } + + return true; +} + + +NetherBridgePieces::CastleSmallCorridorLeftTurnPiece::CastleSmallCorridorLeftTurnPiece(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void NetherBridgePieces::CastleSmallCorridorLeftTurnPiece::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateChildLeft((StartPiece *) startPiece, pieces, random, 0, 1, true); +} + +NetherBridgePieces::CastleSmallCorridorLeftTurnPiece *NetherBridgePieces::CastleSmallCorridorLeftTurnPiece::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, 0, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new CastleSmallCorridorLeftTurnPiece(genDepth, random, box, direction); +} + +bool NetherBridgePieces::CastleSmallCorridorLeftTurnPiece::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // floor + generateBox(level, chunkBB, 0, 0, 0, 4, 1, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // room air + generateBox(level, chunkBB, 0, 2, 0, 4, 5, 4, 0, 0, false); + + // walls + generateBox(level, chunkBB, 4, 2, 0, 4, 5, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 4, 3, 1, 4, 4, 1, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 4, 3, 3, 4, 4, 3, Tile::netherFence_Id, Tile::netherFence_Id, false); + + generateBox(level, chunkBB, 0, 2, 0, 0, 5, 0, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + generateBox(level, chunkBB, 0, 2, 4, 3, 5, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 3, 4, 1, 4, 4, Tile::netherFence_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 3, 3, 4, 3, 4, 4, Tile::netherFence_Id, Tile::netherBrick_Id, false); + + // roof + generateBox(level, chunkBB, 0, 6, 0, 4, 6, 4, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // supports + for (int x = 0; x <= 4; x++) + { + for (int z = 0; z <= 4; z++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + } + } + + return true; +} + + +NetherBridgePieces::CastleCorridorStairsPiece::CastleCorridorStairsPiece(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void NetherBridgePieces::CastleCorridorStairsPiece::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateChildForward((StartPiece *) startPiece, pieces, random, 1, 0, true); +} + +NetherBridgePieces::CastleCorridorStairsPiece *NetherBridgePieces::CastleCorridorStairsPiece::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -7, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new CastleCorridorStairsPiece(genDepth, random, box, direction); +} + +bool NetherBridgePieces::CastleCorridorStairsPiece::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // generate stairs + int stairsData = getOrientationData(Tile::stairs_netherBricks_Id, 2); + for (int step = 0; step <= 9; step++) + { + int floor = max(1, 7 - step); + int roof = min(max(floor + 5, 14 - step), 13); + int z = step; + + // floor + generateBox(level, chunkBB, 0, 0, z, 4, floor, z, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // room air + generateBox(level, chunkBB, 1, floor + 1, z, 3, roof - 1, z, 0, 0, false); + if (step <= 6) + { + placeBlock(level, Tile::stairs_netherBricks_Id, stairsData, 1, floor + 1, z, chunkBB); + placeBlock(level, Tile::stairs_netherBricks_Id, stairsData, 2, floor + 1, z, chunkBB); + placeBlock(level, Tile::stairs_netherBricks_Id, stairsData, 3, floor + 1, z, chunkBB); + } + // roof + generateBox(level, chunkBB, 0, roof, z, 4, roof, z, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // walls + generateBox(level, chunkBB, 0, floor + 1, z, 0, roof - 1, z, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 4, floor + 1, z, 4, roof - 1, z, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + if ((step & 1) == 0) + { + generateBox(level, chunkBB, 0, floor + 2, z, 0, floor + 3, z, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 4, floor + 2, z, 4, floor + 3, z, Tile::netherFence_Id, Tile::netherFence_Id, false); + } + + // supports + for (int x = 0; x <= 4; x++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + } + } + + + return true; +} + + +NetherBridgePieces::CastleCorridorTBalconyPiece::CastleCorridorTBalconyPiece(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : NetherBridgePiece(genDepth) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void NetherBridgePieces::CastleCorridorTBalconyPiece::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + int zOff = 1; + // compensate for weird negative-facing behaviour + if (orientation == Direction::WEST || orientation == Direction::NORTH) + { + zOff = 5; + } + + generateChildLeft((StartPiece *) startPiece, pieces, random, 0, zOff, random->nextInt(8) > 0); + generateChildRight((StartPiece *) startPiece, pieces, random, 0, zOff, random->nextInt(8) > 0); +} + +NetherBridgePieces::CastleCorridorTBalconyPiece *NetherBridgePieces::CastleCorridorTBalconyPiece::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -3, 0, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((NetherBridgePieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new CastleCorridorTBalconyPiece(genDepth, random, box, direction); +} + +bool NetherBridgePieces::CastleCorridorTBalconyPiece::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // floor + generateBox(level, chunkBB, 0, 0, 0, 8, 1, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + // room air + generateBox(level, chunkBB, 0, 2, 0, 8, 5, 8, 0, 0, false); + // corridor roof + generateBox(level, chunkBB, 0, 6, 0, 8, 6, 5, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + + // inside walls + generateBox(level, chunkBB, 0, 2, 0, 2, 5, 0, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 6, 2, 0, 8, 5, 0, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 3, 0, 1, 4, 0, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 7, 3, 0, 7, 4, 0, Tile::netherFence_Id, Tile::netherFence_Id, false); + + // balcony floor + generateBox(level, chunkBB, 0, 2, 4, 8, 2, 8, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 1, 4, 2, 2, 4, 0, 0, false); + generateBox(level, chunkBB, 6, 1, 4, 7, 2, 4, 0, 0, false); + + // hand rails + generateBox(level, chunkBB, 0, 3, 8, 8, 3, 8, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 0, 3, 6, 0, 3, 7, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 8, 3, 6, 8, 3, 7, Tile::netherFence_Id, Tile::netherFence_Id, false); + + // balcony walls + generateBox(level, chunkBB, 0, 3, 4, 0, 5, 5, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 8, 3, 4, 8, 5, 5, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 3, 5, 2, 5, 5, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 6, 3, 5, 7, 5, 5, Tile::netherBrick_Id, Tile::netherBrick_Id, false); + generateBox(level, chunkBB, 1, 4, 5, 1, 5, 5, Tile::netherFence_Id, Tile::netherFence_Id, false); + generateBox(level, chunkBB, 7, 4, 5, 7, 5, 5, Tile::netherFence_Id, Tile::netherFence_Id, false); + + // supports + for (int z = 0; z <= 5; z++) + { + for (int x = 0; x <= 8; x++) + { + fillColumnDown(level, Tile::netherBrick_Id, 0, x, -1, z, chunkBB); + } + } + + return true; +} + + diff --git a/Minecraft.World/NetherBridgePieces.h b/Minecraft.World/NetherBridgePieces.h new file mode 100644 index 00000000..47d2ca67 --- /dev/null +++ b/Minecraft.World/NetherBridgePieces.h @@ -0,0 +1,343 @@ +#pragma once +#include "StructurePiece.h" + +class NetherBridgePieces +{ +private: + static const int MAX_DEPTH = 30; + // the dungeon starts at 64 and traverses downwards to this point + static const int LOWEST_Y_POSITION = 10; + + // 4J - added to replace use of Class within this class + enum EPieceClass + { + EPieceClass_BridgeStraight, + EPieceClass_BridgeEndFiller, + EPieceClass_BridgeCrossing, + EPieceClass_RoomCrossing, + EPieceClass_StairsRoom, + EPieceClass_MonsterThrone, + EPieceClass_CastleEntrance, + EPieceClass_CastleStalkRoom, + EPieceClass_CastleSmallCorridorPiece, + EPieceClass_CastleSmallCorridorCrossingPiece, + EPieceClass_CastleSmallCorridorRightTurnPiece, + EPieceClass_CastleSmallCorridorLeftTurnPiece, + EPieceClass_CastleCorridorStairsPiece, + EPieceClass_CastleCorridorTBalconyPiece + }; + + class PieceWeight + { + public: + EPieceClass pieceClass; + const int weight; + int placeCount; + int maxPlaceCount; + bool allowInRow; + + PieceWeight(EPieceClass pieceClass, int weight, int maxPlaceCount, bool allowInRow); + PieceWeight(EPieceClass pieceClass, int weight, int maxPlaceCount); + bool doPlace(int depth); + bool isValid(); + }; + + static const int BRIDGE_PIECEWEIGHTS_COUNT = 6; + static const int CASTLE_PIECEWEIGHTS_COUNT = 7; + static NetherBridgePieces::PieceWeight *bridgePieceWeights[BRIDGE_PIECEWEIGHTS_COUNT]; + static NetherBridgePieces::PieceWeight *castlePieceWeights[CASTLE_PIECEWEIGHTS_COUNT]; + +private: + class NetherBridgePiece; + static NetherBridgePiece *findAndCreateBridgePieceFactory(NetherBridgePieces::PieceWeight *piece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth); + + /** + * + * + */ +public: + class StartPiece; +private: + + class NetherBridgePiece : public StructurePiece + { + protected: + NetherBridgePiece(int genDepth); + private: + int updatePieceWeight(list *currentPieces); + + NetherBridgePiece *generatePiece(StartPiece *startPiece, list *currentPieces, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth); + StructurePiece *generateAndAddPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth, bool isCastle); + protected: + StructurePiece *generateChildForward(StartPiece *startPiece, list *pieces, Random *random, int xOff, int yOff, bool isCastle); + StructurePiece *generateChildLeft(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff, bool isCastle); + StructurePiece *generateChildRight(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff, bool isCastle); + + static bool isOkBox(BoundingBox *box, StartPiece *startRoom); // 4J added startRoom param + void generateLightPost(Level *level, Random *random, BoundingBox *chunkBB, int x, int y, int z, int xOff, int zOff); + + void generateLightPostFacingRight(Level *level, Random *random, BoundingBox *chunkBB, int x, int y, int z); + void generateLightPostFacingLeft(Level *level, Random *random, BoundingBox *chunkBB, int x, int y, int z); + void generateLightPostFacingUp(Level *level, Random *random, BoundingBox *chunkBB, int x, int y, int z); + void generateLightPostFacingDown(Level *level, Random *random, BoundingBox *chunkBB, int x, int y, int z); + }; + + /** + * + * + */ + class BridgeStraight : public NetherBridgePiece + { + private: + static const int width = 5; + static const int height = 10; + static const int depth = 19; + + public: + BridgeStraight(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static BridgeStraight *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + class BridgeEndFiller : public NetherBridgePiece + { + private: + static const int width = 5; + static const int height = 10; + static const int depth = 8; + + int selfSeed; + + public: + BridgeEndFiller(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + static BridgeEndFiller *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + class BridgeCrossing : public NetherBridgePiece + { + private: + static const int width = 19; + static const int height = 10; + static const int depth = 19; + + public: + BridgeCrossing(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + protected: + BridgeCrossing(Random *random, int west, int north); + public: + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static BridgeCrossing *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + +public: + class StartPiece : public BridgeCrossing + { + + public: + bool isLibraryAdded; + PieceWeight *previousPiece; + Level *m_level; + + list availableBridgePieces; + list availableCastlePieces; + + // this queue is used so that the addChildren calls are + // called in a random order + vector pendingChildren; + + StartPiece(Random *random, int west, int north, Level *level); // 4J Added level param + + }; + +private: + class RoomCrossing : public NetherBridgePiece + { + private: + static const int width = 7; + static const int height = 9; + static const int depth = 7; + + public: + RoomCrossing(int genDepth, Random *random, BoundingBox *box, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static RoomCrossing *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + class StairsRoom : public NetherBridgePiece + { + private: + static const int width = 7; + static const int height = 11; + static const int depth = 7; + + public: + StairsRoom(int genDepth, Random *random, BoundingBox *box, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static StairsRoom *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + class MonsterThrone : public NetherBridgePiece + { + private: + static const int width = 7; + static const int height = 8; + static const int depth = 9; + + bool hasPlacedMobSpawner; + + public: + MonsterThrone(int genDepth, Random *random, BoundingBox *box, int direction); + static MonsterThrone *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ + class CastleEntrance : public NetherBridgePiece + { + private: + static const int width = 13; + static const int height = 14; + static const int depth = 13; + public: + CastleEntrance(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static CastleEntrance *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ + class CastleStalkRoom : public NetherBridgePiece + { + private: + static const int width = 13; + static const int height = 14; + static const int depth = 13; + + public: + CastleStalkRoom(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static CastleStalkRoom *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ + class CastleSmallCorridorPiece : public NetherBridgePiece + { + private: + static const int width = 5; + static const int height = 7; + static const int depth = 5; + + public: + CastleSmallCorridorPiece(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static CastleSmallCorridorPiece *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ + class CastleSmallCorridorCrossingPiece : public NetherBridgePiece + { + private: + static const int width = 5; + static const int height = 7; + static const int depth = 5; + + public: + CastleSmallCorridorCrossingPiece(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static CastleSmallCorridorCrossingPiece *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ + class CastleSmallCorridorRightTurnPiece : public NetherBridgePiece + { + private: + static const int width = 5; + static const int height = 7; + static const int depth = 5; + + public: + CastleSmallCorridorRightTurnPiece(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static CastleSmallCorridorRightTurnPiece *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + + }; + + /** + * + * + */ + class CastleSmallCorridorLeftTurnPiece : public NetherBridgePiece + { + private: + static const int width = 5; + static const int height = 7; + static const int depth = 5; + + public: + CastleSmallCorridorLeftTurnPiece(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static CastleSmallCorridorLeftTurnPiece *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ + class CastleCorridorStairsPiece : public NetherBridgePiece + { + private: + static const int width = 5; + static const int height = 14; + static const int depth = 10; + + public: + CastleCorridorStairsPiece(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static CastleCorridorStairsPiece *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + + }; + + /** + * + * + */ + class CastleCorridorTBalconyPiece : public NetherBridgePiece + { + private: + static const int width = 9; + static const int height = 7; + static const int depth = 9; + + public: + CastleCorridorTBalconyPiece(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static CastleCorridorTBalconyPiece *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; +}; diff --git a/Minecraft.World/NetherSphere.cpp b/Minecraft.World/NetherSphere.cpp new file mode 100644 index 00000000..072a8ad4 --- /dev/null +++ b/Minecraft.World/NetherSphere.cpp @@ -0,0 +1,26 @@ +#include "stdafx.h" +#include "NetherSphere.h" + + + +NetherSphere::NetherSphere(Level *level) : Entity(level) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + setSize(4, 4); +} + + +void NetherSphere::defineSynchedData() +{ +} + +void NetherSphere::readAdditionalSaveData(CompoundTag *tag) +{ +} + +void NetherSphere::addAdditonalSaveData(CompoundTag *tag) +{ +} \ No newline at end of file diff --git a/Minecraft.World/NetherSphere.h b/Minecraft.World/NetherSphere.h new file mode 100644 index 00000000..2a6ce490 --- /dev/null +++ b/Minecraft.World/NetherSphere.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Entity.h" + +class NetherSphere : public Entity +{ +public: + eINSTANCEOF GetType() { return eTYPE_NETHER_SPHERE; }; +public: + NetherSphere(Level *level); + +protected: + virtual void defineSynchedData(); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual void addAdditonalSaveData(CompoundTag *tag); +}; \ No newline at end of file diff --git a/Minecraft.World/NetherStalkTile.cpp b/Minecraft.World/NetherStalkTile.cpp new file mode 100644 index 00000000..eadf9646 --- /dev/null +++ b/Minecraft.World/NetherStalkTile.cpp @@ -0,0 +1,122 @@ +#include "stdafx.h" +#include "NetherStalkTile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.h" + +const wstring NetherStalkTile::TEXTURE_NAMES[] = { L"netherStalk_0", L"netherStalk_1", L"netherStalk_2" }; + +NetherStalkTile::NetherStalkTile(int id) : Bush(id) +{ + setTicking(true); + updateDefaultShape(); + + icons = NULL; +} + +// 4J Added override +void NetherStalkTile::updateDefaultShape() +{ + float ss = 0.5f; + this->setShape(0.5f - ss, 0, 0.5f - ss, 0.5f + ss, 0.25f, 0.5f + ss); +} + +bool NetherStalkTile::mayPlaceOn(int tile) +{ + return tile == Tile::hellSand_Id; +} + +// Brought forward to fix #60073 - TU7: Content: Gameplay: Nether Warts cannot be placed next to each other in the Nether +bool NetherStalkTile::canSurvive(Level *level, int x, int y, int z) +{ + return mayPlaceOn(level->getTile(x, y - 1, z)); +} + +void NetherStalkTile::tick(Level *level, int x, int y, int z, Random *random) +{ + int age = level->getData(x, y, z); + if (age < MAX_AGE) + { + //Biome *biome = biomeSource->getBiome(x, z); + //if (dynamic_cast(biome) != NULL) + //{ + if (random->nextInt(10) == 0) + { + age++; + level->setData(x, y, z, age); + } + //} + } + + Bush::tick(level, x, y, z, random); +} + +void NetherStalkTile::growCropsToMax(Level *level, int x, int y, int z) +{ + level->setData(x, y, z, MAX_AGE); +} + +Icon *NetherStalkTile::getTexture(int face, int data) +{ + if (data >= MAX_AGE) + { + return icons[2]; + } + if (data > 0) + { + return icons[1]; + } + return icons[0]; +} + +int NetherStalkTile::getRenderShape() +{ + return Tile::SHAPE_ROWS; +} + +void NetherStalkTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus) +{ + if (level->isClientSide) + { + return; + } + int count = 1; + if (data >= MAX_AGE) + { + count = 2 + level->random->nextInt(3); + if (playerBonus > 0) + { + count += level->random->nextInt(playerBonus + 1); + } + } + for (int i = 0; i < count; i++) + { + popResource(level, x, y, z, shared_ptr(new ItemInstance(Item::netherStalkSeeds))); + } +} + +int NetherStalkTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return 0; +} + +int NetherStalkTile::getResourceCount(Random *random) +{ + return 0; +} + +int NetherStalkTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Item::netherStalkSeeds_Id; +} + +void NetherStalkTile::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[NETHER_STALK_TEXTURE_COUNT]; + + for (int i = 0; i < NETHER_STALK_TEXTURE_COUNT; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURE_NAMES[i]); + } +} diff --git a/Minecraft.World/NetherStalkTile.h b/Minecraft.World/NetherStalkTile.h new file mode 100644 index 00000000..39093274 --- /dev/null +++ b/Minecraft.World/NetherStalkTile.h @@ -0,0 +1,33 @@ +#pragma once +#include "Bush.h" + +class ChunkRebuildData; +class NetherStalkTile : public Bush +{ + friend class ChunkRebuildData; +private: + static const int MAX_AGE = 3; + + static const int NETHER_STALK_TEXTURE_COUNT = 3; + static const wstring TEXTURE_NAMES[]; + + Icon **icons; + +public: + NetherStalkTile(int id); + virtual void updateDefaultShape(); // 4J Added override + virtual bool mayPlaceOn(int tile); + + // Brought forward to fix #60073 - TU7: Content: Gameplay: Nether Warts cannot be placed next to each other in the Nether + virtual bool canSurvive(Level *level, int x, int y, int z); + + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual void growCropsToMax(Level *level, int x, int y, int z); + virtual Icon *getTexture(int face, int data); + virtual int getRenderShape(); + virtual void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int getResourceCount(Random *random); + virtual int cloneTileId(Level *level, int x, int y, int z); + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/Node.cpp b/Minecraft.World/Node.cpp new file mode 100644 index 00000000..5f501a84 --- /dev/null +++ b/Minecraft.World/Node.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.pathfinder.h" + +// 4J - added for common ctor code +// Do all the default initialisations done in the java class +void Node::_init() +{ + heapIdx = -1; + + closed = false; + + cameFrom = NULL; +} + +Node::Node(const int x, const int y, const int z) : +x(x), +y(y), +z(z), +hash(createHash(x, y, z)) +{ + _init(); + + //this->x = x; + //this->y = y; + //this->z = z; + + //hash = createHash(x, y, z); +} + +int Node::createHash(const int x, const int y, const int z) +{ + return (y & 0xff) | ((x & 0x7fff) << 8) | ((z & 0x7fff) << 24) | ((x < 0) ? 0x0080000000 : 0) | ((z < 0) ? 0x0000008000 : 0); +} + +float Node::distanceTo(Node *to) +{ + float xd = (float) ( to->x - x ); + float yd = (float) ( to->y - y ); + float zd = (float) ( to->z - z ); + return Mth::sqrt(xd * xd + yd * yd + zd * zd); +} + +float Node::distanceToSqr(Node *to) +{ + float xd = to->x - x; + float yd = to->y - y; + float zd = to->z - z; + return xd * xd + yd * yd + zd * zd; +} + +bool Node::equals(Node *o) +{ + //4J Jev, never used anything other than a node. + //if (dynamic_cast((Node *) o) != NULL) + //{ + return hash == o->hash && x == o->x && y == o->y && z == o->z; + //} + //return false; +} + +int Node::hashCode() +{ + return hash; +} + +bool Node::inOpenSet() +{ + return heapIdx >= 0; +} + +wstring Node::toString() +{ + return _toString(x) + L", " + _toString(y) + L", " + _toString(z); +} \ No newline at end of file diff --git a/Minecraft.World/Node.h b/Minecraft.World/Node.h new file mode 100644 index 00000000..6d2bdda8 --- /dev/null +++ b/Minecraft.World/Node.h @@ -0,0 +1,38 @@ +#pragma once +using namespace std; + +class Node +{ + // 4J Jev, these classes were accessing protected members. + friend class BinaryHeap; + friend class PathFinder; + friend class EnderDragon; + +public: + const int x, y, z; + +private: + const int hash; + +protected: + int heapIdx; + float g, h, f; + Node *cameFrom; + +public: + bool closed; + + void _init(); + eINSTANCEOF GetType() { return eType_NODE;} + + Node() : hash(0),x(0),y(0),z(0) {} // 4J - added default constructor so we can make an empty of array of these as a copy target + Node(const int x, const int y, const int z); + + static int createHash(const int x, const int y, const int z); + float distanceTo(Node *to); + float distanceToSqr(Node *to); + bool equals(Node *o); + int hashCode(); + bool inOpenSet(); + wstring toString(); +}; diff --git a/Minecraft.World/NonTameRandomTargetGoal.cpp b/Minecraft.World/NonTameRandomTargetGoal.cpp new file mode 100644 index 00000000..734d637f --- /dev/null +++ b/Minecraft.World/NonTameRandomTargetGoal.cpp @@ -0,0 +1,14 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.animal.h" +#include "NonTameRandomTargetGoal.h" + +NonTameRandomTargetGoal::NonTameRandomTargetGoal(TamableAnimal *mob, const type_info& targetType, float within, int randomInterval, bool mustSee) : NearestAttackableTargetGoal(mob, targetType, within, randomInterval, mustSee) +{ + this->tamableMob = mob; +} + +bool NonTameRandomTargetGoal::canUse() +{ + if (tamableMob->isTame()) return false; + return NearestAttackableTargetGoal::canUse(); +} diff --git a/Minecraft.World/NonTameRandomTargetGoal.h b/Minecraft.World/NonTameRandomTargetGoal.h new file mode 100644 index 00000000..8adba7df --- /dev/null +++ b/Minecraft.World/NonTameRandomTargetGoal.h @@ -0,0 +1,16 @@ +#pragma once + +#include "NearestAttackableTargetGoal.h" + +class TamableAnimal; + +class NonTameRandomTargetGoal : public NearestAttackableTargetGoal +{ +private: + TamableAnimal *tamableMob; // Owner of this goal + +public: + NonTameRandomTargetGoal(TamableAnimal *mob, const type_info& targetType, float within, int randomInterval, bool mustSee); + + bool canUse(); +}; \ No newline at end of file diff --git a/Minecraft.World/NormalDimension.h b/Minecraft.World/NormalDimension.h new file mode 100644 index 00000000..357ff04e --- /dev/null +++ b/Minecraft.World/NormalDimension.h @@ -0,0 +1,6 @@ +#pragma once +#include "Dimension.h" + +class NormalDimension : public Dimension +{ +}; \ No newline at end of file diff --git a/Minecraft.World/NotGateTile.cpp b/Minecraft.World/NotGateTile.cpp new file mode 100644 index 00000000..a3971284 --- /dev/null +++ b/Minecraft.World/NotGateTile.cpp @@ -0,0 +1,247 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "NotGateTile.h" +#include "SoundTypes.h" +#include "net.minecraft.world.h" + +unordered_map *> NotGateTile::recentToggles = unordered_map *>(); + +// 4J - added, to tie in with other changes brought forward from 1.3.2 to associate toggles with a level. In addition to what the java +// version does, we are also removing any references to levels that we are storing when they hit their dtor. +void NotGateTile::removeLevelReferences(Level *level) +{ + if( recentToggles.find(level) != recentToggles.end() ) + { + delete recentToggles[level]; + recentToggles.erase(level); + } +} + +bool NotGateTile::isToggledTooFrequently(Level *level, int x, int y, int z, bool add) +{ + // 4J - brought forward changes to associate toggles with a level from 1.3.2 + if( recentToggles.find(level) == recentToggles.end() ) + { + recentToggles[level] = new deque; + } + if (add) recentToggles[level]->push_back(Toggle(x, y, z, level->getTime())); + int count = 0; + + AUTO_VAR(itEnd, recentToggles[level]->end()); + for (AUTO_VAR(it, recentToggles[level]->begin()); it != itEnd; it++) + { + if (it->x == x && it->y == y && it->z == z) + { + count++; + if (count >= MAX_RECENT_TOGGLES) + { + return true; + } + } + } + return false; +} + +NotGateTile::NotGateTile(int id, bool on) : TorchTile(id) +{ + this->on = on; + this->setTicking(true); +} + +int NotGateTile::getTickDelay() +{ + return 2; +} + +void NotGateTile::onPlace(Level *level, int x, int y, int z) +{ + if (level->getData(x, y, z) == 0) TorchTile::onPlace(level, x, y, z); + if (on) + { + level->updateNeighborsAt(x, y - 1, z, id); + level->updateNeighborsAt(x, y + 1, z, id); + level->updateNeighborsAt(x - 1, y, z, id); + level->updateNeighborsAt(x + 1, y, z, id); + level->updateNeighborsAt(x, y, z - 1, id); + level->updateNeighborsAt(x, y, z + 1, id); + } +} + +void NotGateTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + if (on) + { + level->updateNeighborsAt(x, y - 1, z, this->id); + level->updateNeighborsAt(x, y + 1, z, this->id); + level->updateNeighborsAt(x - 1, y, z, this->id); + level->updateNeighborsAt(x + 1, y, z, this->id); + level->updateNeighborsAt(x, y, z - 1, this->id); + level->updateNeighborsAt(x, y, z + 1, this->id); + } +} + +bool NotGateTile::getSignal(LevelSource *level, int x, int y, int z, int face) +{ + if (!on) return false; + + int dir = level->getData(x, y, z); + + if (dir == 5 && face == 1) return false; + if (dir == 3 && face == 3) return false; + if (dir == 4 && face == 2) return false; + if (dir == 1 && face == 5) return false; + if (dir == 2 && face == 4) return false; + + return true; +} + +bool NotGateTile::hasNeighborSignal(Level *level, int x, int y, int z) +{ + int dir = level->getData(x, y, z); + + if (dir == 5 && level->getSignal(x, y - 1, z, 0)) return true; + if (dir == 3 && level->getSignal(x, y, z - 1, 2)) return true; + if (dir == 4 && level->getSignal(x, y, z + 1, 3)) return true; + if (dir == 1 && level->getSignal(x - 1, y, z, 4)) return true; + if (dir == 2 && level->getSignal(x + 1, y, z, 5)) return true; + return false; +} + +void NotGateTile::tick(Level *level, int x, int y, int z, Random *random) +{ + bool neighborSignal = hasNeighborSignal(level, x, y, z); + + // 4J - brought forward changes from 1.3.2 to associate toggles with level + if( recentToggles.find(level) != recentToggles.end() ) + { + deque *toggles = recentToggles[level]; + while (!toggles->empty() && level->getTime() - toggles->front().when > RECENT_TOGGLE_TIMER) + { + toggles->pop_front(); + } + } + + if (on) + { + if (neighborSignal) + { + level->setTileAndData(x, y, z, Tile::notGate_off_Id, level->getData(x, y, z)); + + if (isToggledTooFrequently(level, x, y, z, true)) + { + app.DebugPrintf("Torch at (%d,%d,%d) has toggled too many times\n",x,y,z); + + level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_RANDOM_FIZZ, 0.5f, 2.6f + (level->random->nextFloat() - level->random->nextFloat()) * 0.8f); + for (int i = 0; i < 5; i++) + { + double xx = x + random->nextDouble() * 0.6 + 0.2; + double yy = y + random->nextDouble() * 0.6 + 0.2; + double zz = z + random->nextDouble() * 0.6 + 0.2; + + level->addParticle(eParticleType_smoke, xx, yy, zz, 0, 0, 0); + } + } + } + } + else + { + if (!neighborSignal) + { + if (!isToggledTooFrequently(level, x, y, z, false)) + { + level->setTileAndData(x, y, z, Tile::notGate_on_Id, level->getData(x, y, z)); + } + else + { + app.DebugPrintf("Torch at (%d,%d,%d) has toggled too many times\n",x,y,z); + } + } + } +} + +void NotGateTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + TorchTile::neighborChanged(level, x, y, z, type); + level->addToTickNextTick(x, y, z, id, getTickDelay()); +} + +bool NotGateTile::getDirectSignal(Level *level, int x, int y, int z, int face) +{ + if (face == 0) + { + return getSignal(level, x, y, z, face); + } + return false; +} + +int NotGateTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::notGate_on_Id; +} + +bool NotGateTile::isSignalSource() +{ + return true; +} + +void NotGateTile::animateTick(Level *level, int xt, int yt, int zt, Random *random) +{ + if (!on) return; + int dir = level->getData(xt, yt, zt); + double x = xt + 0.5f + (random->nextFloat() - 0.5f) * 0.2; + double y = yt + 0.7f + (random->nextFloat() - 0.5f) * 0.2; + double z = zt + 0.5f + (random->nextFloat() - 0.5f) * 0.2; + double h = 0.22f; + double r = 0.27f; + if (dir == 1) + { + level->addParticle(eParticleType_reddust, x - r, y + h, z, 0, 0, 0); + } + else if(dir == 2) + { + level->addParticle(eParticleType_reddust, x + r, y + h, z, 0, 0, 0); + } + else if (dir == 3) + { + level->addParticle(eParticleType_reddust, x, y + h, z - r, 0, 0, 0); + } + else if (dir == 4) + { + level->addParticle(eParticleType_reddust, x, y + h, z + r, 0, 0, 0); + } + else + { + level->addParticle(eParticleType_reddust, x, y, z, 0, 0, 0); + } + +} + +int NotGateTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Tile::notGate_on_Id; +} + +void NotGateTile::levelTimeChanged(Level *level, __int64 delta, __int64 newTime) +{ + deque *toggles = recentToggles[level]; + + if (toggles != NULL) + { + for (AUTO_VAR(it,toggles->begin()); it != toggles->end(); ++it) + { + (*it).when += delta; + } + } +} + +void NotGateTile::registerIcons(IconRegister *iconRegister) +{ + if (on) + { + icon = iconRegister->registerIcon(L"redtorch_lit"); + } + else + { + icon = iconRegister->registerIcon(L"redtorch"); + } +} \ No newline at end of file diff --git a/Minecraft.World/NotGateTile.h b/Minecraft.World/NotGateTile.h new file mode 100644 index 00000000..52dd4a2e --- /dev/null +++ b/Minecraft.World/NotGateTile.h @@ -0,0 +1,67 @@ +#pragma once +#include "TorchTile.h" +using namespace std; + +class Random; + +class NotGateTile : public TorchTile +{ + friend class Tile; + +private: + static const int RECENT_TOGGLE_TIMER = 20 * 3; + static const int MAX_RECENT_TOGGLES = 8; + + bool on; + +public: + class Toggle + { + public: + int x, y, z; + __int64 when; + + Toggle(int x, int y, int z, __int64 when) + { + this->x = x; + this->y = y; + this->z = z; + this->when = when; + } + }; + +private: + static unordered_map *> recentToggles; // 4J - brought forward change from 1.3.2 +public: + static void removeLevelReferences(Level *level); // 4J added +private: + bool isToggledTooFrequently(Level *level, int x, int y, int z, bool add); + +protected: + NotGateTile(int id, bool on); + +public: + int getTickDelay(); + void onPlace(Level *level, int x, int y, int z); + void onRemove(Level *level, int x, int y, int z, int id, int data); + bool getSignal(LevelSource *level, int x, int y, int z, int face); + +private: + bool hasNeighborSignal(Level *level, int x, int y, int z); + +public: + void tick(Level *level, int x, int y, int z, Random *random); + void neighborChanged(Level *level, int x, int y, int z, int type); + + bool getDirectSignal(Level *level, int x, int y, int z, int face); + + int getResource(int data, Random *random, int playerBonusLevel); + bool isSignalSource(); + +public: + void animateTick(Level *level, int xt, int yt, int zt, Random *random); + int cloneTileId(Level *level, int x, int y, int z); + void levelTimeChanged(Level *level, __int64 delta, __int64 newTime); + + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/Npc.cpp b/Minecraft.World/Npc.cpp new file mode 100644 index 00000000..b96c937f --- /dev/null +++ b/Minecraft.World/Npc.cpp @@ -0,0 +1,4 @@ +#include "stdafx.h" + +#include "Npc.h" + diff --git a/Minecraft.World/Npc.h b/Minecraft.World/Npc.h new file mode 100644 index 00000000..2b20e672 --- /dev/null +++ b/Minecraft.World/Npc.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Creature.h" + +class Level; + +class Npc : public Creature +{ +public: + Npc() {} +}; \ No newline at end of file diff --git a/Minecraft.World/NumberFormaters.h b/Minecraft.World/NumberFormaters.h new file mode 100644 index 00000000..5f641819 --- /dev/null +++ b/Minecraft.World/NumberFormaters.h @@ -0,0 +1,35 @@ +#pragma once +using namespace std; + +// 4J Stu - The java formated numbers based on a local passed in, but I am just going for a constant format here +class NumberFormat +{ +public: + static wstring format(int value) + { + // TODO 4J Stu - Change the length of the formatted number + wchar_t output[256]; + swprintf( output, 256, L"%d", value); + wstring result = wstring( output ); + return result; + } +}; + + +class DecimalFormat +{ +private: + const wstring formatString; +public: + wstring format(double value) + { + // TODO 4J Stu - Change the length of the formatted number + wchar_t output[256]; + swprintf( output, 256, formatString.c_str(), value); + wstring result = wstring( output ); + return result; + } + + // 4J Stu - The java code took a string format, we take a printf format string + DecimalFormat(wstring x) : formatString( x ) {}; +}; \ No newline at end of file diff --git a/Minecraft.World/ObsidianTile.cpp b/Minecraft.World/ObsidianTile.cpp new file mode 100644 index 00000000..36390d47 --- /dev/null +++ b/Minecraft.World/ObsidianTile.cpp @@ -0,0 +1,16 @@ +#include "stdafx.h" +#include "ObsidianTile.h" + +ObsidianTile::ObsidianTile(int id) : StoneTile(id) +{ +} + +int ObsidianTile::getResourceCount(Random *random) +{ + return 1; +} + +int ObsidianTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::obsidian_Id; +} \ No newline at end of file diff --git a/Minecraft.World/ObsidianTile.h b/Minecraft.World/ObsidianTile.h new file mode 100644 index 00000000..6bf23edf --- /dev/null +++ b/Minecraft.World/ObsidianTile.h @@ -0,0 +1,12 @@ +#pragma once +#include "StoneTile.h" + +class Random; + +class ObsidianTile : public StoneTile +{ +public: + ObsidianTile(int id); + virtual int getResourceCount(Random *random); + virtual int getResource(int data, Random *random, int playerBonusLevel); +}; \ No newline at end of file diff --git a/Minecraft.World/OceanBiome.h b/Minecraft.World/OceanBiome.h new file mode 100644 index 00000000..a7faaf39 --- /dev/null +++ b/Minecraft.World/OceanBiome.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Biome.h" + +class OceanBiome : public Biome +{ +public: + OceanBiome(int id) : Biome(id) + { + friendlies.clear(); + friendlies_chicken.clear(); // 4J added since chicken now separated from main friendlies + friendlies_wolf.clear(); // 4J added since wolf now separated from main friendlies + } +}; diff --git a/Minecraft.World/OcelotSitOnTileGoal.cpp b/Minecraft.World/OcelotSitOnTileGoal.cpp new file mode 100644 index 00000000..f5cf7066 --- /dev/null +++ b/Minecraft.World/OcelotSitOnTileGoal.cpp @@ -0,0 +1,127 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "BasicTypeContainers.h" +#include "Arrays.h" +#include "OcelotSitOnTileGoal.h" + +const int OcelotSitOnTileGoal::GIVE_UP_TICKS = 3 * SharedConstants::TICKS_PER_SECOND; +const int OcelotSitOnTileGoal::SIT_TICKS = 60 * SharedConstants::TICKS_PER_SECOND; +const int OcelotSitOnTileGoal::SEARCH_RANGE = 8; +const double OcelotSitOnTileGoal::SIT_CHANCE = 0.0065f; + +OcelotSitOnTileGoal::OcelotSitOnTileGoal(Ozelot *ocelot, float speed) +{ + _tick = 0; + tryTicks = 0; + maxTicks = 0; + tileX = 0; + tileY = 0; + tileZ = 0; + + this->ocelot = ocelot; + this->speed = speed; + setRequiredControlFlags(Control::MoveControlFlag | Control::JumpControlFlag); +} + +bool OcelotSitOnTileGoal::canUse() +{ + return ocelot->isTame() && !ocelot->isSitting() && ocelot->getRandom()->nextDouble() <= SIT_CHANCE && findNearestTile(); +} + +bool OcelotSitOnTileGoal::canContinueToUse() +{ + return _tick <= maxTicks && tryTicks <= GIVE_UP_TICKS && isValidTarget(ocelot->level, tileX, tileY, tileZ); +} + +void OcelotSitOnTileGoal::start() +{ + ocelot->getNavigation()->moveTo((float) tileX + 0.5, tileY + 1, (float) tileZ + 0.5, speed); + _tick = 0; + tryTicks = 0; + maxTicks = ocelot->getRandom()->nextInt(ocelot->getRandom()->nextInt(SIT_TICKS) + SIT_TICKS) + SIT_TICKS; + ocelot->getSitGoal()->wantToSit(false); +} + +void OcelotSitOnTileGoal::stop() +{ + ocelot->setSitting(false); +} + +void OcelotSitOnTileGoal::tick() +{ + _tick++; + ocelot->getSitGoal()->wantToSit(false); + if (ocelot->distanceToSqr(tileX, tileY + 1, tileZ) > 1) + { + ocelot->setSitting(false); + ocelot->getNavigation()->moveTo((float) tileX + 0.5, tileY + 1, (float) tileZ + 0.5, speed); + tryTicks++; + } + else if (!ocelot->isSitting()) + { + ocelot->setSitting(true); + } + else + { + tryTicks--; + } +} + +bool OcelotSitOnTileGoal::findNearestTile() +{ + int y = (int) ocelot->y; + double distSqr = Integer::MAX_VALUE; + + for (int x = (int) ocelot->x - SEARCH_RANGE; x < ocelot->x + SEARCH_RANGE; x++) + { + for (int z = (int) ocelot->z - SEARCH_RANGE; z < ocelot->z + SEARCH_RANGE; z++) + { + if (isValidTarget(ocelot->level, x, y, z) && ocelot->level->isEmptyTile(x, y + 1, z)) + { + double dist = ocelot->distanceToSqr(x, y, z); + + if (dist < distSqr) + { + this->tileX = x; + this->tileY = y; + this->tileZ = z; + distSqr = dist; + } + } + } + } + + return distSqr < Integer::MAX_VALUE; +} + +bool OcelotSitOnTileGoal::isValidTarget(Level *level, int x, int y, int z) +{ + int tile = level->getTile(x, y, z); + int data = level->getData(x, y, z); + + if (tile == Tile::chest_Id) + { + shared_ptr chest = dynamic_pointer_cast(level->getTileEntity(x, y, z)); + + if (chest->openCount < 1) + { + return true; + } + } + else if (tile == Tile::furnace_lit_Id) + { + return true; + } + else if (tile == Tile::bed_Id && !BedTile::isHeadPiece(data)) + { + return true; + } + + return false; +} \ No newline at end of file diff --git a/Minecraft.World/OcelotSitOnTileGoal.h b/Minecraft.World/OcelotSitOnTileGoal.h new file mode 100644 index 00000000..6a8d9ed5 --- /dev/null +++ b/Minecraft.World/OcelotSitOnTileGoal.h @@ -0,0 +1,37 @@ +#pragma once + +#include "Goal.h" + +class Ozelot; + +class OcelotSitOnTileGoal : public Goal +{ +private: + static const int GIVE_UP_TICKS; + static const int SIT_TICKS; + static const int SEARCH_RANGE; + static const double SIT_CHANCE; + +private: + Ozelot *ocelot; // Owner of this goal + float speed; + int _tick; + int tryTicks; + int maxTicks; + int tileX; + int tileY; + int tileZ; + +public: + OcelotSitOnTileGoal(Ozelot *ocelot, float speed); + + bool canUse(); + bool canContinueToUse(); + void start(); + void stop(); + void tick(); + +private: + bool findNearestTile(); + bool isValidTarget(Level *level, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/OfferFlowerGoal.cpp b/Minecraft.World/OfferFlowerGoal.cpp new file mode 100644 index 00000000..b604ea84 --- /dev/null +++ b/Minecraft.World/OfferFlowerGoal.cpp @@ -0,0 +1,44 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.npc.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "OfferFlowerGoal.h" + +OfferFlowerGoal::OfferFlowerGoal(VillagerGolem *golem) +{ + this->golem = golem; + setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag); +} + +bool OfferFlowerGoal::canUse() +{ + if (!golem->level->isDay()) return false; + if (golem->getRandom()->nextInt(8000) != 0) return false; + villager = weak_ptr(dynamic_pointer_cast( golem->level->getClosestEntityOfClass(typeid(Villager), golem->bb->grow(6, 2, 6), golem->shared_from_this()) )); + return villager.lock() != NULL; +} + +bool OfferFlowerGoal::canContinueToUse() +{ + return _tick > 0 && villager.lock() != NULL; +} + +void OfferFlowerGoal::start() +{ + _tick = OFFER_TICKS; + golem->offerFlower(true); +} + +void OfferFlowerGoal::stop() +{ + golem->offerFlower(false); + villager = weak_ptr(); +} + +void OfferFlowerGoal::tick() +{ + golem->getLookControl()->setLookAt(villager.lock(), 30, 30); + --_tick; +} \ No newline at end of file diff --git a/Minecraft.World/OfferFlowerGoal.h b/Minecraft.World/OfferFlowerGoal.h new file mode 100644 index 00000000..35622efe --- /dev/null +++ b/Minecraft.World/OfferFlowerGoal.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Goal.h" + +class VillagerGolem; + +class OfferFlowerGoal : public Goal +{ +public: + static const int OFFER_TICKS = 400; + +private: + VillagerGolem *golem; + weak_ptr villager; + int _tick; + +public: + OfferFlowerGoal(VillagerGolem *golem); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + virtual void stop(); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/OldChunkStorage.cpp b/Minecraft.World/OldChunkStorage.cpp new file mode 100644 index 00000000..50c29a96 --- /dev/null +++ b/Minecraft.World/OldChunkStorage.cpp @@ -0,0 +1,594 @@ +#include "stdafx.h" +#include "File.h" +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.chunk.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.level.storage.h" +#include "FileHeader.h" +#include "OldChunkStorage.h" +DWORD OldChunkStorage::tlsIdx = 0; +OldChunkStorage::ThreadStorage *OldChunkStorage::tlsDefault = NULL; + +OldChunkStorage::ThreadStorage::ThreadStorage() +{ + blockData = byteArray(Level::CHUNK_TILE_COUNT); + dataData = byteArray(Level::HALF_CHUNK_TILE_COUNT); + skyLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); + blockLightData = byteArray(Level::HALF_CHUNK_TILE_COUNT); +} + +OldChunkStorage::ThreadStorage::~ThreadStorage() +{ + delete [] blockData.data; + delete [] dataData.data; + delete [] skyLightData.data; + delete [] blockLightData.data; +} + +void OldChunkStorage::CreateNewThreadStorage() +{ + ThreadStorage *tls = new ThreadStorage(); + if(tlsDefault == NULL ) + { + tlsIdx = TlsAlloc(); + tlsDefault = tls; + } + TlsSetValue(tlsIdx, tls); +} + +void OldChunkStorage::UseDefaultThreadStorage() +{ + TlsSetValue(tlsIdx, tlsDefault); +} + +void OldChunkStorage::ReleaseThreadStorage() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); + if( tls == tlsDefault ) return; + + delete tls; +} + +OldChunkStorage::OldChunkStorage(File dir, bool create) +{ + this->dir = dir; + this->create = create; +} + +File OldChunkStorage::getFile(int x, int z) +{ + wchar_t name[MAX_PATH_SIZE]; + wchar_t path1[MAX_PATH_SIZE]; + wchar_t path2[MAX_PATH_SIZE]; + + wchar_t xRadix36[64]; + wchar_t zRadix36[64]; +#if ( defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ ) + assert(0); // need a gcc verison of _itow ? +#else + _itow(x,xRadix36,36); + _itow(z,zRadix36,36); + swprintf(name,MAX_PATH_SIZE,L"c.%ls.%ls.dat",xRadix36,zRadix36); + _itow(x & 63,path1,36); + _itow(z & 63,path2,36); +#endif + //sprintf(file,"%s\\%s",dir,path1); + File file( dir, wstring( path1 ) ); + if( !file.exists() ) + { + if(create) file.mkdir(); + else + { + return File(L""); + } + } + + //strcat(file,"\\"); + //strcat(file,path2); + file = File( file, wstring( path2 ) ); + if( !file.exists() ) + { + if(create) file.mkdir(); + else + { + return File(L""); + } + } + + //strcat(file,"\\"); + //strcat(file,name); + //sprintf(file,"%s\\%s",file,name); + file = File( file, wstring( name ) ); + if ( !file.exists() ) + { + if (!create) + { + return File(L""); + } + } + return file; +} + +LevelChunk *OldChunkStorage::load(Level *level, int x, int z) +{ + File file = getFile(x, z); + if (!file.getPath().empty() && file.exists()) + { + // 4J - removed try/catch + // try { + // System.out.println("Loading chunk "+x+", "+z); + FileInputStream fis = FileInputStream(file); + CompoundTag *tag = NbtIo::readCompressed(&fis); + if (!tag->contains(L"Level")) + { + char buf[256]; + sprintf(buf,"Chunk file at %d, %d is missing level data, skipping\n",x,z); + app.DebugPrintf(buf); + return NULL; + } + if (!tag->getCompound(L"Level")->contains(L"Blocks")) + { + char buf[256]; + sprintf(buf,"Chunk file at %d, %d is missing block data, skipping\n",x,z); + app.DebugPrintf(buf); + return NULL; + } + LevelChunk *levelChunk = OldChunkStorage::load(level, tag->getCompound(L"Level")); + if (!levelChunk->isAt(x, z)) + { + char buf[256]; + sprintf(buf,"Chunk fileat %d, %d is in the wrong location; relocating. Expected %d, %d, got %d, %d\n", + x, z, x, z, levelChunk->x, levelChunk->z); + app.DebugPrintf(buf); + tag->putInt(L"xPos", x); + tag->putInt(L"zPos", z); + levelChunk = OldChunkStorage::load(level, tag->getCompound(L"Level")); + } + + return levelChunk; + // } catch (Exception e) { + // e.printStackTrace(); + // } + } + return NULL; +} + +void OldChunkStorage::save(Level *level, LevelChunk *levelChunk) +{ + level->checkSession(); + File file = getFile(levelChunk->x, levelChunk->z); + if (file.exists()) + { + LevelData *levelData = level->getLevelData(); + levelData->setSizeOnDisk( levelData->getSizeOnDisk() - file.length() ); + } + + // 4J - removed try/catch + // try { + //char tmpFileName[MAX_PATH_SIZE]; + //sprintf(tmpFileName,"%s\\%s",dir,"tmp_chunk.dat"); + File tmpFile( dir, L"tmp_chunk.dat" ); + // System.out.println("Saving chunk "+levelChunk.x+", "+levelChunk.z); + + FileOutputStream fos = FileOutputStream(tmpFile); + CompoundTag *tag = new CompoundTag(); + CompoundTag *levelData = new CompoundTag(); + tag->put(L"Level", levelData); + OldChunkStorage::save(levelChunk, level, levelData); + NbtIo::writeCompressed(tag, &fos); + fos.close(); + + if (file.exists()) + { + //DeleteFile(file); + file._delete(); + } + //MoveFile(tmpFile,file); + tmpFile.renameTo( file ); + + LevelData *levelInfo = level->getLevelData(); + levelInfo->setSizeOnDisk(levelInfo->getSizeOnDisk() + file.length() ); + // } catch (Exception e) { + // e.printStackTrace(); + // } +} + +bool OldChunkStorage::saveEntities(LevelChunk *lc, Level *level, CompoundTag *tag) +{ + // If we saved and it had no entities, and nothing has been added since skip this one + if(!lc->lastSaveHadEntities) return false; + + lc->lastSaveHadEntities = false; + ListTag *entityTags = new ListTag(); + +#ifdef _ENTITIES_RW_SECTION + EnterCriticalRWSection(&lc->m_csEntities, true); +#else + EnterCriticalSection(&lc->m_csEntities); +#endif + for (int i = 0; i < lc->ENTITY_BLOCKS_LENGTH; i++) + { + AUTO_VAR(itEnd, lc->entityBlocks[i]->end()); + for( vector >::iterator it = lc->entityBlocks[i]->begin(); it != itEnd; it++ ) + { + shared_ptr e = *it; + lc->lastSaveHadEntities = true; + CompoundTag *teTag = new CompoundTag(); + if (e->save(teTag)) + { + entityTags->add(teTag); + } + + } + } +#ifdef _ENTITIES_RW_SECTION + LeaveCriticalRWSection(&lc->m_csEntities, true); +#else + LeaveCriticalSection(&lc->m_csEntities); +#endif + + tag->put(L"Entities", entityTags); + + return lc->lastSaveHadEntities; +} + +void OldChunkStorage::save(LevelChunk *lc, Level *level, DataOutputStream *dos) +{ + dos->writeShort(SAVE_FILE_VERSION_NUMBER); + dos->writeInt(lc->x); + dos->writeInt(lc->z); + dos->writeLong(level->getTime()); + + PIXBeginNamedEvent(0,"Getting block data"); + lc->writeCompressedBlockData(dos); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Getting data data"); + lc->writeCompressedDataData(dos); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Getting sky and block light data"); + lc->writeCompressedSkyLightData(dos); + lc->writeCompressedBlockLightData(dos); + PIXEndNamedEvent(); + + dos->write(lc->heightmap); + dos->writeShort(lc->terrainPopulated); + dos->write(lc->getBiomes()); + + PIXBeginNamedEvent(0,"Saving entities"); + CompoundTag *tag = new CompoundTag(); +#ifndef SPLIT_SAVES + saveEntities(lc, level, tag); +#endif + + PIXBeginNamedEvent(0,"Saving tile entities"); + ListTag *tileEntityTags = new ListTag(); + + AUTO_VAR(itEnd, lc->tileEntities.end()); + for( unordered_map, TilePosKeyHash, TilePosKeyEq>::iterator it = lc->tileEntities.begin(); + it != itEnd; it++) + { + shared_ptr te = it->second; + CompoundTag *teTag = new CompoundTag(); + te->save(teTag); + tileEntityTags->add(teTag); + } + tag->put(L"TileEntities", tileEntityTags); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Saving tile tick data"); + vector *ticksInChunk = level->fetchTicksInChunk(lc, false); + if (ticksInChunk != NULL) + { + __int64 levelTime = level->getTime(); + + ListTag *tickTags = new ListTag(); + for( int i = 0; i < ticksInChunk->size(); i++ ) + { + TickNextTickData td = ticksInChunk->at(i); + CompoundTag *teTag = new CompoundTag(); + teTag->putInt(L"i", td.tileId); + teTag->putInt(L"x", td.x); + teTag->putInt(L"y", td.y); + teTag->putInt(L"z", td.z); + teTag->putInt(L"t", (int) (td.m_delay - levelTime)); + + tickTags->add(teTag); + } + tag->put(L"TileTicks", tickTags); + } + delete ticksInChunk; + PIXEndNamedEvent(); + + NbtIo::write(tag,dos); + delete tag; + PIXEndNamedEvent(); +} + +void OldChunkStorage::save(LevelChunk *lc, Level *level, CompoundTag *tag) +{ + level->checkSession(); + tag->putInt(L"xPos", lc->x); + tag->putInt(L"zPos", lc->z); + tag->putLong(L"LastUpdate", level->getTime()); + // 4J - changes here for new storage. Now have static storage for getting lighting data for block, data, and sky & block lighting. This + // wasn't required in the original version as we could just reference the information in the level itself, but with our new storage system + // the full data doesn't normally exist & so getSkyLightData/getBlockLightData etc. need somewhere to output this data. Making this static so + // that we aren't dynamically allocating memory in the server thread when writing chunks as this causes serious stalling on the main thread. + // Will be fine so long as we only actually create tags for once chunk at a time. + + // 4J Stu - As we now save on multiple threads, the static data has been moved to TLS + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); + + PIXBeginNamedEvent(0,"Getting block data"); + //static byteArray blockData = byteArray(32768); + lc->getBlockData(tls->blockData); + tag->putByteArray(L"Blocks", tls->blockData); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Getting data data"); + //static byteArray dataData = byteArray(16384); + lc->getDataData(tls->dataData); + tag->putByteArray(L"Data", tls->dataData); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Getting sky and block light data"); + //static byteArray skyLightData = byteArray(16384); + //static byteArray blockLightData = byteArray(16384); + lc->getSkyLightData(tls->skyLightData); + lc->getBlockLightData(tls->blockLightData); + tag->putByteArray(L"SkyLight", tls->skyLightData); + tag->putByteArray(L"BlockLight", tls->blockLightData); + PIXEndNamedEvent(); + + tag->putByteArray(L"HeightMap", lc->heightmap); + tag->putShort(L"TerrainPopulatedFlags", lc->terrainPopulated); // 4J - changed from "TerrainPopulated" to "TerrainPopulatedFlags" as now stores a bitfield, java stores a bool + tag->putByteArray(L"Biomes", lc->getBiomes()); + + PIXBeginNamedEvent(0,"Saving entities"); +#ifndef SPLIT_SAVES + saveEntities(lc, level, tag); +#endif + + PIXBeginNamedEvent(0,"Saving tile entities"); + ListTag *tileEntityTags = new ListTag(); + + AUTO_VAR(itEnd, lc->tileEntities.end()); + for( unordered_map, TilePosKeyHash, TilePosKeyEq>::iterator it = lc->tileEntities.begin(); + it != itEnd; it++) + { + shared_ptr te = it->second; + CompoundTag *teTag = new CompoundTag(); + te->save(teTag); + tileEntityTags->add(teTag); + } + tag->put(L"TileEntities", tileEntityTags); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Saving tile tick data"); + vector *ticksInChunk = level->fetchTicksInChunk(lc, false); + if (ticksInChunk != NULL) + { + __int64 levelTime = level->getTime(); + + ListTag *tickTags = new ListTag(); + for( int i = 0; i < ticksInChunk->size(); i++ ) + { + TickNextTickData td = ticksInChunk->at(i); + CompoundTag *teTag = new CompoundTag(); + teTag->putInt(L"i", td.tileId); + teTag->putInt(L"x", td.x); + teTag->putInt(L"y", td.y); + teTag->putInt(L"z", td.z); + teTag->putInt(L"t", (int) (td.m_delay - levelTime)); + + tickTags->add(teTag); + } + tag->put(L"TileTicks", tickTags); + } + delete ticksInChunk; + PIXEndNamedEvent(); + PIXEndNamedEvent(); +} + +void OldChunkStorage::loadEntities(LevelChunk *lc, Level *level, CompoundTag *tag) +{ + ListTag *entityTags = (ListTag *) tag->getList(L"Entities"); + if (entityTags != NULL) + { + for (int i = 0; i < entityTags->size(); i++) + { + CompoundTag *teTag = entityTags->get(i); + shared_ptr te = EntityIO::loadStatic(teTag, level); + lc->lastSaveHadEntities = true; + if (te != NULL) + { + lc->addEntity(te); + } + } + } + + ListTag *tileEntityTags = (ListTag *) tag->getList(L"TileEntities"); + if (tileEntityTags != NULL) + { + for (int i = 0; i < tileEntityTags->size(); i++) + { + CompoundTag *teTag = tileEntityTags->get(i); + shared_ptr te = TileEntity::loadStatic(teTag); + if (te != NULL) + { + lc->addTileEntity(te); + } + } + } +} + +LevelChunk *OldChunkStorage::load(Level *level, DataInputStream *dis) +{ + short version = dis->readShort(); + int x = dis->readInt(); + int z = dis->readInt(); + int time = dis->readLong(); + + LevelChunk *levelChunk = new LevelChunk(level, x, z); + + levelChunk->readCompressedBlockData(dis); + levelChunk->readCompressedDataData(dis); + levelChunk->readCompressedSkyLightData(dis); + levelChunk->readCompressedBlockLightData(dis); + + dis->readFully(levelChunk->heightmap); + + levelChunk->terrainPopulated = dis->readShort(); + // If all neighbours have been post-processed, then we should have done the post-post-processing now. Check that this is set as if it isn't then we won't be able + // to send network data for chunks, and we won't ever try and set it again as all the directional flags are now already set - should only be an issue for old maps + // before this flag was added. + if( ( levelChunk->terrainPopulated & LevelChunk::sTerrainPopulatedAllNeighbours ) == LevelChunk::sTerrainPopulatedAllNeighbours ) + { + levelChunk->terrainPopulated |= LevelChunk::sTerrainPostPostProcessed; + } + + dis->readFully(levelChunk->biomes); + + CompoundTag *tag = NbtIo::read(dis); + + loadEntities(levelChunk, level, tag); + + if (tag->contains(L"TileTicks")) + { + ListTag *tileTicks = (ListTag *) tag->getList(L"TileTicks"); + + if (tileTicks != NULL) + { + for (int i = 0; i < tileTicks->size(); i++) + { + CompoundTag *teTag = tileTicks->get(i); + + level->forceAddTileTick(teTag->getInt(L"x"), teTag->getInt(L"y"), teTag->getInt(L"z"), teTag->getInt(L"i"), teTag->getInt(L"t")); + } + } + } + + delete tag; + + return levelChunk; +} + +LevelChunk *OldChunkStorage::load(Level *level, CompoundTag *tag) +{ + int x = tag->getInt(L"xPos"); + int z = tag->getInt(L"zPos"); + + LevelChunk *levelChunk = new LevelChunk(level, x, z); + // 4J - the original code uses the data in the tag directly, but this is now just used as a source when creating the compressed data, so + // we need to free up the data in the tag once we are done + levelChunk->setBlockData(tag->getByteArray(L"Blocks")); + delete [] tag->getByteArray(L"Blocks").data; + // levelChunk->blocks = tag->getByteArray(L"Blocks"); + + // 4J - the original code uses the data in the tag directly, but this is now just used as a source when creating the compressed data, so + // we need to free up the data in the tag once we are done + levelChunk->setDataData(tag->getByteArray(L"Data")); + delete [] tag->getByteArray(L"Data").data; + + // 4J - changed to use our new methods for accessing lighting + levelChunk->setSkyLightData(tag->getByteArray(L"SkyLight")); + levelChunk->setBlockLightData(tag->getByteArray(L"BlockLight")); + + // In the original code (commented out below) constructing DataLayers from these arrays uses the data directly and so it doesn't need deleted. The new + // setSkyLightData/setBlockLightData take a copy of the data so we need to delete the local one now + delete [] tag->getByteArray(L"SkyLight").data; + delete [] tag->getByteArray(L"BlockLight").data; + + // levelChunk->skyLight = new DataLayer(tag->getByteArray(L"SkyLight"), level->depthBits); + // levelChunk->blockLight = new DataLayer(tag->getByteArray(L"BlockLight"), level->depthBits); + + delete [] levelChunk->heightmap.data; + levelChunk->heightmap = tag->getByteArray(L"HeightMap"); + // 4J - TerrainPopulated was a bool (java), then changed to be a byte bitfield, then replaced with TerrainPopulatedShort to store a wider bitfield + if( tag->get(L"TerrainPopulated") ) + { + // Java bool type or byte bitfield + levelChunk->terrainPopulated = tag->getByte(L"TerrainPopulated"); + if( levelChunk->terrainPopulated >= 1 ) levelChunk->terrainPopulated = LevelChunk::sTerrainPopulatedAllNeighbours | LevelChunk::sTerrainPostPostProcessed; // Convert from old bool type to new bitfield + } + else + { + // New style short + levelChunk->terrainPopulated = tag->getShort(L"TerrainPopulatedFlags"); + // If all neighbours have been post-processed, then we should have done the post-post-processing now. Check that this is set as if it isn't then we won't be able + // to send network data for chunks, and we won't ever try and set it again as all the directional flags are now already set - should only be an issue for old maps + // before this flag was added. + if( ( levelChunk->terrainPopulated & LevelChunk::sTerrainPopulatedAllNeighbours ) == LevelChunk::sTerrainPopulatedAllNeighbours ) + { + levelChunk->terrainPopulated |= LevelChunk::sTerrainPostPostProcessed; + } + } + +#if 0 + // 4J - removed - we shouldn't need this any more + if (!levelChunk->data->isValid()) + { + levelChunk->data = new DataLayer(LevelChunk::BLOCKS_LENGTH, level->depthBits); // 4J - BLOCKS_LENGTH was levelChunk->blocks.length + } +#endif + + // 4J removed - we shouldn't need this any more +#if 0 + if (levelChunk->heightmap.data == NULL || !levelChunk->skyLight->isValid()) + { + static int chunksUpdated = 0; + delete [] levelChunk->heightmap.data; + levelChunk->heightmap = byteArray(16 * 16); + delete levelChunk->skyLight; + levelChunk->skyLight = new DataLayer(levelChunk->blocks.length, level->depthBits); + levelChunk->recalcHeightmap(); + } + + if (!levelChunk->blockLight->isValid()) + { + delete levelChunk->blockLight; + levelChunk->blockLight = new DataLayer(levelChunk->blocks.length, level->depthBits); + levelChunk->recalcBlockLights(); + } +#endif + + if (tag->contains(L"Biomes")) + { + levelChunk->setBiomes(tag->getByteArray(L"Biomes")); + } + + loadEntities(levelChunk, level, tag); + + if (tag->contains(L"TileTicks")) + { + ListTag *tileTicks = (ListTag *) tag->getList(L"TileTicks"); + + if (tileTicks != NULL) + { + for (int i = 0; i < tileTicks->size(); i++) + { + CompoundTag *teTag = tileTicks->get(i); + + level->forceAddTileTick(teTag->getInt(L"x"), teTag->getInt(L"y"), teTag->getInt(L"z"), teTag->getInt(L"i"), teTag->getInt(L"t")); + } + } + } + + return levelChunk; +} + +void OldChunkStorage::tick() +{ +} + +void OldChunkStorage::flush() +{ +} + +void OldChunkStorage::saveEntities(Level *level, LevelChunk *levelChunk) +{ +} diff --git a/Minecraft.World/OldChunkStorage.h b/Minecraft.World/OldChunkStorage.h new file mode 100644 index 00000000..a77b102c --- /dev/null +++ b/Minecraft.World/OldChunkStorage.h @@ -0,0 +1,57 @@ +#pragma once +#include "ChunkStorage.h" +#include "LevelChunk.h" +#include "File.h" +#include "CompoundTag.h" +#include "com.mojang.nbt.h" + +class Level; + +class OldChunkStorage : public ChunkStorage +{ +private: + // 4J added so we can have separate storage arrays for different threads + class ThreadStorage + { + public: + byteArray blockData; + byteArray dataData; + byteArray skyLightData; + byteArray blockLightData; + + ThreadStorage(); + ~ThreadStorage(); + }; + static DWORD tlsIdx; + static ThreadStorage *tlsDefault; +public: + // Each new thread that needs to use Compression will need to call one of the following 2 functions, to either create its own + // local storage, or share the default storage already allocated by the main thread + static void CreateNewThreadStorage(); + static void UseDefaultThreadStorage(); + static void ReleaseThreadStorage(); + +private: + File dir; + bool create; + +public: + OldChunkStorage(File dir, bool create); +private: + File getFile(int x, int z); + LevelChunk *load(Level *level, int x, int z); + +public: + virtual void save(Level *level, LevelChunk *levelChunk); + + static bool saveEntities(LevelChunk *lc, Level *level, CompoundTag *tag); // 4J Added + static void save(LevelChunk *lc, Level *level, DataOutputStream *dos); // 4J Added + static void save(LevelChunk *lc, Level *level, CompoundTag *tag); + static void loadEntities(LevelChunk *lc, Level *level, CompoundTag *tag); + static LevelChunk *load(Level *level, CompoundTag *tag); + static LevelChunk *load(Level *level, DataInputStream *dis); // 4J Added + + virtual void tick(); + virtual void flush(); + virtual void saveEntities(Level *level, LevelChunk *levelChunk); +}; diff --git a/Minecraft.World/OpenDoorGoal.cpp b/Minecraft.World/OpenDoorGoal.cpp new file mode 100644 index 00000000..dbe5b410 --- /dev/null +++ b/Minecraft.World/OpenDoorGoal.cpp @@ -0,0 +1,35 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.tile.h" +#include "OpenDoorGoal.h" + +OpenDoorGoal::OpenDoorGoal(Mob *mob, bool closeDoorAfter) : DoorInteractGoal(mob) +{ + this->mob = mob; + this->closeDoor = closeDoorAfter; +} + +bool OpenDoorGoal::canContinueToUse() +{ + return closeDoor && forgetTime > 0 && DoorInteractGoal::canContinueToUse(); +} + +void OpenDoorGoal::start() +{ + forgetTime = 20; + doorTile->setOpen(mob->level, doorX, doorY, doorZ, true); +} + +void OpenDoorGoal::stop() +{ + if (closeDoor) + { + doorTile->setOpen(mob->level, doorX, doorY, doorZ, false); + } +} + +void OpenDoorGoal::tick() +{ + --forgetTime; + DoorInteractGoal::tick(); +} \ No newline at end of file diff --git a/Minecraft.World/OpenDoorGoal.h b/Minecraft.World/OpenDoorGoal.h new file mode 100644 index 00000000..ff27367a --- /dev/null +++ b/Minecraft.World/OpenDoorGoal.h @@ -0,0 +1,18 @@ +#pragma once + +#include "DoorInteractGoal.h" + +class OpenDoorGoal : public DoorInteractGoal +{ +private: + bool closeDoor; + int forgetTime; + +public: + OpenDoorGoal(Mob *mob, bool closeDoorAfter); + + virtual bool canContinueToUse(); + virtual void start(); + virtual void stop(); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/OreFeature.cpp b/Minecraft.World/OreFeature.cpp new file mode 100644 index 00000000..75482a57 --- /dev/null +++ b/Minecraft.World/OreFeature.cpp @@ -0,0 +1,127 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "OreFeature.h" + +void OreFeature::_init(int tile, int count, int targetTile) +{ + this->tile = tile; + this->count = count; + this->targetTile = targetTile; +} + +OreFeature::OreFeature(int tile, int count) +{ + _init(tile, count, Tile::rock_Id); +} + +OreFeature::OreFeature(int tile, int count, int targetTile) +{ + _init(tile, count, targetTile); +} + +bool OreFeature::place(Level *level, Random *random, int x, int y, int z) +{ + PIXBeginNamedEvent(0,"Place Ore Feature"); + float dir = random->nextFloat() * PI; + + double x0 = x + 8 + Mth::sin(dir) * count / 8; + double x1 = x + 8 - Mth::sin(dir) * count / 8; + double z0 = z + 8 + Mth::cos(dir) * count / 8; + double z1 = z + 8 - Mth::cos(dir) * count / 8; + + double y0 = y + random->nextInt(3) - 2; + double y1 = y + random->nextInt(3) - 2; + + bool collisionsExpected = false; + + LevelGenerationOptions *levelGenOptions = NULL; + if( app.getLevelGenerationOptions() != NULL ) + { + levelGenOptions = app.getLevelGenerationOptions(); + + // 4J Stu - Optimise schematic intersection checks by first checking the max possible bounding box of this place call + int minX = x0 - 1; + int minY = y0 - 1; + int minZ = z0 - 1; + + double maxss = count / 16; + double maxr = (Mth::sin(PI) + 1) * maxss + 1; + double maxhr = (Mth::sin(PI) + 1) * maxss + 1; + int maxX = Mth::floor(x1 + maxr / 2); + int maxY = Mth::floor(y1 + maxhr / 2); + int maxZ = Mth::floor(z1 + maxr / 2); + + collisionsExpected = levelGenOptions->checkIntersects(minX, minY, minZ, maxX, maxY, maxZ); + } + + for (int d = 0; d <= count; d++) + { + double xx = x0 + (x1 - x0) * d / count; + double yy = y0 + (y1 - y0) * d / count; + double zz = z0 + (z1 - z0) * d / count; + + double ss = random->nextDouble() * count / 16; + double r = (Mth::sin(d * PI / count) + 1) * ss + 1; + double hr = (Mth::sin(d * PI / count) + 1) * ss + 1; + + int xt0 = Mth::floor(xx - r / 2); + int yt0 = Mth::floor(yy - hr / 2); + int zt0 = Mth::floor(zz - r / 2); + + int xt1 = Mth::floor(xx + r / 2); + int yt1 = Mth::floor(yy + hr / 2); + int zt1 = Mth::floor(zz + r / 2); + + // 4J Stu Added to stop ore features generating areas previously place by game rule generation + if(collisionsExpected && levelGenOptions != NULL) + { + bool intersects = levelGenOptions->checkIntersects(xt0, yt0, zt0, xt1, yt1, zt1); + if(intersects) + { + //app.DebugPrintf("Skipping ore feature generation as it overlaps a game rule structure\n"); + continue; + } + } + + // A large % of ore placement is entirely into the air. Attempt to identify some of these early, by check the corners + // of the area we are placing in to see if we are going to (very probably) be entirely above the height stored in the heightmap + + bool earlyReject = true; + if ( level->getHeightmap(xt0, zt0) >= yt0 ) earlyReject = false; + else if( level->getHeightmap(xt1, zt0) >= yt0 ) earlyReject = false; + else if( level->getHeightmap(xt0, zt1) >= yt0 ) earlyReject = false; + else if( level->getHeightmap(xt1, zt1) >= yt0 ) earlyReject = false; + + if( earlyReject ) continue; + + for (int x2 = xt0; x2 <= xt1; x2++) + { + double xd = ((x2 + 0.5) - xx) / (r / 2); + if (xd * xd < 1) + { + for (int y2 = yt0; y2 <= yt1; y2++) + { + double yd = ((y2 + 0.5) - yy) / (hr / 2); + if (xd * xd + yd * yd < 1) + { + for (int z2 = zt0; z2 <= zt1; z2++) + { + double zd = ((z2 + 0.5) - zz) / (r / 2); + if (xd * xd + yd * yd + zd * zd < 1) + { + if (level->getTile(x2, y2, z2) == targetTile) + { + level->setTileNoUpdateNoLightCheck(x2, y2, z2, tile); // 4J changed from setTileNoUpdate + } + } + } + } + } + } + } + } + + PIXEndNamedEvent(); + return true; +} \ No newline at end of file diff --git a/Minecraft.World/OreFeature.h b/Minecraft.World/OreFeature.h new file mode 100644 index 00000000..4103f962 --- /dev/null +++ b/Minecraft.World/OreFeature.h @@ -0,0 +1,19 @@ +#pragma once +#include "Feature.h" + +class Level; + +class OreFeature : public Feature +{ +private: + int tile; + int count; + int targetTile; + + void _init(int tile, int count, int targetTile); +public: + OreFeature (int tile, int count); + OreFeature(int tile, int count, int targetTile); + + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/OreRecipies.cpp b/Minecraft.World/OreRecipies.cpp new file mode 100644 index 00000000..f18e985c --- /dev/null +++ b/Minecraft.World/OreRecipies.cpp @@ -0,0 +1,75 @@ +// package net.minecraft.world.item.crafting; +// +// import net.minecraft.world.item.DyePowderItem; +// import net.minecraft.world.item.Item; +// import net.minecraft.world.item.ItemInstance; +// import net.minecraft.world.level.tile.Tile; + +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "DyePowderItem.h" +#include "Tile.h" +#include "Recipy.h" +#include "Recipes.h" +#include "OreRecipies.h" + + +/* + private Object[][] map = { + { + Tile.goldBlock, new ItemInstance(Item.goldIngot, 9) + }, { + Tile.ironBlock, new ItemInstance(Item.ironIngot, 9) + }, { + Tile.diamondBlock, new ItemInstance(Item.diamond, 9) + }, { + Tile.lapisBlock, new ItemInstance(Item.dye_powder, 9, DyePowderItem.BLUE) + }, + }; +*/ + +void OreRecipies::_init() +{ + map = new vector [MAX_ORE_RECIPES]; + + ADD_OBJECT(map[0],Tile::goldBlock); + ADD_OBJECT(map[0],new ItemInstance(Item::goldIngot, 9)); + + ADD_OBJECT(map[1],Tile::ironBlock); + ADD_OBJECT(map[1],new ItemInstance(Item::ironIngot, 9)); + + ADD_OBJECT(map[2],Tile::diamondBlock); + ADD_OBJECT(map[2],new ItemInstance(Item::diamond, 9)); + + ADD_OBJECT(map[3],Tile::emeraldBlock); + ADD_OBJECT(map[3],new ItemInstance(Item::emerald, 9)); + + ADD_OBJECT(map[4],Tile::lapisBlock); + ADD_OBJECT(map[4],new ItemInstance(Item::dye_powder, 9, DyePowderItem::BLUE)); +} +void OreRecipies::addRecipes(Recipes *r) +{ + + for (int i = 0; i < MAX_ORE_RECIPES; i++) + { + Tile *from = (Tile*) map[i].at(0)->tile; + ItemInstance *to = (ItemInstance*) map[i].at(1)->iteminstance; + r->addShapedRecipy(new ItemInstance(from), // + L"sssczg", + L"###", // + L"###", // + L"###", // + + L'#', to, + L'D'); + + r->addShapedRecipy(to, // + L"sctg", + L"#", // + + L'#', from, + L'D'); + } +} + + diff --git a/Minecraft.World/OreRecipies.h b/Minecraft.World/OreRecipies.h new file mode 100644 index 00000000..fa226a8a --- /dev/null +++ b/Minecraft.World/OreRecipies.h @@ -0,0 +1,17 @@ +#pragma once + +#define MAX_ORE_RECIPES 5 + +class OreRecipies +{ +public: + // 4J - added for common ctor code + void _init(); + OreRecipies() {_init();} + +private: + vector *map; + +public: + void addRecipes(Recipes *r); +}; \ No newline at end of file diff --git a/Minecraft.World/OreTile.cpp b/Minecraft.World/OreTile.cpp new file mode 100644 index 00000000..bd656541 --- /dev/null +++ b/Minecraft.World/OreTile.cpp @@ -0,0 +1,77 @@ +#include "stdafx.h" +#include "OreTile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" + +OreTile::OreTile(int id) : Tile(id, Material::stone) +{ +} + +int OreTile::getResource(int data, Random *random, int playerBonusLevel) +{ + if (id == Tile::coalOre_Id) return Item::coal_Id; + if (id == Tile::diamondOre_Id) return Item::diamond_Id; + if (id == Tile::lapisOre_Id) return Item::dye_powder_Id; + if (id == Tile::emeraldOre_Id) return Item::emerald_Id; + if (id == Tile::netherQuartz_Id) return Item::netherQuartz_Id; + return id; +} + +int OreTile::getResourceCount(Random *random) +{ + if (id == Tile::lapisOre_Id) return 4 + random->nextInt(5); + return 1; +} + +int OreTile::getResourceCountForLootBonus(int bonusLevel, Random *random) +{ + if (bonusLevel > 0 && id != getResource(0, random, bonusLevel)) + { + int bonus = random->nextInt(bonusLevel + 2) - 1; + if (bonus < 0) + { + bonus = 0; + } + return getResourceCount(random) * (bonus + 1); + } + return getResourceCount(random); +} + +void OreTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel) +{ + Tile::spawnResources(level, x, y, z, data, odds, playerBonusLevel); + + // also spawn experience if the block is broken + if (getResource(data, level->random, playerBonusLevel) != id) + { + int magicCount = 0; + if (id == Tile::coalOre_Id) + { + magicCount = Mth::nextInt(level->random, 0, 2); + } + else if (id == Tile::diamondOre_Id) + { + magicCount = Mth::nextInt(level->random, 3, 7); + } + else if (id == Tile::emeraldOre_Id) + { + magicCount = Mth::nextInt(level->random, 3, 7); + } + else if (id == Tile::lapisOre_Id) + { + magicCount = Mth::nextInt(level->random, 2, 5); + } + else if (id == Tile::netherQuartz_Id) + { + magicCount = Mth::nextInt(level->random, 2, 5); + } + popExperience(level, x, y, z, magicCount); + } +} + +int OreTile::getSpawnResourcesAuxValue(int data) +{ + // lapis spawns blue dye + if (id == Tile::lapisOre_Id) return DyePowderItem::BLUE; + return 0; +} \ No newline at end of file diff --git a/Minecraft.World/OreTile.h b/Minecraft.World/OreTile.h new file mode 100644 index 00000000..061bc430 --- /dev/null +++ b/Minecraft.World/OreTile.h @@ -0,0 +1,17 @@ +#pragma once +#include "Tile.h" + +class Random; + +class OreTile : public Tile +{ +public: + + OreTile(int id); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int getResourceCount(Random *random); + virtual int getResourceCountForLootBonus(int bonusLevel, Random *random); + virtual void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel); +protected: + virtual int getSpawnResourcesAuxValue(int data); +}; \ No newline at end of file diff --git a/Minecraft.World/OutputStream.h b/Minecraft.World/OutputStream.h new file mode 100644 index 00000000..f6d9bcf7 --- /dev/null +++ b/Minecraft.World/OutputStream.h @@ -0,0 +1,14 @@ +#pragma once +// 4J Stu - Represents Java standard lib abstract + +class OutputStream +{ +public: + virtual ~OutputStream() {} + + virtual void write(unsigned int b) = 0; + virtual void write(byteArray b) = 0; + virtual void write(byteArray b, unsigned int offset, unsigned int length) = 0; + virtual void close() = 0; + virtual void flush() = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/OwnerHurtByTargetGoal.cpp b/Minecraft.World/OwnerHurtByTargetGoal.cpp new file mode 100644 index 00000000..e576f575 --- /dev/null +++ b/Minecraft.World/OwnerHurtByTargetGoal.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.animal.h" +#include "OwnerHurtByTargetGoal.h" + +OwnerHurtByTargetGoal::OwnerHurtByTargetGoal(TamableAnimal *tameAnimal) : TargetGoal(tameAnimal, 32, false) +{ + this->tameAnimal = tameAnimal; + setRequiredControlFlags(TargetGoal::TargetFlag); +} + +bool OwnerHurtByTargetGoal::canUse() +{ + if (!tameAnimal->isTame()) return false; + shared_ptr owner = tameAnimal->getOwner(); + if (owner == NULL) return false; + ownerLastHurtBy = weak_ptr(owner->getLastHurtByMob()); + return canAttack(ownerLastHurtBy.lock(), false); +} + +void OwnerHurtByTargetGoal::start() +{ + mob->setTarget(ownerLastHurtBy.lock()); + TargetGoal::start(); +} \ No newline at end of file diff --git a/Minecraft.World/OwnerHurtByTargetGoal.h b/Minecraft.World/OwnerHurtByTargetGoal.h new file mode 100644 index 00000000..48b30313 --- /dev/null +++ b/Minecraft.World/OwnerHurtByTargetGoal.h @@ -0,0 +1,18 @@ +#pragma once + +#include "TargetGoal.h" + +class TamableAnimal; + +class OwnerHurtByTargetGoal : public TargetGoal +{ +private: + TamableAnimal *tameAnimal; // Owner of this goal + weak_ptr ownerLastHurtBy; + +public: + OwnerHurtByTargetGoal(TamableAnimal *tameAnimal); + + bool canUse(); + void start(); +}; \ No newline at end of file diff --git a/Minecraft.World/OwnerHurtTargetGoal.cpp b/Minecraft.World/OwnerHurtTargetGoal.cpp new file mode 100644 index 00000000..c6c367d7 --- /dev/null +++ b/Minecraft.World/OwnerHurtTargetGoal.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.animal.h" +#include "OwnerHurtTargetGoal.h" + +OwnerHurtTargetGoal::OwnerHurtTargetGoal(TamableAnimal *tameAnimal) : TargetGoal(tameAnimal, 32, false) +{ + this->tameAnimal = tameAnimal; + setRequiredControlFlags(TargetGoal::TargetFlag); +} + +bool OwnerHurtTargetGoal::canUse() +{ + if (!tameAnimal->isTame()) return false; + shared_ptr owner = tameAnimal->getOwner(); + if (owner == NULL) return false; + ownerLastHurt = weak_ptr(owner->getLastHurtMob()); + return canAttack(ownerLastHurt.lock(), false); +} + +void OwnerHurtTargetGoal::start() +{ + mob->setTarget(ownerLastHurt.lock()); + TargetGoal::start(); +} \ No newline at end of file diff --git a/Minecraft.World/OwnerHurtTargetGoal.h b/Minecraft.World/OwnerHurtTargetGoal.h new file mode 100644 index 00000000..71079645 --- /dev/null +++ b/Minecraft.World/OwnerHurtTargetGoal.h @@ -0,0 +1,18 @@ +#pragma once + +#include "TargetGoal.h" + +class TamableAnimal; + +class OwnerHurtTargetGoal : public TargetGoal +{ +private: + TamableAnimal *tameAnimal; // Owner of this goal + weak_ptr ownerLastHurt; + +public: + OwnerHurtTargetGoal(TamableAnimal *tameAnimal); + + bool canUse(); + void start(); +}; \ No newline at end of file diff --git a/Minecraft.World/OxygenEnchantment.cpp b/Minecraft.World/OxygenEnchantment.cpp new file mode 100644 index 00000000..c7458ac9 --- /dev/null +++ b/Minecraft.World/OxygenEnchantment.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "OxygenEnchantment.h" + +OxygenEnchantment::OxygenEnchantment(int id, int frequency) : Enchantment(id, frequency, EnchantmentCategory::armor_head) +{ + setDescriptionId(IDS_ENCHANTMENT_OXYGEN); +} + +int OxygenEnchantment::getMinCost(int level) +{ + return 10 * level; +} + +int OxygenEnchantment::getMaxCost(int level) +{ + return getMinCost(level) + 30; +} + +int OxygenEnchantment::getMaxLevel() +{ + return 3; +} \ No newline at end of file diff --git a/Minecraft.World/OxygenEnchantment.h b/Minecraft.World/OxygenEnchantment.h new file mode 100644 index 00000000..ef57652c --- /dev/null +++ b/Minecraft.World/OxygenEnchantment.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Enchantment.h" + +class OxygenEnchantment : public Enchantment +{ +public: + OxygenEnchantment(int id, int frequency); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); +}; \ No newline at end of file diff --git a/Minecraft.World/Ozelot.cpp b/Minecraft.World/Ozelot.cpp new file mode 100644 index 00000000..3c05f357 --- /dev/null +++ b/Minecraft.World/Ozelot.cpp @@ -0,0 +1,333 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.goal.target.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.phys.h" +#include "SynchedEntityData.h" +#include "StringHelpers.h" +#include "..\Minecraft.Client\Textures.h" +#include "GenericStats.h" +#include "Ozelot.h" + +const float Ozelot::SNEAK_SPEED = 0.18f; +const float Ozelot::WALK_SPEED = 0.23f; +const float Ozelot::FOLLOW_SPEED = 0.3f; +const float Ozelot::SPRINT_SPEED = 0.4f; + +const int Ozelot::DATA_TYPE_ID = 18; + +const int Ozelot::TYPE_OZELOT = 0; +const int Ozelot::TYPE_BLACK = 1; +const int Ozelot::TYPE_RED = 2; +const int Ozelot::TYPE_SIAMESE = 3; + +Ozelot::Ozelot(Level *level) : TamableAnimal(level) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_OZELOT; // "/mob/ozelot.png"; + this->setSize(0.6f, 0.8f); + + getNavigation()->setAvoidWater(true); + goalSelector.addGoal(1, new FloatGoal(this)); + goalSelector.addGoal(2, sitGoal, false); + goalSelector.addGoal(3, temptGoal = new TemptGoal(this, SNEAK_SPEED, Item::fish_raw_Id, true), false); + goalSelector.addGoal(4, new AvoidPlayerGoal(this, typeid(Player), 16, WALK_SPEED, SPRINT_SPEED)); + goalSelector.addGoal(5, new FollowOwnerGoal(this, FOLLOW_SPEED, 10, 5)); + goalSelector.addGoal(6, new OcelotSitOnTileGoal(this, SPRINT_SPEED)); + goalSelector.addGoal(7, new LeapAtTargetGoal(this, 0.3f)); + goalSelector.addGoal(8, new OzelotAttackGoal(this)); + goalSelector.addGoal(9, new BreedGoal(this, WALK_SPEED)); + goalSelector.addGoal(10, new RandomStrollGoal(this, WALK_SPEED)); + goalSelector.addGoal(11, new LookAtPlayerGoal(this, typeid(Player), 10)); + + targetSelector.addGoal(1, new NonTameRandomTargetGoal(this, typeid(Chicken), 14, 750, false)); +} + +void Ozelot::defineSynchedData() +{ + TamableAnimal::defineSynchedData(); + + entityData->define(DATA_TYPE_ID, (byte) TYPE_OZELOT); +} + +void Ozelot::serverAiMobStep() +{ + if (getMoveControl()->hasWanted()) + { + float speed = getMoveControl()->getSpeed(); + if (speed == SNEAK_SPEED) + { + setSneaking(true); + setSprinting(false); + } + else if (speed == SPRINT_SPEED) + { + setSneaking(false); + setSprinting(true); + } + else + { + setSneaking(false); + setSprinting(false); + } + } + else + { + setSneaking(false); + setSprinting(false); + } +} + +bool Ozelot::removeWhenFarAway() +{ + return Animal::removeWhenFarAway() && !isTame(); +} + +int Ozelot::getTexture() +{ + switch (getCatType()) + { + case TYPE_OZELOT: + return TN_MOB_OZELOT; //"/mob/ozelot.png"; + case TYPE_BLACK: + return TN_MOB_CAT_BLACK; //"/mob/cat_black.png"; + case TYPE_RED: + return TN_MOB_CAT_RED; //"/mob/cat_red.png"; + case TYPE_SIAMESE: + return TN_MOB_CAT_SIAMESE; //"/mob/cat_siamese.png"; + } + return TamableAnimal::getTexture(); +} + +bool Ozelot::useNewAi() +{ + return true; +} + +int Ozelot::getMaxHealth() +{ + return 10; +} + +void Ozelot::causeFallDamage(float distance) +{ + // do nothing +} + +void Ozelot::addAdditonalSaveData(CompoundTag *tag) +{ + TamableAnimal::addAdditonalSaveData(tag); + tag->putInt(L"CatType", getCatType()); +} + +void Ozelot::readAdditionalSaveData(CompoundTag *tag) +{ + TamableAnimal::readAdditionalSaveData(tag); + if(isTame()) + { + setCatType(tag->getInt(L"CatType")); + } + else + { + setCatType(TYPE_OZELOT); + } +} + +int Ozelot::getAmbientSound() +{ + if (isTame()) + { + if (isInLove()) + { + return eSoundType_MOB_CAT_PURR; + } + if (random->nextInt(4) == 0) + { + return eSoundType_MOB_CAT_PURREOW; + } + return eSoundType_MOB_CAT_MEOW; + } + + return -1; +} + +int Ozelot::getHurtSound() +{ + return eSoundType_MOB_CAT_HITT; +} + +int Ozelot::getDeathSound() +{ + return eSoundType_MOB_CAT_HITT; +} + +float Ozelot::getSoundVolume() +{ + return 0.4f; +} + +int Ozelot::getDeathLoot() +{ + return Item::leather_Id; +} + +bool Ozelot::doHurtTarget(shared_ptr target) +{ + return target->hurt(DamageSource::mobAttack(dynamic_pointer_cast(shared_from_this())), 3); +} + +bool Ozelot::hurt(DamageSource *source, int dmg) +{ + sitGoal->wantToSit(false); + return TamableAnimal::hurt(source, dmg); +} + +void Ozelot::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ +} + +bool Ozelot::interact(shared_ptr player) +{ + shared_ptr item = player->inventory->getSelected(); + if (isTame()) + { + if (equalsIgnoreCase(player->getUUID(), getOwnerUUID())) + { + if (!level->isClientSide && !isFood(item)) + { + sitGoal->wantToSit(!isSitting()); + } + } + } + else + { + if (temptGoal->isRunning() && item != NULL && item->id == Item::fish_raw_Id && player->distanceToSqr(shared_from_this()) < 3 * 3) + { + // 4J-PB - don't lose the fish in creative mode + if (!player->abilities.instabuild) item->count--; + if (item->count <= 0) + { + player->inventory->setItem(player->inventory->selected, nullptr); + } + + if (!level->isClientSide) + { + if (random->nextInt(3) == 0) + { + setTame(true); + + // 4J-JEV, hook for durango event. + player->awardStat(GenericStats::tamedEntity(eTYPE_OZELOT),GenericStats::param_tamedEntity(eTYPE_OZELOT)); + + setCatType(1 + level->random->nextInt(3)); + setOwnerUUID(player->getUUID()); + spawnTamingParticles(true); + sitGoal->wantToSit(true); + level->broadcastEntityEvent(shared_from_this(), EntityEvent::TAMING_SUCCEEDED); + } + else + { + spawnTamingParticles(false); + level->broadcastEntityEvent(shared_from_this(), EntityEvent::TAMING_FAILED); + } + } + return true; + } + } + return TamableAnimal::interact(player); +} + +shared_ptr Ozelot::getBreedOffspring(shared_ptr target) +{ + // 4J - added limit to number of animals that can be bred + if( level->canCreateMore( GetType(), Level::eSpawnType_Breed) ) + { + shared_ptr offspring = shared_ptr( new Ozelot(level) ); + if (isTame()) + { + offspring->setOwnerUUID(getOwnerUUID()); + offspring->setTame(true); + offspring->setCatType(getCatType()); + } + return offspring; + } + else + { + return nullptr; + } +} + +bool Ozelot::isFood(shared_ptr itemInstance) +{ + return itemInstance != NULL && itemInstance->id == Item::fish_raw_Id; +} + +bool Ozelot::canMate(shared_ptr animal) +{ + if (animal == shared_from_this()) return false; + if (!isTame()) return false; + + shared_ptr partner = dynamic_pointer_cast(animal); + if (partner == NULL) return false; + if (!partner->isTame()) return false; + + return isInLove() && partner->isInLove(); +} + +int Ozelot::getCatType() +{ + return entityData->getByte(DATA_TYPE_ID); +} + +void Ozelot::setCatType(int type) +{ + entityData->set(DATA_TYPE_ID, (byte) type); +} + +bool Ozelot::canSpawn() +{ + // artificially make ozelots more rare + if (level->random->nextInt(3) == 0) + { + return false; + } + if (level->isUnobstructed(bb) && level->getCubes(shared_from_this(), bb)->empty() && !level->containsAnyLiquid(bb)) + { + int xt = Mth::floor(x); + int yt = Mth::floor(bb->y0); + int zt = Mth::floor(z); + if (yt < level->seaLevel) + { + return false; + } + + int tile = level->getTile(xt, yt - 1, zt); + if (tile == Tile::grass_Id || tile == Tile::leaves_Id) + { + return true; + } + } + return false; +} + +wstring Ozelot::getAName() +{ + if (isTame()) + { + return L"entity.Cat.name"; + } + return TamableAnimal::getAName(); +} diff --git a/Minecraft.World/Ozelot.h b/Minecraft.World/Ozelot.h new file mode 100644 index 00000000..c763f3bc --- /dev/null +++ b/Minecraft.World/Ozelot.h @@ -0,0 +1,76 @@ +#pragma once + +#include "TamableAnimal.h" + +class TemptGoal; + +class Ozelot : public TamableAnimal +{ +public: + eINSTANCEOF GetType() { return eTYPE_OZELOT; } + static Entity *create(Level *level) { return new Ozelot(level); } + +public: + static const float SNEAK_SPEED; + static const float WALK_SPEED; + static const float FOLLOW_SPEED; + static const float SPRINT_SPEED; + +private: + static const int DATA_TYPE_ID; + + static const int TYPE_OZELOT; + static const int TYPE_BLACK; + static const int TYPE_RED; + static const int TYPE_SIAMESE; + + TemptGoal *temptGoal; + +public: + Ozelot(Level *level); + +protected: + virtual void defineSynchedData(); + +public: + virtual void serverAiMobStep(); + +protected: + virtual bool removeWhenFarAway(); + +public: + virtual int getTexture(); + virtual bool useNewAi(); + virtual int getMaxHealth(); + +protected: + virtual void causeFallDamage(float distance); + +public: + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +protected: + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual float getSoundVolume(); + virtual int getDeathLoot(); + +public: + virtual bool doHurtTarget(shared_ptr target); + virtual bool hurt(DamageSource *source, int dmg); + +protected: + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + +public: + virtual bool interact(shared_ptr player); + virtual shared_ptr getBreedOffspring(shared_ptr target); + virtual bool isFood(shared_ptr itemInstance); + virtual bool canMate(shared_ptr animal); + virtual int getCatType(); + virtual void setCatType(int type); + virtual bool canSpawn(); + virtual wstring getAName(); +}; \ No newline at end of file diff --git a/Minecraft.World/OzelotAttackGoal.cpp b/Minecraft.World/OzelotAttackGoal.cpp new file mode 100644 index 00000000..27ca2b5c --- /dev/null +++ b/Minecraft.World/OzelotAttackGoal.cpp @@ -0,0 +1,61 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.phys.h" +#include "OzelotAttackGoal.h" + +OzelotAttackGoal::OzelotAttackGoal(Mob *mob) +{ + target = weak_ptr(); + attackTime = 0; + speed = 0; + trackTarget = false; + + this->mob = mob; + this->level = mob->level; + setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag); +} + +bool OzelotAttackGoal::canUse() +{ + shared_ptr bestTarget = mob->getTarget(); + if (bestTarget == NULL) return false; + target = weak_ptr(bestTarget); + return true; +} + +bool OzelotAttackGoal::canContinueToUse() +{ + if (target.lock() == NULL || !target.lock()->isAlive()) return false; + if (mob->distanceToSqr(target.lock()) > 15 * 15) return false; + return !mob->getNavigation()->isDone() || canUse(); +} + +void OzelotAttackGoal::stop() +{ + target = weak_ptr(); + mob->getNavigation()->stop(); +} + +void OzelotAttackGoal::tick() +{ + mob->getLookControl()->setLookAt(target.lock(), 30, 30); + + double meleeRadiusSqr = (mob->bbWidth * 2) * (mob->bbWidth * 2); + double distSqr = mob->distanceToSqr(target.lock()->x, target.lock()->bb->y0, target.lock()->z); + + float speed = Ozelot::WALK_SPEED; + if (distSqr > meleeRadiusSqr && distSqr < 4 * 4) speed = Ozelot::SPRINT_SPEED; + else if (distSqr < 15 * 15) speed = Ozelot::SNEAK_SPEED; + + mob->getNavigation()->moveTo(target.lock(), speed); + + attackTime = max(attackTime - 1, 0); + + if (distSqr > meleeRadiusSqr) return; + if (attackTime > 0) return; + attackTime = 20; + mob->doHurtTarget(target.lock()); +} \ No newline at end of file diff --git a/Minecraft.World/OzelotAttackGoal.h b/Minecraft.World/OzelotAttackGoal.h new file mode 100644 index 00000000..c214b722 --- /dev/null +++ b/Minecraft.World/OzelotAttackGoal.h @@ -0,0 +1,25 @@ +#pragma once + +#include "Goal.h" + +class OzelotAttackGoal : public Goal +{ +private: + Level *level; + Mob *mob; + weak_ptr target; + int attackTime; + float speed; + bool trackTarget; + +public: + OzelotAttackGoal(Mob *mob); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void stop(); + virtual void tick(); + + // 4J Added override to update ai elements when loading entity from schematics + virtual void setLevel(Level *level) { this->level = level; } +}; \ No newline at end of file diff --git a/Minecraft.World/Packet.cpp b/Minecraft.World/Packet.cpp new file mode 100644 index 00000000..0a59204c --- /dev/null +++ b/Minecraft.World/Packet.cpp @@ -0,0 +1,596 @@ +#include "stdafx.h" +#include "System.h" +#include "BasicTypeContainers.h" +#include "InputOutputStream.h" +#include "net.minecraft.network.packet.h" +#include "PacketListener.h" +#include "Packet.h" +#include "com.mojang.nbt.h" + +#ifndef _CONTENT_PACKAGE +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\Gui.h" +#endif + +void Packet::staticCtor() +{ + //nextPrint = 0; + + // 4J - Note that item IDs are now defined in virtual method for each packet type + + // 4J Stu - The values for canSendToAnyClient may not necessarily be the correct choices + map(0, true, true, true, false, typeid(KeepAlivePacket), KeepAlivePacket::create); + map(1, true, true, true, false, typeid(LoginPacket), LoginPacket::create); + map(2, true, true, true, false, typeid(PreLoginPacket), PreLoginPacket::create); + map(3, true, true, true, false, typeid(ChatPacket), ChatPacket::create); + map(4, true, false, false, true, typeid(SetTimePacket), SetTimePacket::create); + map(5, true, false, false, true, typeid(SetEquippedItemPacket), SetEquippedItemPacket::create); + map(6, true, false, true, true, typeid(SetSpawnPositionPacket), SetSpawnPositionPacket::create); + map(7, false, true, false, false, typeid(InteractPacket), InteractPacket::create); + map(8, true, false, true, true, typeid(SetHealthPacket), SetHealthPacket::create); + map(9, true, true, true, false, typeid(RespawnPacket), RespawnPacket::create); + + map(10, true, true, true, false, typeid(MovePlayerPacket), MovePlayerPacket::create); + map(11, true, true, true, true, typeid(MovePlayerPacket::Pos), MovePlayerPacket::Pos::create); + map(12, true, true, true, true, typeid(MovePlayerPacket::Rot), MovePlayerPacket::Rot::create); + map(13, true, true, true, true, typeid(MovePlayerPacket::PosRot), MovePlayerPacket::PosRot::create); + + map(14, false, true, false, false, typeid(PlayerActionPacket), PlayerActionPacket::create); + map(15, false, true, false, false, typeid(UseItemPacket), UseItemPacket::create); + map(16, false, true, false, false, typeid(SetCarriedItemPacket), SetCarriedItemPacket::create); + // 4J-PB - we need to send to any client for the sleep in bed + //map(17, true, false, false, false, EntityActionAtPositionPacket)); + map(17, true, false, true, false, typeid(EntityActionAtPositionPacket), EntityActionAtPositionPacket::create); + // 4J-PB - we need to send to any client for the wake up from sleeping + //map(18, true, true, false, false, AnimatePacket)); + map(18, true, true, true, false, typeid(AnimatePacket), AnimatePacket::create); + map(19, false, true, false, false, typeid(PlayerCommandPacket), PlayerCommandPacket::create); + + map(20, true, false, false, true, typeid(AddPlayerPacket), AddPlayerPacket::create); + map(22, true, false, true, true, typeid(TakeItemEntityPacket), TakeItemEntityPacket::create); + map(23, true, false, false, true, typeid(AddEntityPacket), AddEntityPacket::create); + map(24, true, false, false, true, typeid(AddMobPacket), AddMobPacket::create); + map(25, true, false, false, false, typeid(AddPaintingPacket), AddPaintingPacket::create); + map(26, true, false, false, false, typeid(AddExperienceOrbPacket), AddExperienceOrbPacket::create); // TODO New for 1.8.2 - Needs sendToAny? + //map(27, false, true, false, false, PlayerInputPacket)); + // 4J-PB - needs to go to any player, due to the knockback effect when a played is hit + map(28, true, false, true, true, typeid(SetEntityMotionPacket), SetEntityMotionPacket::create); + map(29, true, false, false, true, typeid(RemoveEntitiesPacket), RemoveEntitiesPacket::create); + + map(30, true, false, false, false, typeid(MoveEntityPacket), MoveEntityPacket::create); + map(31, true, false, false, true, typeid(MoveEntityPacket::Pos), MoveEntityPacket::Pos::create); + map(32, true, false, false, true, typeid(MoveEntityPacket::Rot), MoveEntityPacket::Rot::create); + map(33, true, false, false, true, typeid(MoveEntityPacket::PosRot), MoveEntityPacket::PosRot::create); + map(34, true, false, false, true, typeid(TeleportEntityPacket), TeleportEntityPacket::create); + map(35, true, false, false, false, typeid(RotateHeadPacket), RotateHeadPacket::create); + + // 4J - needs to go to any player, to create sound effect when a player is hit + map(38, true, false, true, true, typeid(EntityEventPacket), EntityEventPacket::create); + map(39, true, false, true, false, typeid(SetRidingPacket), SetRidingPacket::create); + map(40, true, false, true, true, typeid(SetEntityDataPacket), SetEntityDataPacket::create); + map(41, true, false, true, false, typeid(UpdateMobEffectPacket), UpdateMobEffectPacket::create); + map(42, true, false, true, false, typeid(RemoveMobEffectPacket), RemoveMobEffectPacket::create); + map(43, true, false, true, false, typeid(SetExperiencePacket), SetExperiencePacket::create); + + map(50, true, false, true, true, typeid(ChunkVisibilityPacket), ChunkVisibilityPacket::create); + map(51, true, false, true, true, typeid(BlockRegionUpdatePacket), BlockRegionUpdatePacket::create); // Changed to LevelChunkPacket in Java but we aren't using that + map(52, true, false, true, true, typeid(ChunkTilesUpdatePacket), ChunkTilesUpdatePacket::create); + map(53, true, false, true, true, typeid(TileUpdatePacket), TileUpdatePacket::create); + map(54, true, false, true, true, typeid(TileEventPacket), TileEventPacket::create); + map(55, true, false, false, false, typeid(TileDestructionPacket), TileDestructionPacket::create); + + map(60, true, false, true, false, typeid(ExplodePacket), ExplodePacket::create); + map(61, true, false, true, false, typeid(LevelEventPacket), LevelEventPacket::create); + // 4J-PB - don't see the need for this, we can use 61 + map(62, true, false, true, false, typeid(LevelSoundPacket), LevelSoundPacket::create); + //map(62, true, false, true, false, typeid(LevelSoundPacket), LevelSoundPacket::create); + + map(70, true, false, false, false, typeid(GameEventPacket), GameEventPacket::create); + map(71, true, false, false, false, typeid(AddGlobalEntityPacket), AddGlobalEntityPacket::create); + + map(100, true, false, true, false, typeid(ContainerOpenPacket), ContainerOpenPacket::create); + map(101, true, true, true, false, typeid(ContainerClosePacket), ContainerClosePacket::create); + map(102, false, true, false, false, typeid(ContainerClickPacket), ContainerClickPacket::create); +#ifndef _CONTENT_PACKAGE + // 4J Stu - We have some debug code that uses this packet to send data back to the server from the client + // We may wish to add this into the real game at some point + map(103, true, true, true, false, typeid(ContainerSetSlotPacket), ContainerSetSlotPacket::create); +#else + map(103, true, false, true, false, typeid(ContainerSetSlotPacket), ContainerSetSlotPacket::create); +#endif + map(104, true, false, true, false, typeid(ContainerSetContentPacket), ContainerSetContentPacket::create); + map(105, true, false, true, false, typeid(ContainerSetDataPacket), ContainerSetDataPacket::create); + map(106, true, true, true, false, typeid(ContainerAckPacket), ContainerAckPacket::create); + map(107, true, true, true, false, typeid(SetCreativeModeSlotPacket), SetCreativeModeSlotPacket::create); + map(108, false, true, false, false, typeid(ContainerButtonClickPacket), ContainerButtonClickPacket::create); + + map(130, true, true, true, false, typeid(SignUpdatePacket), SignUpdatePacket::create); + map(131, true, false, true, false, typeid(ComplexItemDataPacket), ComplexItemDataPacket::create); + map(132, true, false, false, false, typeid(TileEntityDataPacket), TileEntityDataPacket::create); + + // 4J Added + map(150, false, true, false, false, typeid(CraftItemPacket), CraftItemPacket::create); + map(151, false, true, true, false, typeid(TradeItemPacket), TradeItemPacket::create); + map(152, false, true, false, false, typeid(DebugOptionsPacket), DebugOptionsPacket::create); + map(153, true, true, false, false, typeid(ServerSettingsChangedPacket), ServerSettingsChangedPacket::create); + map(154, true, true, true, false, typeid(TexturePacket), TexturePacket::create); + map(155, true, false, true, true, typeid(ChunkVisibilityAreaPacket), ChunkVisibilityAreaPacket::create); + map(156, true, false, false, true, typeid(UpdateProgressPacket), UpdateProgressPacket::create); + map(157, true, true, true, false, typeid(TextureChangePacket), TextureChangePacket::create); + map(158, true, false, true, false, typeid(UpdateGameRuleProgressPacket), UpdateGameRuleProgressPacket::create); + map(159, false, true, false, false, typeid(KickPlayerPacket), KickPlayerPacket::create); + map(160, true, true, true, false, typeid(TextureAndGeometryPacket), TextureAndGeometryPacket::create); + map(161, true, true, true, false, typeid(TextureAndGeometryChangePacket), TextureAndGeometryChangePacket::create); + + map(162, true, false, false, false, typeid(MoveEntityPacketSmall), MoveEntityPacketSmall::create); + map(163, true, false, false, true, typeid(MoveEntityPacketSmall::Pos), MoveEntityPacketSmall::Pos::create); + map(164, true, false, false, true, typeid( MoveEntityPacketSmall::Rot), MoveEntityPacketSmall::Rot::create); + map(165, true, false, false, true, typeid(MoveEntityPacketSmall::PosRot), MoveEntityPacketSmall::PosRot::create); + map(166, true, true, false, false, typeid(XZPacket), XZPacket::create); + map(167, false, true, false, false, typeid(GameCommandPacket), GameCommandPacket::create); + + map(200, true, false, true, false, typeid(AwardStatPacket), AwardStatPacket::create); + map(201, true, true, false, false, typeid(PlayerInfoPacket), PlayerInfoPacket::create); // TODO New for 1.8.2 - Repurposed by 4J + map(202, true, true, true, false, typeid(PlayerAbilitiesPacket), PlayerAbilitiesPacket::create); + // 4J Stu - These added 1.3.2, but don't think we need them + //map(203, true, true, true, false, ChatAutoCompletePacket.class); + //map(204, false, true, true, false, ClientInformationPacket.class); + map(205, false, true, true, false, typeid(ClientCommandPacket), ClientCommandPacket::create); + map(250, true, true, true, false, typeid(CustomPayloadPacket), CustomPayloadPacket::create); + // 4J Stu - These added 1.3.2, but don't think we need them + //map(252, true, true, SharedKeyPacket.class); + //map(253, true, false, ServerAuthDataPacket.class); + map(254, false, true, false, false, typeid(GetInfoPacket), GetInfoPacket::create); // TODO New for 1.8.2 - Needs sendToAny? + map(255, true, true, true, false, typeid(DisconnectPacket), DisconnectPacket::create); +} + +IllegalArgumentException::IllegalArgumentException(const wstring& information) +{ + this->information = information; +} + +IOException::IOException(const wstring& information) +{ + this->information = information; +} + +Packet::Packet() : createTime( System::currentTimeMillis() ) +{ + shouldDelay = false; +} + +unordered_map Packet::idToCreateMap; + +unordered_set Packet::clientReceivedPackets = unordered_set(); +unordered_set Packet::serverReceivedPackets = unordered_set(); +unordered_set Packet::sendToAnyClientPackets = unordered_set(); + +// 4J Added +unordered_map Packet::outgoingStatistics = unordered_map(); +vector Packet::renderableStats = vector(); +int Packet::renderPos = 0; + +// sendToAnyClient - true - send to anyone, false - Sends to one person per dimension per machine +void Packet::map(int id, bool receiveOnClient, bool receiveOnServer, bool sendToAnyClient, bool renderStats, const type_info& clazz, packetCreateFn createFn) +{ +#if 0 + if (idToClassMap.count(id) > 0) throw new IllegalArgumentException(wstring(L"Duplicate packet id:") + _toString(id)); + if (classToIdMap.count(clazz) > 0) throw new IllegalArgumentException(L"Duplicate packet class:"); // TODO + clazz); +#endif + + idToCreateMap.insert( unordered_map::value_type(id, createFn) ); + +#ifndef _CONTENT_PACKAGE +#if PACKET_ENABLE_STAT_TRACKING + Packet::PacketStatistics *packetStatistics = new PacketStatistics(id); + outgoingStatistics[id] = packetStatistics; + + if(renderStats) + { + renderableStats.push_back( packetStatistics ); + } +#endif +#endif + + if (receiveOnClient) + { + clientReceivedPackets.insert(id); + } + if (receiveOnServer) + { + serverReceivedPackets.insert(id); + } + if(sendToAnyClient) + { + sendToAnyClientPackets.insert(id); + } +} + +// 4J Added to record data for outgoing packets +void Packet::recordOutgoingPacket(shared_ptr packet) +{ +#ifndef _CONTENT_PACKAGE +#if PACKET_ENABLE_STAT_TRACKING + AUTO_VAR(it, outgoingStatistics.find(packet->getId())); + + if( it == outgoingStatistics.end() ) + { + Packet::PacketStatistics *packetStatistics = new PacketStatistics(packet->getId()); + outgoingStatistics[packet->getId()] = packetStatistics; + packetStatistics->addPacket(packet->getEstimatedSize()); + } + else + { + it->second->addPacket(packet->getEstimatedSize()); + } +#endif +#endif +} + +void Packet::renderPacketStats(int id) +{ + AUTO_VAR(it, outgoingStatistics.find(id)); + + if( it != outgoingStatistics.end() ) + { + it->second->renderStats(); + } +} + +void Packet::renderAllPacketStats() +{ +#ifndef _CONTENT_PACKAGE +#if PACKET_ENABLE_STAT_TRACKING + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->gui->renderStackedGraph(Packet::renderPos, 512, renderableStats.size(), &Packet::getIndexedStatValue ); + + renderAllPacketStatsKey(); + + Packet::renderPos++; + Packet::renderPos%=511; +#endif +#endif +} + +void Packet::renderAllPacketStatsKey() +{ +#ifndef _CONTENT_PACKAGE +#if PACKET_ENABLE_STAT_TRACKING + Minecraft *pMinecraft = Minecraft::GetInstance(); + int total = Packet::renderableStats.size(); + for(unsigned int i = 0; i < total; ++i) + { + Packet::PacketStatistics *stat = Packet::renderableStats[i]; + float vary = (float)i/total; + int fColour = floor(vary * 0xffffff); + + int colour = 0xff000000 + fColour; + pMinecraft->gui->drawString( pMinecraft->font, stat->getLegendString(), 900, 30 + (10 * i), colour); + } +#endif +#endif +} + +__int64 Packet::getIndexedStatValue(unsigned int samplePos, unsigned int renderableId) +{ + __int64 val = 0; + +#ifndef _CONTENT_PACKAGE +#if PACKET_ENABLE_STAT_TRACKING + val = renderableStats[renderableId]->getCountSample(samplePos); +#endif +#endif + + return val; +} + + +shared_ptr Packet::getPacket(int id) +{ + // 4J - removed try/catch + // try + // { + return idToCreateMap[id](); + // } + // catch (exception e) + // { + // // TODO 4J JEV print stack trace, newInstance doesnt throw an exception in c++ yet. + // printf("Skipping packet with id %d" , id); + // return NULL; + // } +} + +void Packet::writeBytes(DataOutputStream *dataoutputstream, byteArray bytes) +{ + dataoutputstream->writeShort(bytes.length); + dataoutputstream->write(bytes); +} + +byteArray Packet::readBytes(DataInputStream *datainputstream) +{ + int size = datainputstream->readShort(); + if (size < 0) + { + app.DebugPrintf("Key was smaller than nothing! Weird key!"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return byteArray(); + //throw new IOException("Key was smaller than nothing! Weird key!"); + } + + byteArray bytes(size); + datainputstream->read(bytes); + + return bytes; +} + + +bool Packet::canSendToAnyClient(shared_ptr packet) +{ + int packetId = packet->getId(); + + return sendToAnyClientPackets.count(packetId) != 0; +} + +// 4J - now a pure virtual method +/* +int Packet::getId() +{ +return id; +} +*/ + +unordered_map Packet::statistics = unordered_map(); + +//int Packet::nextPrint = 0; + +shared_ptr Packet::readPacket(DataInputStream *dis, bool isServer) // throws IOException TODO 4J JEV, should this declare a throws? +{ + int id = 0; + shared_ptr packet = nullptr; + + // 4J - removed try/catch + // try + // { + id = dis->read(); + if (id == -1) return nullptr; + + if ((isServer && serverReceivedPackets.find(id) == serverReceivedPackets.end()) || (!isServer && clientReceivedPackets.find(id) == clientReceivedPackets.end())) + { + //app.DebugPrintf("Bad packet id %d\n", id); + __debugbreak(); + assert(false); + // throw new IOException(wstring(L"Bad packet id ") + _toString(id)); + } + + packet = getPacket(id); + if (packet == NULL) assert(false);//throw new IOException(wstring(L"Bad packet id ") + _toString(id)); + + //app.DebugPrintf("%s reading packet %d\n", isServer ? "Server" : "Client", packet->getId()); + packet->read(dis); + // } + // catch (EOFException e) + // { + // // reached end of stream + // OutputDebugString("Reached end of stream"); + // return NULL; + // } + + // 4J - Don't bother tracking stats in a content package + // 4J Stu - This changes a bit in 1.0.1, but we don't really use it so stick with what we have +#ifndef _CONTENT_PACKAGE +#if PACKET_ENABLE_STAT_TRACKING + AUTO_VAR(it, statistics.find(id)); + + if( it == statistics.end() ) + { + Packet::PacketStatistics *packetStatistics = new PacketStatistics(id); + statistics[id] = packetStatistics; + packetStatistics->addPacket(packet->getEstimatedSize()); + } + else + { + it->second->addPacket(packet->getEstimatedSize()); + } +#endif +#endif + + return packet; +} + +void Packet::writePacket(shared_ptr packet, DataOutputStream *dos) // throws IOException TODO 4J JEV, should this declare a throws? +{ + //app.DebugPrintf("Writing packet %d\n", packet->getId()); + dos->write(packet->getId()); + packet->write(dos); +} + +void Packet::writeUtf(const wstring& value, DataOutputStream *dos) // throws IOException TODO 4J JEV, should this declare a throws? +{ +#if 0 + if (value.length() > Short::MAX_VALUE) + { + throw new IOException(L"String too big"); + } +#endif + + dos->writeShort((short)value.length()); + dos->writeChars(value); +} + +wstring Packet::readUtf(DataInputStream *dis, int maxLength) // throws IOException TODO 4J JEV, should this declare a throws? +{ + + short stringLength = dis->readShort(); + if (stringLength > maxLength) + { + wstringstream stream; + stream << L"Received string length longer than maximum allowed (" << stringLength << " > " << maxLength << ")"; + assert(false); + // throw new IOException( stream.str() ); + } + if (stringLength < 0) + { + assert(false); + // throw new IOException(L"Received string length is less than zero! Weird string!"); + } + + wstring builder = L""; + for (int i = 0; i < stringLength; i++) + { + wchar_t rc = dis->readChar(); + builder.push_back( rc ); + } + + return builder; +} + +void Packet::PacketStatistics::addPacket(int bytes) +{ + if(count == 0) + { + firstSampleTime = System::currentTimeMillis(); + } + count++; + totalSize += bytes; + + // 4J Added + countSamples[samplesPos & (512 - 1)]++; + sizeSamples[samplesPos & (512 - 1)] += (unsigned int) bytes; +} + +int Packet::PacketStatistics::getCount() +{ + return count; +} + +double Packet::PacketStatistics::getAverageSize() +{ + if (count == 0) + { + return 0; + } + return (double) totalSize / count; +} + +void Packet::PacketStatistics::renderStats( ) +{ +#ifndef _CONTENT_PACKAGE +#if PACKET_ENABLE_STAT_TRACKING + samplesPos++; + + countSamples[samplesPos & (512 - 1)] = 0; + sizeSamples[samplesPos & (512 - 1)] = 0; + + Minecraft *pMinecraft = Minecraft::GetInstance(); + pMinecraft->gui->renderGraph(512, samplesPos, countSamples, 1, 10, sizeSamples, 1, 50); +#endif +#endif +} + +__int64 Packet::PacketStatistics::getCountSample(int samplePos) +{ + if(samplePos == 511) + { + samplesPos++; + countSamples[samplesPos & (512 - 1)] = 0; + sizeSamples[samplesPos & (512 - 1)] = 0; + } + + return countSamples[samplePos] * 10; +} + +wstring Packet::PacketStatistics::getLegendString() +{ + static wchar_t string[128]; + double bps = 0.0; + if(firstSampleTime > 0) + { + float timeDiff = ((System::currentTimeMillis() - firstSampleTime)/1000); + if(timeDiff > 0) bps = totalSize / timeDiff; + } + swprintf(string, 128, L"id: %d , packets: %d , total: %d , bytes: %d, total: %d, %f Bps", id, countSamples[(samplesPos - 1) & (512 - 1)], count, sizeSamples[(samplesPos - 1) & (512 - 1)], totalSize, bps ); + return string; +} + +bool Packet::canBeInvalidated() +{ + return false; +} + +bool Packet::isInvalidatedBy(shared_ptr packet) +{ + return false; +} + +bool Packet::isAync() +{ + return false; +} + +// 4J Stu - Brought these functions forward for enchanting/game rules +shared_ptr Packet::readItem(DataInputStream *dis) +{ + shared_ptr item = nullptr; + int id = dis->readShort(); + if (id >= 0) + { + int count = dis->readByte(); + int damage = dis->readShort(); + + item = shared_ptr( new ItemInstance(id, count, damage) ); + // 4J Stu - Always read/write the tag + //if (Item.items[id].canBeDepleted() || Item.items[id].shouldOverrideMultiplayerNBT()) + { + item->tag = readNbt(dis); + } + } + + return item; +} + +void Packet::writeItem(shared_ptr item, DataOutputStream *dos) +{ + if (item == NULL) + { + dos->writeShort(-1); + } + else + { + dos->writeShort(item->id); + dos->writeByte(item->count); + dos->writeShort(item->getAuxValue()); + // 4J Stu - Always read/write the tag + //if (item.getItem().canBeDepleted() || item.getItem().shouldOverrideMultiplayerNBT()) + { + writeNbt(item->tag, dos); + } + } +} + +CompoundTag *Packet::readNbt(DataInputStream *dis) +{ + int size = dis->readShort(); + if (size < 0) return NULL; + byteArray buff(size); + dis->readFully(buff); + CompoundTag *result = (CompoundTag *) NbtIo::decompress(buff); + delete [] buff.data; + return result; +} + +void Packet::writeNbt(CompoundTag *tag, DataOutputStream *dos) +{ + if (tag == NULL) + { + dos->writeShort(-1); + } + else + { + byteArray buff = NbtIo::compress(tag); + dos->writeShort((short) buff.length); + dos->write(buff); + delete [] buff.data; + } +} diff --git a/Minecraft.World/Packet.h b/Minecraft.World/Packet.h new file mode 100644 index 00000000..60410df3 --- /dev/null +++ b/Minecraft.World/Packet.h @@ -0,0 +1,113 @@ +#pragma once +using namespace std; + +class Packet; +class PacketListener; +class DataInputStream; +class DataOutputStream; + +#define PACKET_ENABLE_STAT_TRACKING 0 + +class Packet; + +typedef shared_ptr (*packetCreateFn)(); + +class Packet +{ +public: + class PacketStatistics + { + private: + int count; + int totalSize; + + // 4J Added + __int64 countSamples[512]; + __int64 sizeSamples[512]; + int samplesPos; + __int64 firstSampleTime; + + + public: + const int id; + + public: + PacketStatistics(int id) : id( id ), count( 0 ), totalSize( 0 ), samplesPos( 0 ), firstSampleTime( 0 ) { countSamples[0] = 0; sizeSamples[0] = 0; } + void addPacket(int bytes); + int getCount(); + double getAverageSize(); + + // 4J Added + void renderStats(); + __int64 getCountSample(int samplePos); + wstring getLegendString(); + }; + + // 4J JEV, replaces the static blocks. + static void staticCtor(); + +public: + static unordered_map idToCreateMap; // IntHashMap in 1.8.2 ... needed? // Made public in 1.0.1 + + static unordered_set clientReceivedPackets; + static unordered_set serverReceivedPackets; + static unordered_set sendToAnyClientPackets; + + // 4J Stu - Added the sendToAnyClient param so we can limit some packets to be only sent to one player on a system + // 4J Stu - Added renderStats param for use in debugging + static void map(int id, bool receiveOnClient, bool receiveOnServer, bool sendToAnyClient, bool renderStats, const type_info& clazz, packetCreateFn ); + +public: + const __int64 createTime; + + Packet(); + + static shared_ptr getPacket(int id); + + // 4J Added + static bool canSendToAnyClient(shared_ptr packet); + + static void writeBytes(DataOutputStream *dataoutputstream, byteArray bytes); + static byteArray readBytes(DataInputStream *datainputstream); + + virtual int getId() = 0; + + bool shouldDelay; + +private: + // 4J Added to track stats for packets that are going out via QNet + static unordered_map outgoingStatistics; // IntHashMap in 1.8.2 ... needed? + static vector renderableStats; + static int renderPos; +public: + static void recordOutgoingPacket(shared_ptr packet); + static void renderPacketStats(int id); + static void renderAllPacketStats(); + static void renderAllPacketStatsKey(); + static __int64 getIndexedStatValue(unsigned int samplePos, unsigned int renderableId); + +private : + static unordered_map statistics; + //static int nextPrint; + +public: + static shared_ptr readPacket(DataInputStream *dis, bool isServer); + static void writePacket(shared_ptr packet, DataOutputStream *dos); + static void writeUtf(const wstring& value, DataOutputStream *dos); + static wstring readUtf(DataInputStream *dis, int maxLength); + virtual void read(DataInputStream *dis) = 0; // throws IOException = 0; TODO 4J JEV, should this declare a throws? + virtual void write(DataOutputStream *dos) = 0; // throws IOException = 0; TODO 4J JEV, should this declare a throws? + virtual void handle(PacketListener *listener) = 0; + virtual int getEstimatedSize() = 0; + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + virtual bool isAync(); + + // 4J Stu - Brought these functions forward for enchanting/game rules + static shared_ptr readItem(DataInputStream *dis); + static void writeItem(shared_ptr item, DataOutputStream *dos); + static CompoundTag *readNbt(DataInputStream *dis); + +protected: + static void writeNbt(CompoundTag *tag, DataOutputStream *dos); +}; \ No newline at end of file diff --git a/Minecraft.World/PacketListener.cpp b/Minecraft.World/PacketListener.cpp new file mode 100644 index 00000000..2f051d7d --- /dev/null +++ b/Minecraft.World/PacketListener.cpp @@ -0,0 +1,451 @@ +#include "stdafx.h" +#include "net.minecraft.network.packet.h" +#include "PacketListener.h" + +void PacketListener::handleBlockRegionUpdate(shared_ptr packet) +{ +} + +void PacketListener::onUnhandledPacket(shared_ptr packet) +{ +} + +void PacketListener::onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects) +{ +} + +void PacketListener::handleDisconnect(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleLogin(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleMovePlayer(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleChunkTilesUpdate(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handlePlayerAction(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleTileUpdate(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleChunkVisibility(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleAddPlayer(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleMoveEntity(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleMoveEntitySmall(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleTeleportEntity(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleUseItem(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleSetCarriedItem(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleRemoveEntity(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleTakeItemEntity(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleChat(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleAddEntity(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleAnimate(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handlePlayerCommand(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handlePreLogin(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleAddMob(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleSetTime(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleSetSpawn(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleSetEntityMotion(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleSetEntityData(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleRidePacket(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleInteract(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleEntityEvent(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleSetHealth(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleRespawn(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleTexture(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleTextureAndGeometry(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleExplosion(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleContainerOpen(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleContainerClose(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleContainerClick(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleContainerSetSlot(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleContainerContent(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleSignUpdate(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleContainerSetData(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleSetEquippedItem(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleContainerAck(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleAddPainting(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleTileEvent(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleAwardStat(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleEntityActionAtPosition(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handlePlayerInput(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleGameEvent(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleAddGlobalEntity(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleComplexItemData(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleLevelEvent(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +// 1.8.2 +void PacketListener::handleGetInfo(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handleUpdateMobEffect(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handleRemoveMobEffect(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handlePlayerInfo(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handleKeepAlive(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handleSetExperience(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handleSetCreativeModeSlot(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handleAddExperienceOrb(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +// 1.0.1 +void PacketListener::handleContainerButtonClick(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handleTileEntityData(shared_ptr tileEntityDataPacket) +{ + onUnhandledPacket(tileEntityDataPacket); +} + +// 1.1 +void PacketListener::handleCustomPayload(shared_ptr customPayloadPacket) +{ + onUnhandledPacket(customPayloadPacket); +} + +// 1.2.3 +void PacketListener::handleRotateMob(shared_ptr rotateMobPacket) +{ + onUnhandledPacket(rotateMobPacket); +} + +// 1.3.2 +void PacketListener::handleClientProtocolPacket(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handleServerAuthData(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +//void PacketListener::handleSharedKey(shared_ptr packet) +//{ +// onUnhandledPacket(packet); +//} + +void PacketListener::handlePlayerAbilities(shared_ptr playerAbilitiesPacket) +{ + onUnhandledPacket(playerAbilitiesPacket); +} + +void PacketListener::handleChatAutoComplete(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handleClientInformation(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handleSoundEvent(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handleTileDestruction(shared_ptr packet) +{ + onUnhandledPacket(packet); +} + +void PacketListener::handleClientCommand(shared_ptr packet) +{ +} + +//void PacketListener::handleLevelChunks(shared_ptr packet) +//{ +// onUnhandledPacket(packet); +//} + +bool PacketListener::canHandleAsyncPackets() +{ + return false; +} + +// 4J Added + +void PacketListener::handleCraftItem(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleTradeItem(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleDebugOptions(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleServerSettingsChanged(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleChunkVisibilityArea(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleUpdateProgress(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleTextureChange(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleTextureAndGeometryChange(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleUpdateGameRuleProgressPacket(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleKickPlayer(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleXZ(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} + +void PacketListener::handleGameCommand(shared_ptr packet) +{ + onUnhandledPacket( (shared_ptr ) packet); +} diff --git a/Minecraft.World/PacketListener.h b/Minecraft.World/PacketListener.h new file mode 100644 index 00000000..93b14603 --- /dev/null +++ b/Minecraft.World/PacketListener.h @@ -0,0 +1,211 @@ +#pragma once +using namespace std; + +class Packet; + +class AddEntityPacket; +class AddGlobalEntityPacket; +class AddMobPacket; +class AddPaintingPacket; +class AddPlayerPacket; +class AnimatePacket; +class AwardStatPacket; +class BlockRegionUpdatePacket; +class ChatPacket; +class ChunkTilesUpdatePacket; +class ChunkVisibilityPacket; +class ComplexItemDataPacket; +class ContainerAckPacket; +class ContainerClickPacket; +class ContainerClosePacket; +class ContainerOpenPacket; +class ContainerSetContentPacket; +class ContainerSetDataPacket; +class ContainerSetSlotPacket; +#include "DisconnectPacket.h" +class EntityActionAtPositionPacket; +class EntityEventPacket; +class ExplodePacket; +class GameEventPacket; +class InteractPacket; +class KeepAlivePacket; +class LevelEventPacket; +class LoginPacket; +class MoveEntityPacket; +class MoveEntityPacketSmall; +class MovePlayerPacket; +class PlayerActionPacket; +class PlayerCommandPacket; +class PlayerInputPacket; +class PreLoginPacket; +class RemoveEntitiesPacket; +class RespawnPacket; +class SetCarriedItemPacket; +class SetEntityDataPacket; +class SetEntityMotionPacket; +class SetEquippedItemPacket; +class SetHealthPacket; +class SetRidingPacket; +class SetSpawnPositionPacket; +class SetTimePacket; +class SignUpdatePacket; +class TakeItemEntityPacket; +class TeleportEntityPacket; +class TileEventPacket; +class TileUpdatePacket; +class UseItemPacket; + +// 1.8.2 +class GetInfoPacket; +class UpdateMobEffectPacket; +class RemoveMobEffectPacket; +class PlayerInfoPacket; +class SetExperiencePacket; +class SetCreativeModeSlotPacket; +class AddExperienceOrbPacket; + +// 1.0.1 +class ContainerButtonClickPacket; +class TileEntityDataPacket; + +// 1.1 +class CustomPayloadPacket; + +// 1.2.3 +class RotateHeadPacket; + +// 1.3.2 +class ClientProtocolPacket; +class ServerAuthDataPacket; +class SharedKeyPacket; +class PlayerAbilitiesPacket; +class ChatAutoCompletePacket; +class ClientInformationPacket; +class LevelSoundPacket; +class TileDestructionPacket; +class ClientCommandPacket; +class LevelChunksPacket; + +// 4J Added +class CraftItemPacket; +class TradeItemPacket; +class DebugOptionsPacket; +class ServerSettingsChangedPacket; +class TexturePacket; +class TextureAndGeometryPacket; +class ChunkVisibilityAreaPacket; +class UpdateProgressPacket; +class TextureChangePacket; +class TextureAndGeometryChangePacket; +class UpdateGameRuleProgressPacket; +class KickPlayerPacket; +class AdditionalModelPartsPacket; +class XZPacket; +class GameCommandPacket; + +class PacketListener +{ +public: + virtual bool isServerPacketListener() = 0; + virtual void handleBlockRegionUpdate(shared_ptr packet); + virtual void onUnhandledPacket(shared_ptr packet); + virtual void onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects); + virtual void handleDisconnect(shared_ptr packet); + virtual void handleLogin(shared_ptr packet); + virtual void handleMovePlayer(shared_ptr packet); + virtual void handleChunkTilesUpdate(shared_ptr packet); + virtual void handlePlayerAction(shared_ptr packet); + virtual void handleTileUpdate(shared_ptr packet); + virtual void handleChunkVisibility(shared_ptr packet); + virtual void handleAddPlayer(shared_ptr packet); + virtual void handleMoveEntity(shared_ptr packet); + virtual void handleMoveEntitySmall(shared_ptr packet); + virtual void handleTeleportEntity(shared_ptr packet); + virtual void handleUseItem(shared_ptr packet); + virtual void handleSetCarriedItem(shared_ptr packet); + virtual void handleRemoveEntity(shared_ptr packet); + virtual void handleTakeItemEntity(shared_ptr packet); + virtual void handleChat(shared_ptr packet); + virtual void handleAddEntity(shared_ptr packet); + virtual void handleAnimate(shared_ptr packet); + virtual void handlePlayerCommand(shared_ptr packet); + virtual void handlePreLogin(shared_ptr packet); + virtual void handleAddMob(shared_ptr packet); + virtual void handleSetTime(shared_ptr packet); + virtual void handleSetSpawn(shared_ptr packet); + virtual void handleSetEntityMotion(shared_ptr packet); + virtual void handleSetEntityData(shared_ptr packet); + virtual void handleRidePacket(shared_ptr packet); + virtual void handleInteract(shared_ptr packet); + virtual void handleEntityEvent(shared_ptr packet); + virtual void handleSetHealth(shared_ptr packet); + virtual void handleRespawn(shared_ptr packet); + virtual void handleExplosion(shared_ptr packet); + virtual void handleContainerOpen(shared_ptr packet); + virtual void handleContainerClose(shared_ptr packet); + virtual void handleContainerClick(shared_ptr packet); + virtual void handleContainerSetSlot(shared_ptr packet); + virtual void handleContainerContent(shared_ptr packet); + virtual void handleSignUpdate(shared_ptr packet); + virtual void handleContainerSetData(shared_ptr packet); + virtual void handleSetEquippedItem(shared_ptr packet); + virtual void handleContainerAck(shared_ptr packet); + virtual void handleAddPainting(shared_ptr packet); + virtual void handleTileEvent(shared_ptr packet); + virtual void handleAwardStat(shared_ptr packet); + virtual void handleEntityActionAtPosition(shared_ptr packet); + virtual void handlePlayerInput(shared_ptr packet); + virtual void handleGameEvent(shared_ptr packet); + virtual void handleAddGlobalEntity(shared_ptr packet); + virtual void handleComplexItemData(shared_ptr packet); + virtual void handleLevelEvent(shared_ptr packet); + + // 1.8.2 + virtual void handleGetInfo(shared_ptr packet); + virtual void handleUpdateMobEffect(shared_ptr packet); + virtual void handleRemoveMobEffect(shared_ptr packet); + virtual void handlePlayerInfo(shared_ptr packet); + virtual void handleKeepAlive(shared_ptr packet); + virtual void handleSetExperience(shared_ptr packet); + virtual void handleSetCreativeModeSlot(shared_ptr packet); + virtual void handleAddExperienceOrb(shared_ptr packet); + + // 1.0.1 + virtual void handleContainerButtonClick(shared_ptr packet); + virtual void handleTileEntityData(shared_ptr tileEntityDataPacket); + + // 1.1s + virtual void handleCustomPayload(shared_ptr customPayloadPacket); + + // 1.2.3 + virtual void handleRotateMob(shared_ptr rotateMobPacket); + + // 1.3.2 + virtual void handleClientProtocolPacket(shared_ptr packet); + virtual void handleServerAuthData(shared_ptr packet); + //virtual void handleSharedKey(shared_ptr packet); + virtual void handlePlayerAbilities(shared_ptr playerAbilitiesPacket); + virtual void handleChatAutoComplete(shared_ptr packet); + virtual void handleClientInformation(shared_ptr packet); + virtual void handleSoundEvent(shared_ptr packet); + virtual void handleTileDestruction(shared_ptr packet); + virtual void handleClientCommand(shared_ptr packet); + //virtual void handleLevelChunks(shared_ptr packet); + virtual bool canHandleAsyncPackets(); + + // 4J Added + virtual void handleCraftItem(shared_ptr packet); + virtual void handleTradeItem(shared_ptr packet); + virtual void handleDebugOptions(shared_ptr packet); + virtual void handleServerSettingsChanged(shared_ptr packet); + virtual void handleTexture(shared_ptr packet); + virtual void handleTextureAndGeometry(shared_ptr packet); + virtual void handleChunkVisibilityArea(shared_ptr packet); + virtual void handleUpdateProgress(shared_ptr packet); + virtual void handleTextureChange(shared_ptr packet); + virtual void handleTextureAndGeometryChange(shared_ptr packet); + virtual void handleUpdateGameRuleProgressPacket(shared_ptr packet); + virtual void handleKickPlayer(shared_ptr packet); + virtual void handleXZ(shared_ptr packet); + virtual void handleGameCommand(shared_ptr packet); +}; diff --git a/Minecraft.World/Painting.cpp b/Minecraft.World/Painting.cpp new file mode 100644 index 00000000..0d8f35fe --- /dev/null +++ b/Minecraft.World/Painting.cpp @@ -0,0 +1,148 @@ +#include "stdafx.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.damagesource.h" +#include "com.mojang.nbt.h" +#include "Painting.h" +#include "Material.h" + + + +typedef Painting::Motive _Motive; +const _Motive *Painting::Motive::values[] = { + new _Motive(L"Kebab", 16, 16, 0 * 16, 0 * 16), + new _Motive(L"Aztec", 16, 16, 1 * 16, 0 * 16), // + new _Motive(L"Alban", 16, 16, 2 * 16, 0 * 16), // + new _Motive(L"Aztec2", 16, 16, 3 * 16, 0 * 16), // + new _Motive(L"Bomb", 16, 16, 4 * 16, 0 * 16), // + new _Motive(L"Plant", 16, 16, 5 * 16, 0 * 16), // + new _Motive(L"Wasteland", 16, 16, 6 * 16, 0 * 16), // + + new _Motive(L"Pool", 32, 16, 0 * 16, 2 * 16), // + new _Motive(L"Courbet", 32, 16, 2 * 16, 2 * 16), // + new _Motive(L"Sea", 32, 16, 4 * 16, 2 * 16), // + new _Motive(L"Sunset", 32, 16, 6 * 16, 2 * 16), // + new _Motive(L"Creebet", 32, 16, 8 * 16, 2 * 16), // + + new _Motive(L"Wanderer", 16, 32, 0 * 16, 4 * 16), // + new _Motive(L"Graham", 16, 32, 1 * 16, 4 * 16), // + + new _Motive(L"Match", 32, 32, 0 * 16, 8 * 16), // + new _Motive(L"Bust", 32, 32, 2 * 16, 8 * 16), // + new _Motive(L"Stage", 32, 32, 4 * 16, 8 * 16), // + new _Motive(L"Void", 32, 32, 6 * 16, 8 * 16), // + new _Motive(L"SkullAndRoses", 32, 32, 8 * 16, 8 * 16), // + new _Motive(L"Wither", 32, 32, 10 * 16, 8 * 16), + new _Motive(L"Fighters", 64, 32, 0 * 16, 6 * 16), // + + new _Motive(L"Pointer", 64, 64, 0 * 16, 12 * 16), // + new _Motive(L"Pigscene", 64, 64, 4 * 16, 12 * 16), // + new _Motive(L"BurningSkull", 64, 64, 8 * 16, 12 * 16), // + + new _Motive(L"Skeleton", 64, 48, 12 * 16, 4 * 16), // + new _Motive(L"DonkeyKong", 64, 48, 12 * 16, 7 * 16), // +}; + +// 4J Stu - Rather than creating a new string object here I am just using the +// actual number value of the characters in "SkullandRoses" which should be the +// longest name from the above +const int Painting::Motive::MAX_MOTIVE_NAME_LENGTH = 13; //JAVA: "SkullAndRoses".length(); + +// 4J - added for common ctor code +void Painting::_init( Level *level ) +{ + motive = NULL; +}; + +Painting::Painting(Level *level) : HangingEntity( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + _init( level ); +} + +Painting::Painting(Level *level, int xTile, int yTile, int zTile, int dir) : HangingEntity( level , xTile, yTile, zTile, dir) +{ + _init(level); + + // 4J Stu - If you use this ctor, then you need to call the PaintingPostConstructor +} + +// 4J Stu - Added this so that we can use some shared_ptr functions that were needed in the ctor +void Painting::PaintingPostConstructor(int dir) +{ + vector *survivableMotives = new vector(); + for (int i = 0 ; i < LAST_VALUE; i++) + { + this->motive = (Motive *)Motive::values[i]; + setDir(dir); + if (survives()) + { + survivableMotives->push_back(this->motive); + } + } + if (!survivableMotives->empty()) + { + this->motive = survivableMotives->at(random->nextInt((int)survivableMotives->size())); + } + setDir(dir); +} + +Painting::Painting(Level *level, int x, int y, int z, int dir, wstring motiveName) : HangingEntity( level , x, y, z, dir ) +{ + _init(level); + + for (int i = 0 ; i < LAST_VALUE; i++) + { + if ( (Motive::values[i])->name.compare(motiveName) == 0) + { + this->motive = (Motive *)Motive::values[i]; + break; + } + } + setDir(dir); +} + +void Painting::addAdditonalSaveData(CompoundTag *tag) +{ + ///TODO Safe to cast to non-const type? + tag->putString(L"Motive", motive->name); + + HangingEntity::addAdditonalSaveData(tag); + } + +void Painting::readAdditionalSaveData(CompoundTag *tag) +{ + wstring motiveName = tag->getString(L"Motive"); + vector::iterator it; + for (int i = 0 ; i < LAST_VALUE; i++) + { + if ( Motive::values[i]->name.compare(motiveName) == 0) + { + this->motive = (Motive *)Motive::values[i]; + } + } + if (this->motive == NULL) motive = (Motive *)Motive::values[ Kebab ]; + + HangingEntity::readAdditionalSaveData(tag); +} + +int Painting::getWidth() +{ + return motive->w; +} + +int Painting::getHeight() +{ + return motive->h; +} + +void Painting::dropItem() +{ + spawnAtLocation(shared_ptr(new ItemInstance(Item::painting)), 0.0f); +} \ No newline at end of file diff --git a/Minecraft.World/Painting.h b/Minecraft.World/Painting.h new file mode 100644 index 00000000..7866b662 --- /dev/null +++ b/Minecraft.World/Painting.h @@ -0,0 +1,117 @@ +#pragma once +using namespace std; + +#include "Entity.h" +#include "HangingEntity.h" + +class Level; +class CompoundTag; +class DamageSource; + +class Painting : public HangingEntity +{ + +public: + eINSTANCEOF GetType() { return eTYPE_PAINTING; } + static Entity *create(Level *level) { return new Painting(level); } + +private: + //int checkInterval; + +public: + enum MotiveEnum { + Kebab = 0, // + Aztec, // + Alban, // + Aztec2, // + Bomb, // + Plant, // + Wasteland, // + + Pool, // + Courbet, // + Sea, // + Sunset, // + Creebet, // + + Wanderer, // + Graham, // + + Match, // + Bust, // + Stage, // + Void, // + SkullAndRoses, // + Wither, + Fighters, // + + Pointer, // + Pigscene, // + BurningSkull, // + + Skeleton, // + DonkeyKong, // + + LAST_VALUE + }; + + // TODO 4J Replace the ENUM with static consts + class Motive + { + public: + static const Motive *values[]; + + static const int MAX_MOTIVE_NAME_LENGTH; + + const wstring name; + const int w, h; + const int uo, vo; + + //private: + Motive(wstring name, int w, int h, int uo, int vo) : name( name ), w( w ), h( h ), uo( uo ), vo( vo ) {}; + }; + +public: +// int dir; +// +// int xTile, yTile, zTile; + + Motive *motive; + +private: + // 4J - added for common ctor code + void _init( Level *level ); + +public: + Painting(Level *level); + Painting(Level *level, int xTile, int yTile, int zTile, int dir); + Painting(Level *level, int x, int y, int z, int dir, wstring motiveName); + + // 4J Stu - Added this so that we can use some shared_ptr functions that were needed in the ctor + void PaintingPostConstructor(int dir); + +protected: + //void defineSynchedData(); + +public: + //void setDir(int dir); + +private: + //float offs(int w); + +public: + //virtual void tick(); + //bool survives(); + //virtual bool isPickable(); + //virtual bool hurt(DamageSource *source, int damage); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + //static Motive *randomMotive(); + + //virtual void move(double xa, double ya, double za, bool noEntityCubes=false); // 4J - added noEntityCubes parameter + //virtual void push(double xa, double ya, double za); + + virtual int getWidth(); + virtual int getHeight(); + virtual void dropItem(); +}; diff --git a/Minecraft.World/PanicGoal.cpp b/Minecraft.World/PanicGoal.cpp new file mode 100644 index 00000000..da951ca2 --- /dev/null +++ b/Minecraft.World/PanicGoal.cpp @@ -0,0 +1,35 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.util.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.phys.h" +#include "PanicGoal.h" + +PanicGoal::PanicGoal(PathfinderMob *mob, float speed) +{ + this->mob = mob; + this->speed = speed; + setRequiredControlFlags(Control::MoveControlFlag); +} + +bool PanicGoal::canUse() +{ + if (mob->getLastHurtByMob() == NULL) return false; + Vec3 *pos = RandomPos::getPos(dynamic_pointer_cast(mob->shared_from_this()), 5, 4); + if (pos == NULL) return false; + posX = pos->x; + posY = pos->y; + posZ = pos->z; + return true; +} + +void PanicGoal::start() +{ + mob->getNavigation()->moveTo(posX, posY, posZ, speed); +} + +bool PanicGoal::canContinueToUse() +{ + return !mob->getNavigation()->isDone(); +} \ No newline at end of file diff --git a/Minecraft.World/PanicGoal.h b/Minecraft.World/PanicGoal.h new file mode 100644 index 00000000..03d82782 --- /dev/null +++ b/Minecraft.World/PanicGoal.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Goal.h" + +class PathfinderMob; + +class PanicGoal : public Goal +{ +private: + PathfinderMob *mob; + float speed; + double posX, posY, posZ; + +public: + PanicGoal(PathfinderMob *mob, float speed); + + virtual bool canUse(); + virtual void start(); + virtual bool canContinueToUse(); +}; \ No newline at end of file diff --git a/Minecraft.World/ParticleTypes.h b/Minecraft.World/ParticleTypes.h new file mode 100644 index 00000000..d822008e --- /dev/null +++ b/Minecraft.World/ParticleTypes.h @@ -0,0 +1,56 @@ +#pragma once + +// 4J-PB added to avoid string compares on adding particles +enum ePARTICLE_TYPE +{ + eParticleType_bubble, + eParticleType_smoke, + eParticleType_note, + eParticleType_netherportal, // 4J - This particle should only be used by the Nether portal. Everything else should use eParticleType_end + eParticleType_endportal, // 4J - Seperated this from torches and fires + eParticleType_explode, + eParticleType_flame, + eParticleType_lava, + eParticleType_footstep, + eParticleType_splash, + eParticleType_largesmoke, + eParticleType_reddust, + eParticleType_snowballpoof, + eParticleType_snowshovel, + eParticleType_slime, + eParticleType_heart, + eParticleType_suspended, + eParticleType_depthsuspend, + eParticleType_crit, + eParticleType_hugeexplosion, + eParticleType_largeexplode, + eParticleType_townaura, + eParticleType_spell, + eParticleType_mobSpell, + eParticleType_instantSpell, + eParticleType_magicCrit, + eParticleType_dripWater, + eParticleType_dripLava, + eParticleType_enchantmenttable, + eParticleType_dragonbreath, + eParticleType_ender, // 4J Added - These are things that used the "portal" particle but are actually end related entities + eParticleType_angryVillager, + eParticleType_happyVillager, + + // 4J-JEV: In the java, the particle name was used to sneak parameters in for the Terrain and IconCrack particle constructors. + + eParticleType_iconcrack_base = 0x100000, // There's range of iconcrack particle types based on item id and data. + eParticleType_iconcrack_last = 0x1FFFFF, + eParticleType_tilecrack_base = 0x200000, // There's a range of tilecrack particle types based on tile id and data. + eParticleType_tilecrack_last = 0x2FFFFF, + // 0x0000FF, <- these bits are for storing the data value. + // 0x0FFF00, <- these bits are for encoding tile/item id. + // 0x300000, <- these bits show if its an icon/tile or not. + +}; + +#define PARTICLE_TILECRACK(id,data) ( (ePARTICLE_TYPE) ( ((int) eParticleType_tilecrack_base) | ((0x0FFF & id) << 8) | (0x0FF & data)) ) +#define PARTICLE_ICONCRACK(id,data) ( (ePARTICLE_TYPE) ( ((int) eParticleType_iconcrack_base) | ((0x0FFF & id) << 8) | (0x0FF & data)) ) + +#define PARTICLE_CRACK_ID(ePType) ((0x0FFF00 & (int)ePType) >> 8) +#define PARTICLE_CRACK_DATA(ePType) (0x0FF & (int)ePType) \ No newline at end of file diff --git a/Minecraft.World/Path.cpp b/Minecraft.World/Path.cpp new file mode 100644 index 00000000..538917f6 --- /dev/null +++ b/Minecraft.World/Path.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.pathfinder.h" +#include "path.h" + +Path::~Path() +{ + if( nodes.data ) + { + for( int i = 0; i < nodes.length; i++ ) + delete nodes.data[i]; + delete[] nodes.data; + } +} + +Path::Path(NodeArray nodes) +{ + index = 0; + + length = nodes.length; + // 4J - copying these nodes over from a NodeArray (which is an array of Node * references) to just a straight array of Nodes, + // so that this Path is no longer dependent of Nodes allocated elsewhere and can handle its own destruction + // Note: cameFrom pointer will be useless now but that isn't used once this is just a path + this->nodes = NodeArray(length); + + for( int i = 0; i < length; i++ ) + { + this->nodes.data[i] = new Node(); + memcpy(this->nodes.data[i],nodes[i],sizeof(Node)); + } +} + +void Path::next() +{ + index++; +} + +bool Path::isDone() +{ + return index >= length; +} + +Node *Path::last() +{ + if (length > 0) + { + return nodes[length - 1]; + } + return NULL; +} + +Node *Path::get(int i) +{ + return nodes[i]; +} + +int Path::getSize() +{ + return length; +} + +void Path::setSize(int length) +{ + this->length = length; +} + +int Path::getIndex() +{ + return index; +} + +void Path::setIndex(int index) +{ + this->index = index; +} + +Vec3 *Path::getPos(shared_ptr e, int index) +{ + double x = nodes[index]->x + (int) (e->bbWidth + 1) * 0.5; + double y = nodes[index]->y; + double z = nodes[index]->z + (int) (e->bbWidth + 1) * 0.5; + return Vec3::newTemp(x, y, z); +} + +Vec3 *Path::currentPos(shared_ptr e) +{ + return getPos(e, index); +} + +Vec3 *Path::currentPos() +{ + return Vec3::newTemp( nodes[index]->x, nodes[index]->y, nodes[index]->z ); +} + +bool Path::sameAs(Path *path) +{ + if (path == NULL) return false; + if (path->nodes.length != nodes.length) return false; + for (int i = 0; i < nodes.length; ++i) + if (nodes[i]->x != path->nodes[i]->x || nodes[i]->y != path->nodes[i]->y || nodes[i]->z != path->nodes[i]->z) return false; + return true; +} + +bool Path::endsIn(Vec3 *pos) +{ + Node *lastNode = last(); + if (lastNode == NULL) return false; + return lastNode->x == (int) pos->x && lastNode->y == (int) pos->y && lastNode->z == (int) pos->z; +} + +bool Path::endsInXZ(Vec3 *pos) +{ + Node *lastNode = last(); + if (lastNode == NULL) return false; + return lastNode->x == (int) pos->x && lastNode->z == (int) pos->z; +} \ No newline at end of file diff --git a/Minecraft.World/Path.h b/Minecraft.World/Path.h new file mode 100644 index 00000000..01f56e72 --- /dev/null +++ b/Minecraft.World/Path.h @@ -0,0 +1,31 @@ +#pragma once + +class Path +{ + friend class PathFinder; + +private: + NodeArray nodes; + int index; + int length; + +public: + Path(NodeArray nodes); + ~Path(); + + void next(); + bool isDone(); + Node *last() ; + Node *get(int i); + int getSize(); + void setSize(int length); + int getIndex(); + void setIndex(int index); + Vec3 *getPos(shared_ptr e, int index); + NodeArray Getarray(); + Vec3 *currentPos(shared_ptr e); + Vec3 *currentPos(); + bool sameAs(Path *path); + bool endsIn(Vec3 *pos); + bool endsInXZ(Vec3 *pos); +}; diff --git a/Minecraft.World/PathFinder.cpp b/Minecraft.World/PathFinder.cpp new file mode 100644 index 00000000..878c74f1 --- /dev/null +++ b/Minecraft.World/PathFinder.cpp @@ -0,0 +1,271 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "BinaryHeap.h" +#include "Node.h" +#include "Path.h" +#include "PathFinder.h" + +PathFinder::PathFinder(LevelSource *level, bool canPassDoors, bool canOpenDoors, bool avoidWater, bool canFloat) +{ + neighbors = new NodeArray(32); + + this->canPassDoors = canPassDoors; + this->canOpenDoors = canOpenDoors; + this->avoidWater = avoidWater; + this->canFloat = canFloat; + this->level = level; +} + +PathFinder::~PathFinder() +{ + // All the nodes should be uniquely referenced in the nodes map, and everything else should just be duplicate + // references to the same things, so just need to destroy their containers + delete [] neighbors->data; + delete neighbors; + AUTO_VAR(itEnd, nodes.end()); + for( AUTO_VAR(it, nodes.begin()); it != itEnd; it++ ) + { + delete it->second; + } +} + +Path *PathFinder::findPath(Entity *from, Entity *to, float maxDist) +{ + return findPath(from, to->x, to->bb->y0, to->z, maxDist); +} + +Path *PathFinder::findPath(Entity *from, int x, int y, int z, float maxDist) +{ + return findPath(from, x + 0.5f, y + 0.5f, z + 0.5f, maxDist); +} + +Path *PathFinder::findPath(Entity *e, double xt, double yt, double zt, float maxDist) +{ + openSet.clear(); + nodes.clear(); + + bool resetAvoidWater = avoidWater; + int startY = Mth::floor(e->bb->y0 + 0.5f); + if (canFloat && e->isInWater()) + { + startY = (int) (e->bb->y0); + int tileId = level->getTile((int) Mth::floor(e->x), startY, (int) Mth::floor(e->z)); + while (tileId == Tile::water_Id || tileId == Tile::calmWater_Id) + { + ++startY; + tileId = level->getTile((int) Mth::floor(e->x), startY, (int) Mth::floor(e->z)); + } + resetAvoidWater = avoidWater; + avoidWater = false; + } else startY = Mth::floor(e->bb->y0 + 0.5f); + + Node *from = getNode((int) floor(e->bb->x0), startY, (int) floor(e->bb->z0)); + Node *to = getNode((int) floor(xt - e->bbWidth / 2), (int) floor(yt), (int) floor(zt - e->bbWidth / 2)); + + Node *size = new Node((int) floor(e->bbWidth + 1), (int) floor(e->bbHeight + 1), (int) floor(e->bbWidth + 1)); + Path *path = findPath(e, from, to, size, maxDist); + delete size; + + avoidWater = resetAvoidWater; + return path; +} + +// function A*(start,goal) +Path *PathFinder::findPath(Entity *e, Node *from, Node *to, Node *size, float maxDist) +{ + from->g = 0; + from->h = from->distanceToSqr(to); + from->f = from->h; + + openSet.clear(); + openSet.insert(from); + + Node *closest = from; + + while (!openSet.isEmpty()) + { + Node *x = openSet.pop(); + + if (x->equals(to)) + { + return reconstruct_path(from, to); + } + + if (x->distanceToSqr(to) < closest->distanceToSqr(to)) + { + closest = x; + } + x->closed = true; + + int neighborCount = getNeighbors(e, x, size, to, maxDist); + for (int i = 0; i < neighborCount; i++) + { + Node *y = neighbors->data[i]; + + float tentative_g_score = x->g + x->distanceToSqr(y); + if (!y->inOpenSet() || tentative_g_score < y->g) + { + y->cameFrom = x; + y->g = tentative_g_score; + y->h = y->distanceToSqr(to); + if (y->inOpenSet()) + { + openSet.changeCost(y, y->g + y->h); + } + else + { + y->f = y->g + y->h; + openSet.insert(y); + } + } + } + } + + if (closest == from) return NULL; + return reconstruct_path(from, closest); +} + +int PathFinder::getNeighbors(Entity *entity, Node *pos, Node *size, Node *target, float maxDist) +{ + int p = 0; + + int jumpSize = 0; + if (isFree(entity, pos->x, pos->y + 1, pos->z, size) == TYPE_OPEN) jumpSize = 1; + + Node *n = getNode(entity, pos->x, pos->y, pos->z + 1, size, jumpSize); + Node *w = getNode(entity, pos->x - 1, pos->y, pos->z, size, jumpSize); + Node *e = getNode(entity, pos->x + 1, pos->y, pos->z, size, jumpSize); + Node *s = getNode(entity, pos->x, pos->y, pos->z - 1, size, jumpSize); + + if (n != NULL && !n->closed && n->distanceTo(target) < maxDist) neighbors->data[p++] = n; + if (w != NULL && !w->closed && w->distanceTo(target) < maxDist) neighbors->data[p++] = w; + if (e != NULL && !e->closed && e->distanceTo(target) < maxDist) neighbors->data[p++] = e; + if (s != NULL && !s->closed && s->distanceTo(target) < maxDist) neighbors->data[p++] = s; + + return p; +} + +Node *PathFinder::getNode(Entity *entity, int x, int y, int z, Node *size, int jumpSize) +{ + Node *best = NULL; + int pathType = isFree(entity, x, y, z, size); + if (pathType == TYPE_WALKABLE) return getNode(x, y, z); + if (pathType == TYPE_OPEN) best = getNode(x, y, z); + if (best == NULL && jumpSize > 0 && pathType != TYPE_FENCE && pathType != TYPE_TRAP && isFree(entity, x, y + jumpSize, z, size) == TYPE_OPEN) + { + best = getNode(x, y + jumpSize, z); + y += jumpSize; + } + + if (best != NULL) + { + int drop = 0; + int cost = 0; + while (y > 0) + { + cost = isFree(entity, x, y - 1, z, size); + if (avoidWater && cost == TYPE_WATER) return NULL; + if (cost != TYPE_OPEN) break; + // fell too far? + if (++drop >= 4) return NULL; + y--; + + if (y > 0) best = getNode(x, y, z); + } + // fell into lava? + if (cost == TYPE_LAVA) return NULL; + } + + return best; +} + +/*final*/ Node *PathFinder::getNode(int x, int y, int z) +{ + int i = Node::createHash(x, y, z); + Node *node; + AUTO_VAR(it, nodes.find(i)); + if ( it == nodes.end() ) + { + MemSect(54); + node = new Node(x, y, z); + MemSect(0); + nodes.insert( unordered_map::value_type(i, node) ); + } + else + { + node = (*it).second; + } + return node; +} + +int PathFinder::isFree(Entity *entity, int x, int y, int z, Node *size) +{ + return isFree(entity, x, y, z, size, avoidWater, canOpenDoors, canPassDoors); +} + +int PathFinder::isFree(Entity *entity, int x, int y, int z, Node *size, bool avoidWater, bool canOpenDoors, bool canPassDoors) +{ + bool walkable = false; + for (int xx = x; xx < x + size->x; xx++) + for (int yy = y; yy < y + size->y; yy++) + for (int zz = z; zz < z + size->z; zz++) + { + int tileId = entity->level->getTile(xx, yy, zz); + if(tileId <= 0) continue; + if (tileId == Tile::trapdoor_Id) walkable = true; + else if (tileId == Tile::water_Id || tileId == Tile::calmWater_Id) + { + if (avoidWater) return TYPE_WATER; + else walkable = true; + } + else if (!canPassDoors && tileId == Tile::door_wood_Id) + { + return TYPE_BLOCKED; + } + + Tile *tile = Tile::tiles[tileId]; + if (tile->isPathfindable(entity->level, xx, yy, zz)) continue; + if (canOpenDoors && tileId == Tile::door_wood_Id) continue; + + int renderShape = tile->getRenderShape(); + if (renderShape == Tile::SHAPE_FENCE || tileId == Tile::fenceGate_Id || renderShape == Tile::SHAPE_WALL) return TYPE_FENCE; + if (tileId == Tile::trapdoor_Id) return TYPE_TRAP; + Material *m = tile->material; + if (m == Material::lava) + { + if (entity->isInLava()) continue; + return TYPE_LAVA; + } + return TYPE_BLOCKED; + } + + return walkable ? TYPE_WALKABLE : TYPE_OPEN; +} + +// function reconstruct_path(came_from,current_node) +Path *PathFinder::reconstruct_path(Node *from, Node *to) +{ + int count = 1; + Node *n = to; + while (n->cameFrom != NULL) + { + count++; + n = n->cameFrom; + } + + NodeArray nodes = NodeArray(count); + n = to; + nodes.data[--count] = n; + while (n->cameFrom != NULL) + { + n = n->cameFrom; + nodes.data[--count] = n; + } + Path *ret = new Path(nodes); + delete [] nodes.data; + return ret; +} \ No newline at end of file diff --git a/Minecraft.World/PathFinder.h b/Minecraft.World/PathFinder.h new file mode 100644 index 00000000..87321264 --- /dev/null +++ b/Minecraft.World/PathFinder.h @@ -0,0 +1,54 @@ +#pragma once +#include "JavaIntHash.h" +using namespace std; + +class LevelSource; + +class PathFinder +{ +private: + LevelSource *level; + + BinaryHeap openSet; + + // 4J Jev, was a IntHashMap, thought this was close enough. + unordered_map nodes; + + NodeArray *neighbors; + + bool canPassDoors; + bool canOpenDoors; + bool avoidWater; + bool canFloat; + +public: + PathFinder(LevelSource *level, bool canPassDoors, bool canOpenDoors, bool avoidWater, bool canFloat); + ~PathFinder(); + + Path *findPath(Entity *from, Entity *to, float maxDist); + Path *findPath(Entity *from, int x, int y, int z, float maxDist); + +private: + Path *findPath(Entity *e, double xt, double yt, double zt, float maxDist); + + // function A*(start,goal) + Path *findPath(Entity *e, Node *from, Node *to, Node *size, float maxDist); + int getNeighbors(Entity *entity, Node *pos, Node *size, Node *target, float maxDist); + Node *getNode(Entity *entity, int x, int y, int z, Node *size, int jumpSize); + /*final*/ Node *getNode(int x, int y, int z); + +public: + static const int TYPE_TRAP = -4; + static const int TYPE_FENCE = -3; + static const int TYPE_LAVA = -2; + static const int TYPE_WATER = -1; + static const int TYPE_BLOCKED = 0; + static const int TYPE_OPEN = 1; + static const int TYPE_WALKABLE = 2; + + int isFree(Entity *entity, int x, int y, int z, Node *size); + static int isFree(Entity *entity, int x, int y, int z, Node *size, bool avoidWater, bool canOpenDoors, bool canPassDoors); + + // function reconstruct_path(came_from,current_node) + Path *reconstruct_path(Node *from, Node *to); +}; \ No newline at end of file diff --git a/Minecraft.World/PathNavigation.cpp b/Minecraft.World/PathNavigation.cpp new file mode 100644 index 00000000..2bf01672 --- /dev/null +++ b/Minecraft.World/PathNavigation.cpp @@ -0,0 +1,376 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.pathfinder.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "PathNavigation.h" + +PathNavigation::PathNavigation(Mob *mob, Level *level, float maxDist) +{ + this->mob = mob; + this->level = level; + this->maxDist = maxDist; + + path = NULL; + speed = 0.0f; + avoidSun = false; + _tick = 0; + lastStuckCheck = 0; + lastStuckCheckPos = Vec3::newPermanent(0, 0, 0); + _canPassDoors = true; + _canOpenDoors = false; + avoidWater = false; + canFloat = false; +} + +PathNavigation::~PathNavigation() +{ + if(path != NULL) delete path; + delete lastStuckCheckPos; +} + +void PathNavigation::setAvoidWater(bool avoidWater) +{ + this->avoidWater = avoidWater; +} + +bool PathNavigation::getAvoidWater() +{ + return avoidWater; +} + +void PathNavigation::setCanOpenDoors(bool canOpenDoors) +{ + this->_canOpenDoors = canOpenDoors; +} + +bool PathNavigation::canPassDoors() +{ + return _canPassDoors; +} + +void PathNavigation::setCanPassDoors(bool canPass) +{ + _canPassDoors = canPass; +} + +bool PathNavigation::canOpenDoors() +{ + return _canOpenDoors; +} + +void PathNavigation::setAvoidSun(bool avoidSun) +{ + this->avoidSun = avoidSun; +} + +void PathNavigation::setSpeed(float speed) +{ + this->speed = speed; +} + +void PathNavigation::setCanFloat(bool canFloat) +{ + this->canFloat = canFloat; +} + +Path *PathNavigation::createPath(double x, double y, double z) +{ + if (!canUpdatePath()) return NULL; + return level->findPath(mob->shared_from_this(), Mth::floor(x), (int) y, Mth::floor(z), maxDist, _canPassDoors, _canOpenDoors, avoidWater, canFloat); +} + +bool PathNavigation::moveTo(double x, double y, double z, float speed) +{ + MemSect(52); + Path *newPath = createPath(Mth::floor(x), (int) y, Mth::floor(z)); + MemSect(0); + // No need to delete newPath here as this will be copied into the member variable path and the class can assume responsibility for it + return moveTo(newPath, speed); +} + +Path *PathNavigation::createPath(shared_ptr target) +{ + if (!canUpdatePath()) return NULL; + return level->findPath(mob->shared_from_this(), target, maxDist, _canPassDoors, _canOpenDoors, avoidWater, canFloat); +} + +bool PathNavigation::moveTo(shared_ptr target, float speed) +{ + MemSect(53); + Path *newPath = createPath(target); + MemSect(0); + // No need to delete newPath here as this will be copied into the member variable path and the class can assume responsibility for it + if (newPath != NULL) return moveTo(newPath, speed); + else return false; +} + +bool PathNavigation::moveTo(Path *newPath, float speed) +{ + if(newPath == NULL) + { + if(path != NULL) delete path; + path = NULL; + return false; + } + if(!newPath->sameAs(path)) + { + if(path != NULL) delete path; + path = newPath; + } + else + { + delete newPath; + } + if (avoidSun) trimPathFromSun(); + if (path->getSize() == 0) return false; + + this->speed = speed; + Vec3 *mobPos = getTempMobPos(); + lastStuckCheck = _tick; + lastStuckCheckPos->x = mobPos->x; + lastStuckCheckPos->y = mobPos->y; + lastStuckCheckPos->z = mobPos->z; + return true; +} + +Path *PathNavigation::getPath() +{ + return path; +} + +void PathNavigation::tick() +{ + ++_tick; + if (isDone()) return; + + if (canUpdatePath()) updatePath(); + + if (isDone()) return; + Vec3 *target = path->currentPos(mob->shared_from_this()); + if (target == NULL) return; + + mob->getMoveControl()->setWantedPosition(target->x, target->y, target->z, speed); +} + +void PathNavigation::updatePath() +{ + Vec3 *mobPos = getTempMobPos(); + + // find first elevations in path + int firstElevation = path->getSize(); + for (int i = path->getIndex(); path != NULL && i < path->getSize(); ++i) + { + if ((int) path->get(i)->y != (int) mobPos->y) + { + firstElevation = i; + break; + } + } + + // remove those within way point radius (this is not optimal, should + // check canWalkDirectly also) possibly only check next as well + float waypointRadiusSqr = mob->bbWidth * mob->bbWidth; + for (int i = path->getIndex(); i < firstElevation; ++i) + { + Vec3 *pathPos = path->getPos(mob->shared_from_this(), i); + if (mobPos->distanceToSqr(pathPos) < waypointRadiusSqr) + { + path->setIndex(i + 1); + } + } + + // smooth remaining on same elevation + int sx = (int) ceil(mob->bbWidth); + int sy = (int) mob->bbHeight + 1; + int sz = sx; + for (int i = firstElevation - 1; i >= path->getIndex(); --i) + { + if (canMoveDirectly(mobPos, path->getPos(mob->shared_from_this(), i), sx, sy, sz)) + { + path->setIndex(i); + break; + } + } + + // stuck detection (probably pushed off path) + if (_tick - lastStuckCheck > 100) + { + if (mobPos->distanceToSqr(lastStuckCheckPos) < 1.5 * 1.5) stop(); + lastStuckCheck = _tick; + lastStuckCheckPos->x = mobPos->x; + lastStuckCheckPos->y = mobPos->y; + lastStuckCheckPos->z = mobPos->z; + } +} + +bool PathNavigation::isDone() +{ + return path == NULL || path->isDone(); +} + +void PathNavigation::stop() +{ + if(path != NULL) delete path; + path = NULL; +} + +Vec3 *PathNavigation::getTempMobPos() +{ + return Vec3::newTemp(mob->x, getSurfaceY(), mob->z); +} + +int PathNavigation::getSurfaceY() +{ + if (!mob->isInWater() || !canFloat) return (int) (mob->bb->y0 + 0.5); + + int surface = (int) (mob->bb->y0); + int tileId = level->getTile(Mth::floor(mob->x), surface, Mth::floor(mob->z)); + int steps = 0; + while (tileId == Tile::water_Id || tileId == Tile::calmWater_Id) + { + ++surface; + tileId = level->getTile(Mth::floor(mob->x), surface, Mth::floor(mob->z)); + if (++steps > 16) return (int) (mob->bb->y0); + } + return surface; +} + +bool PathNavigation::canUpdatePath() +{ + return mob->onGround || (canFloat && isInLiquid()); +} + +bool PathNavigation::isInLiquid() +{ + return mob->isInWater() || mob->isInLava(); +} + +void PathNavigation::trimPathFromSun() +{ + if (level->canSeeSky(Mth::floor(mob->x), (int) (mob->bb->y0 + 0.5), Mth::floor(mob->z))) return; + + for (int i = 0; i < path->getSize(); ++i) + { + Node *n = path->get(i); + if (level->canSeeSky((int) n->x, (int) n->y, (int) n->z)) + { + path->setSize(i - 1); + return; + } + } +} + +bool PathNavigation::canMoveDirectly(Vec3 *startPos, Vec3 *stopPos, int sx, int sy, int sz) +{ + + int gridPosX = Mth::floor(startPos->x); + int gridPosZ = Mth::floor(startPos->z); + + double dirX = stopPos->x - startPos->x; + double dirZ = stopPos->z - startPos->z; + double distSqr = dirX * dirX + dirZ * dirZ; + if (distSqr < 0.00000001) return false; + + double nf = 1 / sqrt(distSqr); + dirX *= nf; + dirZ *= nf; + + sx += 2; + sz += 2; + if (!canWalkOn(gridPosX, (int) startPos->y, gridPosZ, sx, sy, sz, startPos, dirX, dirZ)) return false; + sx -= 2; + sz -= 2; + + double deltaX = 1 / abs(dirX); + double deltaZ = 1 / abs(dirZ); + + double maxX = gridPosX * 1 - startPos->x; + double maxZ = gridPosZ * 1 - startPos->z; + if (dirX >= 0) maxX += 1; + if (dirZ >= 0) maxZ += 1; + maxX /= dirX; + maxZ /= dirZ; + + int stepX = dirX < 0 ? -1 : 1; + int stepZ = dirZ < 0 ? -1 : 1; + int gridGoalX = Mth::floor(stopPos->x); + int gridGoalZ = Mth::floor(stopPos->z); + int currentDirX = gridGoalX - gridPosX; + int currentDirZ = gridGoalZ - gridPosZ; + while (currentDirX * stepX > 0 || currentDirZ * stepZ > 0) + { + if (maxX < maxZ) + { + maxX += deltaX; + gridPosX += stepX; + currentDirX = gridGoalX - gridPosX; + } + else + { + maxZ += deltaZ; + gridPosZ += stepZ; + currentDirZ = gridGoalZ - gridPosZ; + } + + if (!canWalkOn(gridPosX, (int) startPos->y, gridPosZ, sx, sy, sz, startPos, dirX, dirZ)) return false; + } + return true; +} + +bool PathNavigation::canWalkOn(int x, int y, int z, int sx, int sy, int sz, Vec3 *startPos, double goalDirX, double goalDirZ) +{ + + int startX = x - sx / 2; + int startZ = z - sz / 2; + + if (!canWalkAbove(startX, y, startZ, sx, sy, sz, startPos, goalDirX, goalDirZ)) return false; + + // lava or water or air under + for (int xx = startX; xx < startX + sx; xx++) + { + for (int zz = startZ; zz < startZ + sz; zz++) + { + double dirX = xx + 0.5 - startPos->x; + double dirZ = zz + 0.5 - startPos->z; + if (dirX * goalDirX + dirZ * goalDirZ < 0) continue; + int tile = level->getTile(xx, y - 1, zz); + if (tile <= 0) return false; + Material *m = Tile::tiles[tile]->material; + if (m == Material::water && !mob->isInWater()) return false; + if (m == Material::lava) return false; + } + } + + return true; +} + +bool PathNavigation::canWalkAbove(int startX, int startY, int startZ, int sx, int sy, int sz, Vec3 *startPos, double goalDirX, double goalDirZ) +{ + + for (int xx = startX; xx < startX + sx; xx++) + { + for (int yy = startY; yy < startY + sy; yy++) + { + for (int zz = startZ; zz < startZ + sz; zz++) + { + + double dirX = xx + 0.5 - startPos->x; + double dirZ = zz + 0.5 - startPos->z; + if (dirX * goalDirX + dirZ * goalDirZ < 0) continue; + int tile = level->getTile(xx, yy, zz); + if (tile <= 0) continue; + if (!Tile::tiles[tile]->isPathfindable(level, xx, yy, zz)) return false; + } + } + } + return true; +} + +void PathNavigation::setLevel(Level *level) +{ + this->level = level; +} \ No newline at end of file diff --git a/Minecraft.World/PathNavigation.h b/Minecraft.World/PathNavigation.h new file mode 100644 index 00000000..b8a8cfd6 --- /dev/null +++ b/Minecraft.World/PathNavigation.h @@ -0,0 +1,67 @@ +#pragma once + +class Mob; +class Level; +class Path; + +class PathNavigation +{ +private: + Mob *mob; + Level *level; + Path *path; + float speed; + float maxDist; + bool avoidSun; + int _tick; + int lastStuckCheck; + Vec3 *lastStuckCheckPos; + + bool _canPassDoors; + bool _canOpenDoors; + bool avoidWater; + bool canFloat; + +public: + PathNavigation(Mob *mob, Level *level, float maxDist); + ~PathNavigation(); + + void setAvoidWater(bool avoidWater); + bool getAvoidWater(); + void setCanOpenDoors(bool canOpenDoors); + bool canPassDoors(); + void setCanPassDoors(bool canPass); + bool canOpenDoors(); + void setAvoidSun(bool avoidSun); + void setSpeed(float speed); + void setCanFloat(bool canFloat); + Path *createPath(double x, double y, double z); + bool moveTo(double x, double y, double z, float speed); + Path *createPath(shared_ptr target); + bool moveTo(shared_ptr target, float speed); + bool moveTo(Path *newPath, float speed); + Path *getPath(); + void tick(); + +private: + void updatePath(); + +public: + bool isDone(); + + void stop(); + +private: + Vec3 *getTempMobPos(); + int getSurfaceY(); + bool canUpdatePath(); + bool isInLiquid(); + void trimPathFromSun(); + bool canMoveDirectly(Vec3 *startPos, Vec3 *stopPos, int sx, int sy, int sz); + bool canWalkOn(int x, int y, int z, int sx, int sy, int sz, Vec3 *startPos, double goalDirX, double goalDirZ); + bool canWalkAbove(int startX, int startY, int startZ, int sx, int sy, int sz, Vec3 *startPos, double goalDirX, double goalDirZ); + +public: + // 4J Added override to update ai elements when loading entity from schematics + void setLevel(Level *level); +}; \ No newline at end of file diff --git a/Minecraft.World/PathfinderMob.cpp b/Minecraft.World/PathfinderMob.cpp new file mode 100644 index 00000000..b72e064f --- /dev/null +++ b/Minecraft.World/PathfinderMob.cpp @@ -0,0 +1,264 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.pathfinder.h" +#include "net.minecraft.world.phys.h" +#include "SharedConstants.h" +#include "PathfinderMob.h" + + + +PathfinderMob::PathfinderMob(Level *level) : Mob( level ) +{ + path = NULL; + attackTarget = nullptr; + holdGround = false; + fleeTime = 0; +} + +bool PathfinderMob::shouldHoldGround() +{ + return false; +} + +PathfinderMob::~PathfinderMob() +{ + delete path; +} + +void PathfinderMob::serverAiStep() +{ + if (fleeTime > 0) fleeTime--; + holdGround = shouldHoldGround(); + float maxDist = 16; + + if (attackTarget == NULL) + { + attackTarget = findAttackTarget(); + if (attackTarget != NULL) + { + setPath(level->findPath(shared_from_this(), attackTarget, maxDist, true, false, false, true)); // 4J - changed to setPath from path = + } + } + else + { + if (attackTarget->isAlive()) + { + float d = attackTarget->distanceTo(shared_from_this()); + if (canSee(attackTarget)) + { + checkHurtTarget(attackTarget, d); + } + } + else + { + attackTarget = nullptr; + } + } + + /* + * if (holdGround) { xxa = 0; yya = 0; jumping = false; return; } + */ + + // 4J - a few changes here so that we can call findRandomStrollLocation for a sub-set of things that it normally wouldn't be in the java game. + // This is so that we can have entities wander around a little, in order that we can measure how far they wander and then determine (if they wander too far) that + // they aren't enclosed. We don't want the extra network overhead of just having Everything wandering round all the time, so have put a management system in place + // that selects a subset of entities which have had their flag set through the considerForExtraWandering method so that these can keep doing random strolling. + + if (!holdGround && (attackTarget != NULL && (path == NULL || random->nextInt(20) == 0))) + { + setPath(level->findPath(shared_from_this(), attackTarget, maxDist, true, false, false, true));// 4J - changed to setPath from path = + } + else if (!holdGround && ((path == NULL && (random->nextInt(180) == 0) || fleeTime > 0) || (random->nextInt(120) == 0 || fleeTime > 0))) + { + if(noActionTime < SharedConstants::TICKS_PER_SECOND * 5) + { + findRandomStrollLocation(); + } + } + else if (!holdGround && (path == NULL ) ) + { + if( ( noActionTime >= SharedConstants::TICKS_PER_SECOND * 5 ) && isExtraWanderingEnabled() ) + { + // This entity wouldn't normally be randomly strolling. However, if our management system says that it should do, then do. Don't + // bother waiting for random conditions to be met before picking a direction though as the point here is to see if it is possible to + // stroll out of a given area and so waiting around is just wasting time + findRandomStrollLocation(getWanderingQuadrant()); + } + } + + // Consider this for extra strolling if it is protected against despawning. We aren't interested in ones that aren't protected as the whole point of this + // extra wandering is to potentially transition from protected to not protected. + considerForExtraWandering( isDespawnProtected() ); + + int yFloor = Mth::floor(bb->y0 + 0.5f); + + bool inWater = isInWater(); + bool inLava = isInLava(); + xRot = 0; + if (path == NULL || random->nextInt(100) == 0) + { + this->Mob::serverAiStep(); + setPath(NULL);// 4J - changed to setPath from path = + return; + } + + Vec3 *target = path->currentPos(shared_from_this()); + double r = bbWidth * 2; + while (target != NULL && target->distanceToSqr(x, target->y, z) < r * r) + { + path->next(); + if (path->isDone()) + { + target = NULL; + setPath(NULL); // 4J - changed to setPath from path = + } + else target = path->currentPos(shared_from_this()); + } + + jumping = false; + if (target != NULL) + { + double xd = target->x - x; + double zd = target->z - z; + double yd = target->y - yFloor; + float yRotD = (float) (atan2(zd, xd) * 180 / PI) - 90; + float rotDiff = Mth::wrapDegrees(yRotD - yRot); + yya = runSpeed; + if (rotDiff > MAX_TURN) + { + rotDiff = MAX_TURN; + } + if (rotDiff < -MAX_TURN) + { + rotDiff = -MAX_TURN; + } + yRot += rotDiff; + + if (holdGround) + { + if (attackTarget != NULL) + { + double xd2 = attackTarget->x - x; + double zd2 = attackTarget->z - z; + + float oldyRot = yRot; + yRot = (float) (atan2(zd2, xd2) * 180 / PI) - 90; + + rotDiff = ((oldyRot - yRot) + 90) * PI / 180; + xxa = -Mth::sin(rotDiff) * yya * 1.0f; + yya = Mth::cos(rotDiff) * yya * 1.0f; + } + } + if (yd > 0) + { + jumping = true; + } + } + + if (attackTarget != NULL) + { + lookAt(attackTarget, 30, 30); + } + + if (this->horizontalCollision && !isPathFinding()) jumping = true; + if (random->nextFloat() < 0.8f && (inWater || inLava)) jumping = true; +} + +void PathfinderMob::findRandomStrollLocation(int quadrant/*=-1*/) // 4J - added quadrant +{ + bool hasBest = false; + int xBest = -1; + int yBest = -1; + int zBest = -1; + float best = -99999; + for (int i = 0; i < 10; i++) + { + // 4J - added quadrant parameter to this method so that the caller can request that only stroll locations in one quadrant be found. If -1 is passed then + // behaviour is the same as the java game + int xt, zt; + int yt = Mth::floor(y + random->nextInt(7) - 3); + if( quadrant == -1 ) + { + xt = Mth::floor(x + random->nextInt(13) - 6); + zt = Mth::floor(z + random->nextInt(13) - 6); + } + else + { + int sx = ( ( quadrant & 1 ) ? -1 : 1 ); + int sz = ( ( quadrant & 2 ) ? -1 : 1 ); + xt = Mth::floor(x + random->nextInt(7) * sx); + zt = Mth::floor(z + random->nextInt(7) * sz); + } + float value = getWalkTargetValue(xt, yt, zt); + if (value > best) + { + best = value; + xBest = xt; + yBest = yt; + zBest = zt; + hasBest = true; + } + } + if (hasBest) + { + setPath(level->findPath(shared_from_this(), xBest, yBest, zBest, 10, true, false, false, true)); // 4J - changed to setPath from path = + } +} + +void PathfinderMob::checkHurtTarget(shared_ptr target, float d) +{ +} + +float PathfinderMob::getWalkTargetValue(int x, int y, int z) +{ + return 0; +} + +shared_ptr PathfinderMob::findAttackTarget() +{ + return shared_ptr(); +} + + +bool PathfinderMob::canSpawn() +{ + int xt = Mth::floor(x); + int yt = Mth::floor(bb->y0); + int zt = Mth::floor(z); + return this->Mob::canSpawn() && getWalkTargetValue(xt, yt, zt) >= 0; +} + +bool PathfinderMob::isPathFinding() +{ + return path != NULL; +} + +void PathfinderMob::setPath(Path *path) +{ + delete this->path; + this->path = path; +} + +shared_ptr PathfinderMob::getAttackTarget() +{ + return attackTarget; +} + +void PathfinderMob::setAttackTarget(shared_ptr attacker) +{ + attackTarget = attacker; +} + +float PathfinderMob::getWalkingSpeedModifier() +{ + if (useNewAi()) return 1.0f; + float speed = Mob::getWalkingSpeedModifier(); + if (fleeTime > 0) speed *= 2; + return speed; +} + +bool PathfinderMob::couldWander() +{ + return (noActionTime < SharedConstants::TICKS_PER_SECOND * 5) || ( isExtraWanderingEnabled() ); +} \ No newline at end of file diff --git a/Minecraft.World/PathfinderMob.h b/Minecraft.World/PathfinderMob.h new file mode 100644 index 00000000..fdbadf8c --- /dev/null +++ b/Minecraft.World/PathfinderMob.h @@ -0,0 +1,49 @@ +#pragma once + +#include "Mob.h" + +class Level; +class Path; + +class PathfinderMob : public Mob +{ +private: + static const int MAX_TURN = 30; + +public: + PathfinderMob(Level *level); + virtual ~PathfinderMob(); + +private: + Path *path; + +protected: + shared_ptr attackTarget; + bool holdGround; + int fleeTime; + + virtual bool shouldHoldGround(); + virtual void serverAiStep(); + virtual void findRandomStrollLocation(int quadrant = -1); + virtual void checkHurtTarget(shared_ptr target, float d); + +public: + virtual float getWalkTargetValue(int x, int y, int z); + +protected: + virtual shared_ptr findAttackTarget(); + +public: + virtual bool canSpawn(); + bool isPathFinding(); + void setPath(Path *path); + shared_ptr getAttackTarget(); + void setAttackTarget(shared_ptr attacker); + +protected: + float getWalkingSpeedModifier(); + + // 4J added +public: + virtual bool couldWander(); +}; diff --git a/Minecraft.World/PerformanceTimer.cpp b/Minecraft.World/PerformanceTimer.cpp new file mode 100644 index 00000000..5ed7ee0c --- /dev/null +++ b/Minecraft.World/PerformanceTimer.cpp @@ -0,0 +1,35 @@ +#include "stdafx.h" +#include "PerformanceTimer.h" + +PerformanceTimer::PerformanceTimer() +{ +#ifndef _CONTENT_PACKAGE + // Get the frequency of the timer + LARGE_INTEGER qwTicksPerSec; + QueryPerformanceFrequency( &qwTicksPerSec ); + m_fSecsPerTick = 1.0f / (float)qwTicksPerSec.QuadPart; + + Reset(); +#endif +} + +void PerformanceTimer::Reset() +{ +#ifndef _CONTENT_PACKAGE + QueryPerformanceCounter( &m_qwStartTime ); +#endif +} + +void PerformanceTimer::PrintElapsedTime(const wstring &description) +{ +#ifndef _CONTENT_PACKAGE + LARGE_INTEGER qwNewTime, qwDeltaTime; + + QueryPerformanceCounter( &qwNewTime ); + + qwDeltaTime.QuadPart = qwNewTime.QuadPart - m_qwStartTime.QuadPart; + float fElapsedTime = m_fSecsPerTick * ((FLOAT)(qwDeltaTime.QuadPart)); + + app.DebugPrintf("TIMER: %ls: Elapsed time %f\n", description.c_str(), fElapsedTime); +#endif +} \ No newline at end of file diff --git a/Minecraft.World/PerformanceTimer.h b/Minecraft.World/PerformanceTimer.h new file mode 100644 index 00000000..04a36949 --- /dev/null +++ b/Minecraft.World/PerformanceTimer.h @@ -0,0 +1,13 @@ +#pragma once + +class PerformanceTimer +{ +private: + LARGE_INTEGER m_qwStartTime; + float m_fSecsPerTick; + +public: + PerformanceTimer(); + void Reset(); + void PrintElapsedTime(const wstring &description); +}; \ No newline at end of file diff --git a/Minecraft.World/PerlinNoise.cpp b/Minecraft.World/PerlinNoise.cpp new file mode 100644 index 00000000..b4fd8004 --- /dev/null +++ b/Minecraft.World/PerlinNoise.cpp @@ -0,0 +1,98 @@ +#include "stdafx.h" +#include "PerlinNoise.h" +#include "Mth.h" + +PerlinNoise::PerlinNoise(int levels) +{ + Random random; + init(&random, levels); +} + +PerlinNoise::PerlinNoise(Random *random, int levels) +{ + init(random, levels); +} + +void PerlinNoise::init(Random *random, int levels) +{ + MemSect(2); + this->levels = levels; + noiseLevels = new ImprovedNoise *[levels]; + for (int i = 0; i < levels; i++) + { + noiseLevels[i] = new ImprovedNoise(random); + } + MemSect(0); +} + +PerlinNoise::~PerlinNoise() +{ + for( int i = 0; i < levels; i++ ) + { + delete noiseLevels[i]; + } + delete [] noiseLevels; +} + +double PerlinNoise::getValue(double x, double y) +{ + double value = 0; + double pow = 1; + + for (int i = 0; i < levels; i++) + { + value += noiseLevels[i]->getValue(x * pow, y * pow) / pow; + pow /= 2; + } + + return value; +} + +double PerlinNoise::getValue(double x, double y, double z) +{ + double value = 0; + double pow = 1; + + for (int i = 0; i < levels; i++) + { + value += noiseLevels[i]->getValue(x * pow, y * pow, z * pow) / pow; + pow /= 2; + } + + return value; +} + +doubleArray PerlinNoise::getRegion(doubleArray buffer, int x, int y, int z, int xSize, int ySize, int zSize, double xScale, double yScale, double zScale) +{ + if (buffer.data == NULL) buffer = doubleArray(xSize * ySize * zSize); + else for (unsigned int i = 0; i < buffer.length; i++) + buffer[i] = 0; + + + double pow = 1; + + for (int i = 0; i < levels; i++) + { + // value += noiseLevels[i].getValue(x * pow, y * pow, z * pow) / pow; + double xx = x * pow * xScale; + double yy = y * pow * yScale; + double zz = z * pow * zScale; + __int64 xb = Mth::lfloor(xx); + __int64 zb = Mth::lfloor(zz); + xx -= xb; + zz -= zb; + xb %= 16777216; + zb %= 16777216; + xx += xb; + zz += zb; + noiseLevels[i]->add(buffer, xx, yy, zz, xSize, ySize, zSize, xScale * pow, yScale * pow, zScale * pow, pow); + pow /= 2; + } + + return buffer; +} + +doubleArray PerlinNoise::getRegion(doubleArray sr, int x, int z, int xSize, int zSize, double xScale, double zScale, double pow) +{ + return getRegion(sr, x, 10, z, xSize, 1, zSize, xScale, 1, zScale); +} \ No newline at end of file diff --git a/Minecraft.World/PerlinNoise.h b/Minecraft.World/PerlinNoise.h new file mode 100644 index 00000000..25b67627 --- /dev/null +++ b/Minecraft.World/PerlinNoise.h @@ -0,0 +1,24 @@ +#pragma once +#include "Synth.h" +#include "ImprovedNoise.h" + + +class PerlinNoise: public Synth +{ + friend class PerlinNoise_SPU; +private: + ImprovedNoise **noiseLevels; + int levels; + +public: + PerlinNoise(int levels); + PerlinNoise(Random *random, int levels); + + void init(Random *random, int levels); + ~PerlinNoise(); + + virtual double getValue(double x, double y); + double getValue(double x, double y, double z); + doubleArray getRegion(doubleArray buffer, int x, int y, int z, int xSize, int ySize, int zSize, double xScale, double yScale, double zScale); + doubleArray getRegion(doubleArray sr, int x, int z, int xSize, int zSize, double xScale, double zScale, double pow); +}; diff --git a/Minecraft.World/PerlinSimplexNoise.cpp b/Minecraft.World/PerlinSimplexNoise.cpp new file mode 100644 index 00000000..dbc570a4 --- /dev/null +++ b/Minecraft.World/PerlinSimplexNoise.cpp @@ -0,0 +1,115 @@ +#include "stdafx.h" +#include "PerlinSimplexNoise.h" + +PerlinSimplexNoise::PerlinSimplexNoise(int levels) +{ + Random random; + init(&random,levels); +} + +PerlinSimplexNoise::PerlinSimplexNoise(Random *random, int levels) +{ + init(random,levels); + delete random; +} + +void PerlinSimplexNoise::init(Random *random, int levels) +{ + MemSect(3); + this->levels = levels; + noiseLevels = new SimplexNoise *[levels]; + for (int i = 0; i < levels; i++) + { + noiseLevels[i] = new SimplexNoise(random); + } + MemSect(0); +} + +PerlinSimplexNoise::~PerlinSimplexNoise() +{ + for(int i = 0; i < levels; i++ ) + { + delete noiseLevels[i]; + } + delete[] noiseLevels; +} + +double PerlinSimplexNoise::getValue(double x, double y) +{ + double value = 0; + double pow = 1; + + for (int i = 0; i < levels; i++) + { + value += noiseLevels[i]->getValue(x * pow, y * pow) / pow; + pow /= 2; + } + + return value; +} + +double PerlinSimplexNoise::getValue(double x, double y, double z) +{ + double value = 0; + double pow = 1; + + for (int i = 0; i < levels; i++) + { + value += noiseLevels[i]->getValue(x * pow, y * pow, z * pow) / pow; + pow /= 2; + } + + return value; +} + +doubleArray PerlinSimplexNoise::getRegion(doubleArray buffer, double x, double y, int xSize, int ySize, double xScale, double yScale, double sizeScale) +{ + return getRegion(buffer, x, y, xSize, ySize, xScale, yScale, sizeScale, 0.5); +} + +doubleArray PerlinSimplexNoise::getRegion(doubleArray buffer, double x, double y, int xSize, int ySize, double xScale, double yScale, double sizeScale, double powScale) +{ + xScale/=1.5; + yScale/=1.5; + + if (buffer.data == NULL || (int) buffer.length < xSize * ySize) + { + if( buffer.data ) delete [] buffer.data; + buffer = doubleArray(xSize * ySize); + } + else for (unsigned int i = 0; i < buffer.length; i++) + buffer[i] = 0; + + + double pow = 1; + double scale = 1; + for (int i = 0; i < levels; i++) + { + noiseLevels[i]->add(buffer, x, y, xSize, ySize, xScale * scale, yScale * scale, 0.55/pow); + scale *= sizeScale; + pow *= powScale; + } + + return buffer; +} + +doubleArray PerlinSimplexNoise::getRegion(doubleArray buffer, double x, double y, double z, int xSize, int ySize, int zSize, double xScale, double yScale, double zScale) +{ + xScale/=1.5; + yScale/=1.5; + + if (buffer.data == NULL) buffer = doubleArray(xSize * ySize * zSize); + else for (unsigned int i = 0; i < buffer.length; i++) + buffer[i] = 0; + + + double pow = 1; + + for (int i = 0; i < levels; i++) { + // value += noiseLevels[i].getValue(x * pow, y * pow, z * pow) / pow; + noiseLevels[i]->add(buffer, x, y, z, xSize, ySize, zSize, xScale * pow, yScale * pow, zScale * pow, 0.55/pow); + pow *= 0.5; + } + + return buffer; +} \ No newline at end of file diff --git a/Minecraft.World/PerlinSimplexNoise.h b/Minecraft.World/PerlinSimplexNoise.h new file mode 100644 index 00000000..39625988 --- /dev/null +++ b/Minecraft.World/PerlinSimplexNoise.h @@ -0,0 +1,23 @@ +#pragma once +#include "Synth.h" +#include "SimplexNoise.h" + +class PerlinSimplexNoise : public Synth +{ +private: + SimplexNoise **noiseLevels; + int levels; + +public: + PerlinSimplexNoise(int levels); + PerlinSimplexNoise(Random *random, int levels); + void init(Random *random, int levels); + ~PerlinSimplexNoise(); + + virtual double getValue(double x, double y); + double getValue(double x, double y, double z); + + doubleArray getRegion(doubleArray buffer, double x, double y, int xSize, int ySize, double xScale, double yScale, double sizeScale); + doubleArray getRegion(doubleArray buffer, double x, double y, int xSize, int ySize, double xScale, double yScale, double sizeScale, double powScale); + doubleArray getRegion(doubleArray buffer, double x, double y, double z, int xSize, int ySize, int zSize, double xScale, double yScale, double zScale); +}; \ No newline at end of file diff --git a/Minecraft.World/PickaxeItem.cpp b/Minecraft.World/PickaxeItem.cpp new file mode 100644 index 00000000..5876b9bb --- /dev/null +++ b/Minecraft.World/PickaxeItem.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.tile.h" +#include "PickaxeItem.h" + +TileArray *PickaxeItem::diggables = NULL; + +void PickaxeItem::staticCtor() +{ + PickaxeItem::diggables = new TileArray( PICKAXE_DIGGABLES); + diggables->data[0] = Tile::stoneBrick; + diggables->data[1] = Tile::stoneSlab; + diggables->data[2] = Tile::stoneSlabHalf; + diggables->data[3] = Tile::rock; + diggables->data[4] = Tile::sandStone; + diggables->data[5] = Tile::mossStone; + diggables->data[6] = Tile::ironOre; + diggables->data[7] = Tile::ironBlock; + diggables->data[8] = Tile::coalOre; + diggables->data[9] = Tile::goldBlock; + diggables->data[10] = Tile::goldOre; + diggables->data[11] = Tile::diamondOre; + diggables->data[12] = Tile::diamondBlock; + diggables->data[13] = Tile::ice; + diggables->data[14] = Tile::hellRock; + diggables->data[15] = Tile::lapisOre; + diggables->data[16] = Tile::lapisBlock; + // 4J - brought forward from 1.2.3 + diggables->data[17] = Tile::redStoneOre; + diggables->data[18] = Tile::redStoneOre_lit; + diggables->data[19] = Tile::rail; + diggables->data[20] = Tile::detectorRail; + diggables->data[21] = Tile::goldenRail; +} + +PickaxeItem::PickaxeItem(int id, const Tier *tier) : DiggerItem(id, 2, tier, diggables) +{ +} + +bool PickaxeItem::canDestroySpecial(Tile *tile) +{ + if (tile == Tile::obsidian) return tier->getLevel() == 3; + if (tile == Tile::diamondBlock || tile == Tile::diamondOre) return tier->getLevel() >= 2; + if (tile == Tile::emeraldBlock || tile == Tile::emeraldOre) return tier->getLevel() >= 2; + if (tile == Tile::goldBlock || tile == Tile::goldOre) return tier->getLevel() >= 2; + if (tile == Tile::ironBlock || tile == Tile::ironOre) return tier->getLevel() >= 1; + if (tile == Tile::lapisBlock || tile == Tile::lapisOre) return tier->getLevel() >= 1; + if (tile == Tile::redStoneOre || tile == Tile::redStoneOre_lit) return tier->getLevel() >= 2; + if (tile->material == Material::stone) return true; + if (tile->material == Material::metal) return true; + if (tile->material == Material::heavyMetal) return true; + return false; +} + +// 4J - brought forward from 1.2.3 +float PickaxeItem::getDestroySpeed(shared_ptr itemInstance, Tile *tile) +{ + if (tile != NULL && (tile->material == Material::metal || tile->material == Material::heavyMetal || tile->material == Material::stone)) + { + return speed; + } + return DiggerItem::getDestroySpeed(itemInstance, tile); +} diff --git a/Minecraft.World/PickaxeItem.h b/Minecraft.World/PickaxeItem.h new file mode 100644 index 00000000..52dac510 --- /dev/null +++ b/Minecraft.World/PickaxeItem.h @@ -0,0 +1,20 @@ +#pragma once + +#include "DiggerItem.h" + +#define PICKAXE_DIGGABLES 22 + +class PickaxeItem : public DiggerItem +{ +private: + static TileArray *diggables; + +public: // + static void staticCtor(); + + PickaxeItem(int id, const Tier *tier); + +public: + virtual bool canDestroySpecial(Tile *tile); + virtual float getDestroySpeed(shared_ptr itemInstance, Tile *tile); // 4J - brought forward from 1.2.3 +}; diff --git a/Minecraft.World/Pig.cpp b/Minecraft.World/Pig.cpp new file mode 100644 index 00000000..b556a936 --- /dev/null +++ b/Minecraft.World/Pig.cpp @@ -0,0 +1,192 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.global.h" +#include "Pig.h" +#include "..\Minecraft.Client\Textures.h" +#include "MobCategory.h" + + + +Pig::Pig(Level *level) : Animal( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_PIG; // 4J - was L"/mob/pig.png"; + this->setSize(0.9f, 0.9f); + + getNavigation()->setAvoidWater(true); + float walkSpeed = 0.25f; + goalSelector.addGoal(0, new FloatGoal(this)); + goalSelector.addGoal(1, new PanicGoal(this, 0.38f)); + goalSelector.addGoal(2, controlGoal = new ControlledByPlayerGoal(this, 0.34f, walkSpeed)); + goalSelector.addGoal(3, new BreedGoal(this, walkSpeed)); + goalSelector.addGoal(4, new TemptGoal(this, 0.3f, Item::carrotOnAStick_Id, false)); + goalSelector.addGoal(4, new TemptGoal(this, 0.25f, Item::carrots_Id, false)); + goalSelector.addGoal(5, new FollowParentGoal(this, 0.28f)); + goalSelector.addGoal(6, new RandomStrollGoal(this, walkSpeed)); + goalSelector.addGoal(7, new LookAtPlayerGoal(this, typeid(Player), 6)); + goalSelector.addGoal(8, new RandomLookAroundGoal(this)); +} + +bool Pig::useNewAi() +{ + return true; +} + +int Pig::getMaxHealth() +{ + return 10; +} + +bool Pig::canBeControlledByRider() +{ + shared_ptr item = dynamic_pointer_cast(rider.lock())->getCarriedItem(); + + return item != NULL && item->id == Item::carrotOnAStick_Id; +} + +void Pig::defineSynchedData() +{ + Animal::defineSynchedData(); + entityData->define(DATA_SADDLE_ID, (byte) 0); +} + +void Pig::addAdditonalSaveData(CompoundTag *tag) +{ + Animal::addAdditonalSaveData(tag); + tag->putBoolean(L"Saddle", hasSaddle()); +} + +void Pig::readAdditionalSaveData(CompoundTag *tag) +{ + Animal::readAdditionalSaveData(tag); + setSaddle(tag->getBoolean(L"Saddle")); +} + +int Pig::getAmbientSound() +{ + return eSoundType_MOB_PIG_AMBIENT; +} + +int Pig::getHurtSound() +{ + return eSoundType_MOB_PIG_AMBIENT; +} + +int Pig::getDeathSound() +{ + return eSoundType_MOB_PIG_DEATH; +} + +bool Pig::interact(shared_ptr player) +{ + if(!Animal::interact(player)) + { + if (hasSaddle() && !level->isClientSide && (rider.lock() == NULL || rider.lock() == player)) + { + // 4J HEG - Fixed issue with player not being able to dismount pig (issue #4479) + player->ride( rider.lock() == player ? nullptr : shared_from_this() ); + return true; + } + return false; + } + return true; +} + +int Pig::getDeathLoot() +{ + if (this->isOnFire() ) return Item::porkChop_cooked->id; + return Item::porkChop_raw_Id; +} + +void Pig::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + int count = random->nextInt(3) + 1 + random->nextInt(1 + playerBonusLevel); + + for (int i = 0; i < count; i++) + { + if (isOnFire()) + { + spawnAtLocation(Item::porkChop_cooked_Id, 1); + } + else + { + spawnAtLocation(Item::porkChop_raw_Id, 1); + } + } + if (hasSaddle()) spawnAtLocation(Item::saddle_Id, 1); +} + +bool Pig::hasSaddle() +{ + return (entityData->getByte(DATA_SADDLE_ID) & 1) != 0; +} + +void Pig::setSaddle(bool value) +{ + if (value) + { + entityData->set(DATA_SADDLE_ID, (byte) 1); + } + else + { + entityData->set(DATA_SADDLE_ID, (byte) 0); + } +} + +void Pig::thunderHit(const LightningBolt *lightningBolt) +{ + if (level->isClientSide) return; + shared_ptr pz = shared_ptr( new PigZombie(level) ); + pz->moveTo(x, y, z, yRot, xRot); + level->addEntity(pz); + remove(); +} + +void Pig::causeFallDamage(float distance) +{ + Animal::causeFallDamage(distance); + if (distance > 5 && dynamic_pointer_cast( rider.lock() ) != NULL) + { + (dynamic_pointer_cast(rider.lock()))->awardStat(GenericStats::flyPig(),GenericStats::param_flyPig()); + } +} + +shared_ptr Pig::getBreedOffspring(shared_ptr target) +{ + // 4J - added limit to number of animals that can be bred + if( level->canCreateMore( GetType(), Level::eSpawnType_Breed) ) + { + return shared_ptr( new Pig(level) ); + } + else + { + return nullptr; + } +} + +bool Pig::isFood(shared_ptr itemInstance) +{ + return itemInstance != NULL && itemInstance->id == Item::carrots_Id; +} + +ControlledByPlayerGoal *Pig::getControlGoal() +{ + return controlGoal; +} diff --git a/Minecraft.World/Pig.h b/Minecraft.World/Pig.h new file mode 100644 index 00000000..adcfbf4e --- /dev/null +++ b/Minecraft.World/Pig.h @@ -0,0 +1,58 @@ +#pragma once + +using namespace std; + +#include "Animal.h" + +class Player; +class LightningBolt; +class ControlledByPlayerGoal; + +class Pig : public Animal +{ +public: + eINSTANCEOF GetType() { return eTYPE_PIG; } + static Entity *create(Level *level) { return new Pig(level); } +private: + static const int DATA_SADDLE_ID = 16; + ControlledByPlayerGoal *controlGoal; + +public: + Pig(Level *level); + + virtual bool useNewAi(); + virtual int getMaxHealth(); + virtual bool canBeControlledByRider(); + +protected: + virtual void defineSynchedData(); + +public: + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +protected: + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + +public: + virtual bool interact(shared_ptr player); + +protected: + virtual int getDeathLoot(); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + +public: + bool hasSaddle(); + void setSaddle(bool value); + virtual void thunderHit(const LightningBolt *lightningBolt); + +protected: + virtual void causeFallDamage(float distance); + +public: + virtual shared_ptr getBreedOffspring(shared_ptr target); + bool isFood(shared_ptr itemInstance); + ControlledByPlayerGoal *getControlGoal(); +}; diff --git a/Minecraft.World/PigZombie.cpp b/Minecraft.World/PigZombie.cpp new file mode 100644 index 00000000..cc060484 --- /dev/null +++ b/Minecraft.World/PigZombie.cpp @@ -0,0 +1,202 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.damagesource.h" +#include "PigZombie.h" +#include "..\Minecraft.Client\Textures.h" +#include "SoundTypes.h" + + + +shared_ptr PigZombie::sword; + +void PigZombie::staticCtor() +{ + PigZombie::sword = shared_ptr( new ItemInstance(Item::sword_gold, 1) ); +} + +void PigZombie::_init() +{ + angerTime = 0; + playAngrySoundIn = 0; +} + +PigZombie::PigZombie(Level *level) : Zombie( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + + // 4J Stu - Zombie has already called this, and we don't override it so the Zombie one is the most derived version anyway + //this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + _init(); + + this->textureIdx = TN_MOB_PIGZOMBIE; // 4J was L"/mob/pigzombie.png"; + runSpeed = 0.5f; + attackDamage = 5; + this->fireImmune = true; +} + +bool PigZombie::useNewAi() +{ + return false; +} + +int PigZombie::getTexture() +{ + return textureIdx; +} + +void PigZombie::tick() +{ + runSpeed = attackTarget != NULL ? 0.95f : 0.5f; + if (playAngrySoundIn > 0) + { + if (--playAngrySoundIn == 0) + { + level->playSound(shared_from_this(), eSoundType_MOB_ZOMBIEPIG_ZPIGANGRY, getSoundVolume() * 2, ((random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f) * 1.8f); + } + } + Zombie::tick(); +} + +bool PigZombie::canSpawn() +{ + return level->difficulty > Difficulty::PEACEFUL && level->isUnobstructed(bb) && level->getCubes(shared_from_this(), bb)->empty() && !level->containsAnyLiquid(bb); +} + +void PigZombie::addAdditonalSaveData(CompoundTag *tag) +{ + Zombie::addAdditonalSaveData(tag); + tag->putShort(L"Anger", (short) angerTime); +} + +void PigZombie::readAdditionalSaveData(CompoundTag *tag) +{ + Zombie::readAdditionalSaveData(tag); + angerTime = tag->getShort(L"Anger"); +} + +shared_ptr PigZombie::findAttackTarget() +{ +#ifndef _FINAL_BUILD +#ifdef _DEBUG_MENUS_ENABLED + if(app.GetMobsDontAttackEnabled()) + { + return shared_ptr(); + } +#endif +#endif + + if (angerTime == 0) return nullptr; + return Zombie::findAttackTarget(); +} + +bool PigZombie::hurt(DamageSource *source, int dmg) +{ + shared_ptr sourceEntity = source->getEntity(); + if (dynamic_pointer_cast(sourceEntity) != NULL) + { + vector > *nearby = level->getEntities( shared_from_this(), bb->grow(32, 32, 32)); + AUTO_VAR(itEnd, nearby->end()); + for (AUTO_VAR(it, nearby->begin()); it != itEnd; it++) + { + shared_ptr e = *it; //nearby->at(i); + if (dynamic_pointer_cast(e) != NULL) + { + shared_ptr pigZombie = dynamic_pointer_cast(e); + pigZombie->alert(sourceEntity); + } + } + alert(sourceEntity); + } + return Zombie::hurt(source, dmg); +} + +void PigZombie::alert(shared_ptr target) +{ + this->attackTarget = target; + angerTime = 20 * 20 + random->nextInt(20 * 20); + playAngrySoundIn = random->nextInt(20 * 2); +} + +int PigZombie::getAmbientSound() +{ + return eSoundType_MOB_ZOMBIEPIG_AMBIENT; +} + +int PigZombie::getHurtSound() +{ + return eSoundType_MOB_ZOMBIEPIG_HURT; +} + +int PigZombie::getDeathSound() +{ + return eSoundType_MOB_ZOMBIEPIG_DEATH; +} + +void PigZombie::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + int count = random->nextInt(2 + playerBonusLevel); + for (int i = 0; i < count; i++) + { + spawnAtLocation(Item::rotten_flesh_Id, 1); + } + count = random->nextInt(2 + playerBonusLevel); + for (int i = 0; i < count; i++) + { + spawnAtLocation(Item::goldNugget_Id, 1); + } +} + +void PigZombie::dropRareDeathLoot(int rareLootLevel) +{ + if (rareLootLevel > 0) + { + shared_ptr sword = shared_ptr( new ItemInstance(Item::sword_gold) ); + EnchantmentHelper::enchantItem(random, sword, 5); + spawnAtLocation(sword, 0); + } + else + { + int select = random->nextInt(3); + if (select == 0) + { + spawnAtLocation(Item::goldIngot_Id, 1); + } + else if (select == 1) + { + spawnAtLocation(Item::sword_gold_Id, 1); + } + else if (select == 2) + { + spawnAtLocation(Item::helmet_gold_Id, 1); + } + } +} + +int PigZombie::getDeathLoot() +{ + return Item::rotten_flesh_Id; +} + +void PigZombie::finalizeMobSpawn() +{ + Zombie::finalizeMobSpawn(); + setVillager(false); +} + +shared_ptr PigZombie::getCarriedItem() +{ + // TODO 4J - could be of const shared_ptr type. + return (shared_ptr ) sword; +} diff --git a/Minecraft.World/PigZombie.h b/Minecraft.World/PigZombie.h new file mode 100644 index 00000000..b47efacb --- /dev/null +++ b/Minecraft.World/PigZombie.h @@ -0,0 +1,60 @@ +#pragma once +using namespace std; + +#include "Zombie.h" + +class DamageSource; + +// SKIN BY XaPhobia Chris Beidler +class PigZombie : public Zombie +{ +public: + eINSTANCEOF GetType() { return eTYPE_PIGZOMBIE; } + static Entity *create(Level *level) { return new PigZombie(level); } + +private: + int angerTime; + int playAngrySoundIn; + + void _init(); + +public: + PigZombie(Level *level); + +protected: + bool useNewAi(); + +public: + virtual int getTexture(); + virtual void tick(); + virtual bool canSpawn(); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +protected: + virtual shared_ptr findAttackTarget(); + +public: + virtual bool hurt(DamageSource *source, int dmg); + +private: + void alert(shared_ptr target); + +protected: + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + virtual void dropRareDeathLoot(int rareLootLevel); + virtual int getDeathLoot(); + +private: + static shared_ptr sword; + +public: + virtual void finalizeMobSpawn(); + + shared_ptr getCarriedItem(); + + static void staticCtor(); +}; diff --git a/Minecraft.World/PineFeature.cpp b/Minecraft.World/PineFeature.cpp new file mode 100644 index 00000000..685f1e48 --- /dev/null +++ b/Minecraft.World/PineFeature.cpp @@ -0,0 +1,102 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "PineFeature.h" + +bool PineFeature::place(Level *level, Random *random, int x, int y, int z) +{ + // pines can be quite tall + int treeHeight = random->nextInt(5) + 7; + int trunkHeight = treeHeight - random->nextInt(2) - 3; + int topHeight = treeHeight - trunkHeight; + int topRadius = 1 + random->nextInt(topHeight + 1); + + bool free = true; + // may not be outside of y boundaries + if (y < 1 || y + treeHeight + 1 > Level::genDepth) + { + return false; + } + + // 4J Stu Added to stop tree features generating areas previously place by game rule generation + if(app.getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + bool intersects = levelGenOptions->checkIntersects(x - topRadius, y - 1, z - topRadius, x + topRadius, y + treeHeight, z + topRadius); + if(intersects) + { + //app.DebugPrintf("Skipping reeds feature generation as it overlaps a game rule structure\n"); + return false; + } + } + + // make sure there is enough space + for (int yy = y; yy <= y + 1 + treeHeight && free; yy++) + { + + int r = 1; + if ((yy - y) < trunkHeight) + { + r = 0; + } + else + { + r = topRadius; + } + for (int xx = x - r; xx <= x + r && free; xx++) + { + for (int zz = z - r; zz <= z + r && free; zz++) + { + if (yy >= 0 && yy < Level::genDepth) + { + int tt = level->getTile(xx, yy, zz); + if (tt != 0 && tt != Tile::leaves_Id) free = false; + } + else + { + free = false; + } + } + } + } + + if (!free) return false; + + // must stand on ground + int belowTile = level->getTile(x, y - 1, z); + if ((belowTile != Tile::grass_Id && belowTile != Tile::dirt_Id) || y >= Level::genDepth - treeHeight - 1) return false; + + placeBlock(level, x, y - 1, z, Tile::dirt_Id); + + // place leaf top + int currentRadius = 0; + for (int yy = y + treeHeight; yy >= y + trunkHeight; yy--) + { + for (int xx = x - currentRadius; xx <= x + currentRadius; xx++) + { + int xo = xx - (x); + for (int zz = z - currentRadius; zz <= z + currentRadius; zz++) + { + int zo = zz - (z); + if (abs(xo) == currentRadius && abs(zo) == currentRadius && currentRadius > 0) continue; + if (!Tile::solid[level->getTile(xx, yy, zz)]) placeBlock(level, xx, yy, zz, Tile::leaves_Id, LeafTile::EVERGREEN_LEAF); + } + } + + if (currentRadius >= 1 && yy == (y + trunkHeight + 1)) + { + currentRadius -= 1; + } + else if (currentRadius < topRadius) + { + currentRadius += 1; + } + } + for (int hh = 0; hh < treeHeight - 1; hh++) + { + int t = level->getTile(x, y + hh, z); + if (t == 0 || t == Tile::leaves_Id) placeBlock(level, x, y + hh, z, Tile::treeTrunk_Id, TreeTile::DARK_TRUNK); + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/PineFeature.h b/Minecraft.World/PineFeature.h new file mode 100644 index 00000000..62ded1e4 --- /dev/null +++ b/Minecraft.World/PineFeature.h @@ -0,0 +1,8 @@ +#pragma once +#include "Feature.h" + +class PineFeature : public Feature +{ +public: + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; diff --git a/Minecraft.World/PistonBaseTile.cpp b/Minecraft.World/PistonBaseTile.cpp new file mode 100644 index 00000000..cbad5cec --- /dev/null +++ b/Minecraft.World/PistonBaseTile.cpp @@ -0,0 +1,628 @@ +#include "stdafx.h" +#include "PistonBaseTile.h" +#include "PistonMovingPiece.h" +#include "PistonPieceEntity.h" +#include "PistonExtensionTile.h" +#include "Facing.h" +#include "net.minecraft.world.level.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\MultiPlayerLevel.h" +#include "net.minecraft.world.h" +#include "LevelChunk.h" +#include "Dimension.h" + +const wstring PistonBaseTile::EDGE_TEX = L"piston_side"; +const wstring PistonBaseTile::PLATFORM_TEX = L"piston_top"; +const wstring PistonBaseTile::PLATFORM_STICKY_TEX = L"piston_top_sticky"; +const wstring PistonBaseTile::BACK_TEX = L"piston_bottom"; +const wstring PistonBaseTile::INSIDE_TEX = L"piston_inner_top"; + +const float PistonBaseTile::PLATFORM_THICKNESS = 4.0f; + +DWORD PistonBaseTile::tlsIdx = TlsAlloc(); + +// 4J - NOTE - this ignoreUpdate stuff has been removed from the java version, but I'm not currently sure how the java version does without it... there must be +// some other mechanism that we don't have that stops the event from one piston being processed, from causing neighbours to have extra events created for them. +// For us, that means that if we create a piston next to another one, then one of them gets two events to createPush, the second of which fails, leaving the +// piston in a bad (simultaneously extended & not extended) state. +// 4J - ignoreUpdate is a static in java, implementing as TLS here to make thread safe +bool PistonBaseTile::ignoreUpdate() +{ + return (TlsGetValue(tlsIdx) != NULL); +} + +void PistonBaseTile::ignoreUpdate(bool set) +{ + TlsSetValue(tlsIdx,(LPVOID)(set?1:0)); +} + +PistonBaseTile::PistonBaseTile(int id, bool isSticky) : Tile(id, Material::piston, isSolidRender() ) +{ + // 4J - added initialiser + ignoreUpdate(false); + + this->isSticky = isSticky; + setSoundType(SOUND_STONE); + setDestroyTime(0.5f); + + iconInside = NULL; + iconBack = NULL; + iconPlatform = NULL; +} + +Icon *PistonBaseTile::getPlatformTexture() +{ + return iconPlatform; +} + +void PistonBaseTile::updateShape(float x0, float y0, float z0, float x1, float y1, float z1) +{ + setShape(x0, y0, z0, x1, y1, z1); +} + +Icon *PistonBaseTile::getTexture(int face, int data) +{ + int facing = getFacing(data); + + if (facing > 5) + { + return iconPlatform; + } + + if (face == facing) + { + // sorry about this mess... + // when the piston is extended, either normally + // or because a piston arm animation, the top + // texture is the furnace bottom + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + if (isExtended(data) || tls->xx0 > 0 || tls->yy0 > 0 || tls->zz0 > 0 || tls->xx1 < 1 || tls->yy1 < 1 || tls->zz1 < 1) + { + return iconInside; + } + return iconPlatform; + } + if (face == Facing::OPPOSITE_FACING[facing]) + { + return iconBack; + } + + return icon; +} + +Icon *PistonBaseTile::getTexture(const wstring &name) +{ + if (name.compare(EDGE_TEX) == 0) return Tile::pistonBase->icon; + if (name.compare(PLATFORM_TEX) == 0) return Tile::pistonBase->iconPlatform; + if (name.compare(PLATFORM_STICKY_TEX) == 0) return Tile::pistonStickyBase->iconPlatform; + if (name.compare(INSIDE_TEX) == 0) return Tile::pistonBase->iconInside; + + return NULL; +} + +//@Override +void PistonBaseTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(EDGE_TEX); + iconPlatform = iconRegister->registerIcon(isSticky ? PLATFORM_STICKY_TEX : PLATFORM_TEX); + iconInside = iconRegister->registerIcon(INSIDE_TEX); + iconBack = iconRegister->registerIcon(BACK_TEX); +} + +int PistonBaseTile::getRenderShape() +{ + return SHAPE_PISTON_BASE; +} + +bool PistonBaseTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool PistonBaseTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + return false; +} + +void PistonBaseTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int targetData = getNewFacing(level, x, y, z, dynamic_pointer_cast(by) ); + level->setData(x, y, z, targetData); + if (!level->isClientSide && !ignoreUpdate()) + { + checkIfExtend(level, x, y, z); + } +} + +void PistonBaseTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (!level->isClientSide && !ignoreUpdate()) + { + checkIfExtend(level, x, y, z); + } +} + +void PistonBaseTile::onPlace(Level *level, int x, int y, int z) +{ + if (!level->isClientSide && level->getTileEntity(x, y, z) == NULL && !ignoreUpdate()) + { + checkIfExtend(level, x, y, z); + } +} + +void PistonBaseTile::checkIfExtend(Level *level, int x, int y, int z) +{ + int data = level->getData(x, y, z); + int facing = getFacing(data); + + if (facing == UNDEFINED_FACING) + { + return; + } + bool extend = getNeighborSignal(level, x, y, z, facing); + + if (extend && !isExtended(data)) + { + if (canPush(level, x, y, z, facing)) + { + //level->setDataNoUpdate(x, y, z, facing | EXTENDED_BIT); + level->tileEvent(x, y, z, id, TRIGGER_EXTEND, facing); + } + } + else if (!extend && isExtended(data)) + { + //level->setDataNoUpdate(x, y, z, facing); + level->tileEvent(x, y, z, id, TRIGGER_CONTRACT, facing); + } +} + +/** + * This method checks neighbor signals for this block and the block above, + * and directly beneath. However, it avoids checking blocks that would be + * pushed by this block. + * + * @param level + * @param x + * @param y + * @param z + * @return + */ +bool PistonBaseTile::getNeighborSignal(Level *level, int x, int y, int z, int facing) +{ + // check adjacent neighbors, but not in push direction + if (facing != Facing::DOWN && level->getSignal(x, y - 1, z, Facing::DOWN)) return true; + if (facing != Facing::UP && level->getSignal(x, y + 1, z, Facing::UP)) return true; + if (facing != Facing::NORTH && level->getSignal(x, y, z - 1, Facing::NORTH)) return true; + if (facing != Facing::SOUTH && level->getSignal(x, y, z + 1, Facing::SOUTH)) return true; + if (facing != Facing::EAST && level->getSignal(x + 1, y, z, Facing::EAST)) return true; + if (facing != Facing::WEST && level->getSignal(x - 1, y, z, Facing::WEST)) return true; + + // check signals above + if (level->getSignal(x, y, z, 0)) return true; + if (level->getSignal(x, y + 2, z, 1)) return true; + if (level->getSignal(x, y + 1, z - 1, 2)) return true; + if (level->getSignal(x, y + 1, z + 1, 3)) return true; + if (level->getSignal(x - 1, y + 1, z, 4)) return true; + if (level->getSignal(x + 1, y + 1, z, 5)) return true; + + return false; +} + +void PistonBaseTile::triggerEvent(Level *level, int x, int y, int z, int param1, int facing) +{ + ignoreUpdate(true); + + if (param1 == TRIGGER_EXTEND) + { + level->setDataNoUpdate(x, y, z, facing | EXTENDED_BIT); + } + else + { + level->setDataNoUpdate(x, y, z, facing); + } + + if (param1 == TRIGGER_EXTEND) + { + PIXBeginNamedEvent(0,"Create push\n"); + if (createPush(level, x, y, z, facing)) + { + // 4J - it is (currently) critical that this setData sends data to the client, so have added a bool to the method so that it sends data even if the data was already set to the same value + // as before, which was actually its behaviour until a change in 1.0.1 meant that setData only conditionally sent updates to listeners. If the data update Isn't sent, then what + // can happen is: + // (1) the host sends the tile event to the client + // (2) the client gets the tile event, and sets the tile/data value locally. + // (3) just before setting the tile/data locally, the client will put the old value in the vector of things to be restored should an update not be received back from the host + // (4) we don't get any update of the tile from the host, and so the old value gets restored on the client + // (5) the piston base ends up being restored to its retracted state whilst the piston arm is extended + // We really need to spend some time investigating a better way for pistons to work as it all seems a bit scary how the host/client interact, but forcing this to send should at least + // restore the behaviour of the pistons to something closer to what they were before the 1.0.1 update. By sending this data update, then (4) in the list above doesn't happen + // because the client does actually receive an update for this tile from the host after the event has been processed on the cient. + level->setData(x, y, z, facing | EXTENDED_BIT, true); + level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_TILE_PISTON_OUT, 0.5f, level->random->nextFloat() * 0.25f + 0.6f); + } + else + { + level->setDataNoUpdate(x, y, z, facing); + } + PIXEndNamedEvent(); + } + else if (param1 == TRIGGER_CONTRACT) + { + PIXBeginNamedEvent(0,"Contract phase A\n"); + shared_ptr prevTileEntity = level->getTileEntity(x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); + if (prevTileEntity != NULL && dynamic_pointer_cast(prevTileEntity) != NULL) + { + dynamic_pointer_cast(prevTileEntity)->finalTick(); + } + + stopSharingIfServer(level, x, y, z); // 4J added + level->setTileAndDataNoUpdate(x, y, z, Tile::pistonMovingPiece_Id, facing); + level->setTileEntity(x, y, z, PistonMovingPiece::newMovingPieceEntity(id, facing, facing, false, true)); + + PIXEndNamedEvent(); + + // sticky movement + if (isSticky) + { + PIXBeginNamedEvent(0,"Contract sticky phase A\n"); + int twoX = x + Facing::STEP_X[facing] * 2; + int twoY = y + Facing::STEP_Y[facing] * 2; + int twoZ = z + Facing::STEP_Z[facing] * 2; + int block = level->getTile(twoX, twoY, twoZ); + int blockData = level->getData(twoX, twoY, twoZ); + bool pistonPiece = false; + + PIXEndNamedEvent(); + + if (block == Tile::pistonMovingPiece_Id) + { + PIXBeginNamedEvent(0,"Contract sticky phase B\n"); + // the block two steps away is a moving piston block piece, + // so replace it with the real data, since it's probably + // this piston which is changing too fast + shared_ptr tileEntity = level->getTileEntity(twoX, twoY, twoZ); + if (tileEntity != NULL && dynamic_pointer_cast(tileEntity) != NULL ) + { + shared_ptr ppe = dynamic_pointer_cast(tileEntity); + + if (ppe->getFacing() == facing && ppe->isExtending()) + { + // force the tile to air before pushing + ppe->finalTick(); + block = ppe->getId(); + blockData = ppe->getData(); + pistonPiece = true; + } + } + PIXEndNamedEvent(); + } + + PIXBeginNamedEvent(0,"Contract sticky phase C\n"); + if (!pistonPiece && block > 0 && (isPushable(block, level, twoX, twoY, twoZ, false)) + && (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_NORMAL || block == Tile::pistonBase_Id || block == Tile::pistonStickyBase_Id)) + { + stopSharingIfServer(level, twoX, twoY, twoZ); // 4J added + + x += Facing::STEP_X[facing]; + y += Facing::STEP_Y[facing]; + z += Facing::STEP_Z[facing]; + + level->setTileAndDataNoUpdate(x, y, z, Tile::pistonMovingPiece_Id, blockData); + level->setTileEntity(x, y, z, PistonMovingPiece::newMovingPieceEntity(block, blockData, facing, false, false)); + + ignoreUpdate(false); + level->setTile(twoX, twoY, twoZ, 0); + ignoreUpdate(true); + } + else if (!pistonPiece) + { + stopSharingIfServer(level, x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); // 4J added + ignoreUpdate(false); + level->setTile(x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing], 0); + ignoreUpdate(true); + } + PIXEndNamedEvent(); + } + else + { + stopSharingIfServer(level, x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing]); // 4J added + ignoreUpdate(false); + level->setTile(x + Facing::STEP_X[facing], y + Facing::STEP_Y[facing], z + Facing::STEP_Z[facing], 0); + ignoreUpdate(true); + } + + level->playSound(x + 0.5, y + 0.5, z + 0.5, eSoundType_TILE_PISTON_IN, 0.5f, level->random->nextFloat() * 0.15f + 0.6f); + } + + ignoreUpdate(false); +} + +void PistonBaseTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + int data = (forceData == -1 ) ? level->getData(x, y, z) : forceData; + + if (isExtended(data)) + { + const float thickness = PLATFORM_THICKNESS / 16.0f; + switch (getFacing(data)) + { + case Facing::DOWN: + setShape(0, thickness, 0, 1, 1, 1); + break; + case Facing::UP: + setShape(0, 0, 0, 1, 1 - thickness, 1); + break; + case Facing::NORTH: + setShape(0, 0, thickness, 1, 1, 1); + break; + case Facing::SOUTH: + setShape(0, 0, 0, 1, 1, 1 - thickness); + break; + case Facing::WEST: + setShape(thickness, 0, 0, 1, 1, 1); + break; + case Facing::EAST: + setShape(0, 0, 0, 1 - thickness, 1, 1); + break; + } + } + else + { + setShape(0, 0, 0, 1, 1, 1); + } +} + +void PistonBaseTile::updateDefaultShape() +{ + setShape(0, 0, 0, 1, 1, 1); +} + +void PistonBaseTile::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source) +{ + setShape(0, 0, 0, 1, 1, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); +} + +AABB *PistonBaseTile::getAABB(Level *level, int x, int y, int z) +{ + updateShape(level, x, y, z); + return Tile::getAABB(level, x, y, z); +} + +bool PistonBaseTile::isCubeShaped() +{ + return false; +} + +int PistonBaseTile::getFacing(int data) +{ + return data & 0x7; +} + +bool PistonBaseTile::isExtended(int data) +{ + return (data & EXTENDED_BIT) != 0; +} + +int PistonBaseTile::getNewFacing(Level *level, int x, int y, int z, shared_ptr player) +{ + if (Mth::abs((float) player->x - x) < 2 && Mth::abs((float) player->z - z) < 2) + { + // If the player is above the block, the slot is on the top + double py = player->y + 1.82 - player->heightOffset; + if (py - y > 2) + { + return Facing::UP; + } + // If the player is below the block, the slot is on the bottom + if (y - py > 0) + { + return Facing::DOWN; + } + } + // The slot is on the side + int i = Mth::floor(player->yRot * 4.0f / 360.0f + 0.5) & 0x3; + if (i == 0) return Facing::NORTH; + if (i == 1) return Facing::EAST; + if (i == 2) return Facing::SOUTH; + if (i == 3) return Facing::WEST; + return 0; +} + +bool PistonBaseTile::isPushable(int block, Level *level, int cx, int cy, int cz, bool allowDestroyable) +{ + // special case for obsidian + if (block == Tile::obsidian_Id) + { + return false; + } + + if (block == Tile::pistonBase_Id || block == Tile::pistonStickyBase_Id) + { + // special case for piston bases + if (isExtended(level->getData(cx, cy, cz))) + { + return false; + } + } + else + { + if (Tile::tiles[block]->getDestroySpeed(level, cx, cy, cz) == Tile::INDESTRUCTIBLE_DESTROY_TIME) + { + return false; + } + + if (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_BLOCK) + { + return false; + } + + if (!allowDestroyable && Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_DESTROY) + { + return false; + } + } + + if( Tile::tiles[block]->isEntityTile() ) // 4J - java uses instanceof EntityTile here + { + // may not push tile entities + return false; + } + + return true; +} + +bool PistonBaseTile::canPush(Level *level, int sx, int sy, int sz, int facing) +{ + int cx = sx + Facing::STEP_X[facing]; + int cy = sy + Facing::STEP_Y[facing]; + int cz = sz + Facing::STEP_Z[facing]; + + for (int i = 0; i < MAX_PUSH_DEPTH + 1; i++) + { + + if (cy <= 0 || cy >= (Level::maxBuildHeight - 1)) + { + // out of bounds + return false; + } + + // 4J - added to also check for out of bounds in x/z for our finite world + int minXZ = - (level->dimension->getXZSize() * 16 ) / 2; + int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1; + if( ( cx <= minXZ ) || ( cx >= maxXZ ) || ( cz <= minXZ ) || ( cz >= maxXZ ) ) + { + return false; + } + int block = level->getTile(cx, cy, cz); + if (block == 0) + { + break; + } + + if (!isPushable(block, level, cx, cy, cz, true)) + { + return false; + } + + if (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_DESTROY) + { + break; + } + + if (i == MAX_PUSH_DEPTH) + { + // we've reached the maximum push depth + // without finding air or a breakable block + return false; + } + + cx += Facing::STEP_X[facing]; + cy += Facing::STEP_Y[facing]; + cz += Facing::STEP_Z[facing]; + } + + return true; + +} + +void PistonBaseTile::stopSharingIfServer(Level *level, int x, int y, int z) +{ + if( !level->isClientSide ) + { + MultiPlayerLevel *clientLevel = Minecraft::GetInstance()->getLevel(level->dimension->id); + if( clientLevel ) + { + LevelChunk *lc = clientLevel->getChunkAt( x, z ); + lc->stopSharingTilesAndData(); + } + } +} + +bool PistonBaseTile::createPush(Level *level, int sx, int sy, int sz, int facing) +{ + int cx = sx + Facing::STEP_X[facing]; + int cy = sy + Facing::STEP_Y[facing]; + int cz = sz + Facing::STEP_Z[facing]; + + for (int i = 0; i < MAX_PUSH_DEPTH + 1; i++) + { + if (cy <= 0 || cy >= (Level::maxBuildHeight - 1)) + { + // out of bounds + return false; + } + + // 4J - added to also check for out of bounds in x/z for our finite world + int minXZ = - (level->dimension->getXZSize() * 16 ) / 2; + int maxXZ = (level->dimension->getXZSize() * 16 ) / 2 - 1; + if( ( cx <= minXZ ) || ( cx >= maxXZ ) || ( cz <= minXZ ) || ( cz >= maxXZ ) ) + { + return false; + } + + int block = level->getTile(cx, cy, cz); + if (block == 0) + { + break; + } + + if (!isPushable(block, level, cx, cy, cz, true)) + { + return false; + } + + if (Tile::tiles[block]->getPistonPushReaction() == Material::PUSH_DESTROY) + { + // this block is destroyed when pushed + Tile::tiles[block]->spawnResources(level, cx, cy, cz, level->getData(cx, cy, cz), 0); + // setting the tile to air is actually superflous, but + // helps vs multiplayer problems + stopSharingIfServer(level, cx, cy, cz); // 4J added + level->setTile(cx, cy, cz, 0); + break; + } + + if (i == MAX_PUSH_DEPTH) + { + // we've reached the maximum push depth + // without finding air or a breakable block + return false; + } + + cx += Facing::STEP_X[facing]; + cy += Facing::STEP_Y[facing]; + cz += Facing::STEP_Z[facing]; + } + + while (cx != sx || cy != sy || cz != sz) + { + + int nx = cx - Facing::STEP_X[facing]; + int ny = cy - Facing::STEP_Y[facing]; + int nz = cz - Facing::STEP_Z[facing]; + + int block = level->getTile(nx, ny, nz); + int data = level->getData(nx, ny, nz); + + stopSharingIfServer(level, cx, cy, cz); // 4J added + + if (block == id && nx == sx && ny == sy && nz == sz) + { + level->setTileAndDataNoUpdate(cx, cy, cz, Tile::pistonMovingPiece_Id, facing | (isSticky ? PistonExtensionTile::STICKY_BIT : 0), false); + level->setTileEntity(cx, cy, cz, PistonMovingPiece::newMovingPieceEntity(Tile::pistonExtensionPiece_Id, facing | (isSticky ? PistonExtensionTile::STICKY_BIT : 0), facing, true, false)); + } + else + { + level->setTileAndDataNoUpdate(cx, cy, cz, Tile::pistonMovingPiece_Id, data, false); + level->setTileEntity(cx, cy, cz, PistonMovingPiece::newMovingPieceEntity(block, data, facing, true, false)); + } + + cx = nx; + cy = ny; + cz = nz; + } + + return true; + +} diff --git a/Minecraft.World/PistonBaseTile.h b/Minecraft.World/PistonBaseTile.h new file mode 100644 index 00000000..0fda1391 --- /dev/null +++ b/Minecraft.World/PistonBaseTile.h @@ -0,0 +1,71 @@ +#pragma once +#include "Tile.h" + +class PistonBaseTile : public Tile +{ +public: + static const int EXTENDED_BIT = 8; + static const int UNDEFINED_FACING = 7; + + static const float PLATFORM_THICKNESS; + static const int MAX_PUSH_DEPTH = 12; + static const int TRIGGER_EXTEND = 0; + static const int TRIGGER_CONTRACT = 1; + + static const wstring EDGE_TEX; + static const wstring PLATFORM_TEX; + static const wstring PLATFORM_STICKY_TEX; + static const wstring BACK_TEX; + static const wstring INSIDE_TEX; + +private: + bool isSticky; + + Icon *iconInside; + Icon *iconBack; + Icon *iconPlatform; + + static DWORD tlsIdx; + // 4J - was just a static but implemented with TLS for our version + static bool ignoreUpdate(); + static void ignoreUpdate(bool set); + +public: + PistonBaseTile(int id, bool isSticky); + + Icon *getPlatformTexture(); + virtual void updateShape(float x0, float y0, float z0, float x1, float y1, float z1); + + virtual Icon *getTexture(int face, int data); + static Icon *getTexture(const wstring &name); + void registerIcons(IconRegister *iconRegister); + + virtual int getRenderShape(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + virtual void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual void onPlace(Level *level, int x, int y, int z); + +private: + void checkIfExtend(Level *level, int x, int y, int z); + bool getNeighborSignal(Level *level, int x, int y, int z, int facing); + +public: + virtual void triggerEvent(Level *level, int x, int y, int z, int param1, int facing); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual void updateDefaultShape(); + virtual void addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual bool isCubeShaped(); + + static int getFacing(int data); + static bool isExtended(int data); + static int getNewFacing(Level *level, int x, int y, int z, shared_ptr player); +private: + static bool isPushable(int block, Level *level, int cx, int cy, int cz, bool allowDestroyable); + static bool canPush(Level *level, int sx, int sy, int sz, int facing); + static void stopSharingIfServer(Level *level, int x, int y, int z); // 4J added + + bool createPush(Level *level, int sx, int sy, int sz, int facing); +}; \ No newline at end of file diff --git a/Minecraft.World/PistonExtensionTile.cpp b/Minecraft.World/PistonExtensionTile.cpp new file mode 100644 index 00000000..1827ddde --- /dev/null +++ b/Minecraft.World/PistonExtensionTile.cpp @@ -0,0 +1,210 @@ +#include "stdafx.h" +#include "PistonExtensionTile.h" +#include "PistonBaseTile.h" +#include "Facing.h" +#include "net.minecraft.world.level.h" + +PistonExtensionTile::PistonExtensionTile(int id) : Tile(id, Material::piston,isSolidRender() ) +{ + // 4J added initialiser + overrideTopTexture = NULL; + + setSoundType(SOUND_STONE); + setDestroyTime(0.5f); +} + +void PistonExtensionTile::setOverrideTopTexture(Icon *overrideTopTexture) +{ + this->overrideTopTexture = overrideTopTexture; +} + +void PistonExtensionTile::clearOverrideTopTexture() +{ + this->overrideTopTexture = NULL; +} + +void PistonExtensionTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + Tile::onRemove(level, x, y, z, id, data); + int facing = Facing::OPPOSITE_FACING[getFacing(data)]; + x += Facing::STEP_X[facing]; + y += Facing::STEP_Y[facing]; + z += Facing::STEP_Z[facing]; + + int t = level->getTile(x, y, z); + + if (t == Tile::pistonBase_Id || t == Tile::pistonStickyBase_Id) + { + data = level->getData(x, y, z); + if (PistonBaseTile::isExtended(data)) + { + Tile::tiles[t]->spawnResources(level, x, y, z, data, 0); + level->setTile(x, y, z, 0); + + } + } +} + +Icon *PistonExtensionTile::getTexture(int face, int data) +{ + int facing = getFacing(data); + + if (face == facing) + { + if (overrideTopTexture != NULL) + { + return overrideTopTexture; + } + if ((data & STICKY_BIT) != 0) + { + return PistonBaseTile::getTexture(PistonBaseTile::PLATFORM_STICKY_TEX); + } + return PistonBaseTile::getTexture(PistonBaseTile::PLATFORM_TEX); + } + if (facing < 6 && face == Facing::OPPOSITE_FACING[facing]) + { + return PistonBaseTile::getTexture(PistonBaseTile::PLATFORM_TEX); + } + return PistonBaseTile::getTexture(PistonBaseTile::EDGE_TEX); // edge and arms +} + +void PistonExtensionTile::registerIcons(IconRegister *iconRegister) +{ + // None +} + +int PistonExtensionTile::getRenderShape() +{ + return SHAPE_PISTON_EXTENSION; +} + +bool PistonExtensionTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool PistonExtensionTile::isCubeShaped() +{ + return false; +} + +bool PistonExtensionTile::mayPlace(Level *level, int x, int y, int z) +{ + return false; +} + +bool PistonExtensionTile::mayPlace(Level *level, int x, int y, int z, int face) +{ + return false; +} + +int PistonExtensionTile::getResourceCount(Random *random) +{ + return 0; +} + +void PistonExtensionTile::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source) +{ + int data = level->getData(x, y, z); + + const float thickness = PistonBaseTile::PLATFORM_THICKNESS / 16.0f; + const float smallEdge1 = (8.0f - (PistonBaseTile::PLATFORM_THICKNESS / 2.0f)) / 16.0f; + const float smallEdge2 = (8.0f + (PistonBaseTile::PLATFORM_THICKNESS / 2.0f)) / 16.0f; + const float largeEdge1 = (8.0f - PistonBaseTile::PLATFORM_THICKNESS) / 16.0f; + const float largeEdge2 = (8.0f + PistonBaseTile::PLATFORM_THICKNESS) / 16.0f; + + switch (getFacing(data)) + { + case Facing::DOWN: + setShape(0, 0, 0, 1, thickness, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + setShape(smallEdge1, thickness, smallEdge1, smallEdge2, 1, smallEdge2); + Tile::addAABBs(level, x, y, z, box, boxes, source); + break; + case Facing::UP: + setShape(0, 1 - thickness, 0, 1, 1, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + setShape(smallEdge1, 0, smallEdge1, smallEdge2, 1 - thickness, smallEdge2); + Tile::addAABBs(level, x, y, z, box, boxes, source); + break; + case Facing::NORTH: + setShape(0, 0, 0, 1, 1, thickness); + Tile::addAABBs(level, x, y, z, box, boxes, source); + setShape(largeEdge1, smallEdge1, thickness, largeEdge2, smallEdge2, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + break; + case Facing::SOUTH: + setShape(0, 0, 1 - thickness, 1, 1, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + setShape(largeEdge1, smallEdge1, 0, largeEdge2, smallEdge2, 1 - thickness); + Tile::addAABBs(level, x, y, z, box, boxes, source); + break; + case Facing::WEST: + setShape(0, 0, 0, thickness, 1, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + setShape(smallEdge1, largeEdge1, thickness, smallEdge2, largeEdge2, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + break; + case Facing::EAST: + setShape(1 - thickness, 0, 0, 1, 1, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + setShape(0, smallEdge1, largeEdge1, 1 - thickness, smallEdge2, largeEdge2); + Tile::addAABBs(level, x, y, z, box, boxes, source); + break; + } + setShape(0, 0, 0, 1, 1, 1); + +} + +void PistonExtensionTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + int data = (forceData == -1 ) ? level->getData(x, y, z) : forceData; + + const float thickness = PistonBaseTile::PLATFORM_THICKNESS / 16.0f; + + switch (getFacing(data)) + { + case Facing::DOWN: + setShape(0, 0, 0, 1, thickness, 1); + break; + case Facing::UP: + setShape(0, 1 - thickness, 0, 1, 1, 1); + break; + case Facing::NORTH: + setShape(0, 0, 0, 1, 1, thickness); + break; + case Facing::SOUTH: + setShape(0, 0, 1 - thickness, 1, 1, 1); + break; + case Facing::WEST: + setShape(0, 0, 0, thickness, 1, 1); + break; + case Facing::EAST: + setShape(1 - thickness, 0, 0, 1, 1, 1); + break; + } +} + +void PistonExtensionTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + int facing = getFacing(level->getData(x, y, z)); + int tile = level->getTile(x - Facing::STEP_X[facing], y - Facing::STEP_Y[facing], z - Facing::STEP_Z[facing]); + if (tile != Tile::pistonBase_Id && tile != Tile::pistonStickyBase_Id) + { + level->setTile(x, y, z, 0); + } + else + { + Tile::tiles[tile]->neighborChanged(level, x - Facing::STEP_X[facing], y - Facing::STEP_Y[facing], z - Facing::STEP_Z[facing], type); + } +} + +int PistonExtensionTile::getFacing(int data) +{ + return data & 0x7; +} + +int PistonExtensionTile::cloneTileId(Level *level, int x, int y, int z) +{ + return 0; +} \ No newline at end of file diff --git a/Minecraft.World/PistonExtensionTile.h b/Minecraft.World/PistonExtensionTile.h new file mode 100644 index 00000000..a0b08729 --- /dev/null +++ b/Minecraft.World/PistonExtensionTile.h @@ -0,0 +1,31 @@ +#pragma once +#include "Tile.h" + +class PistonExtensionTile : public Tile +{ +public: + // i'm reusing this block for the sticky pistons + static const int STICKY_BIT = 8; + +private: + Icon *overrideTopTexture; + +public: + PistonExtensionTile(int id); + void setOverrideTopTexture(Icon *overrideTopTexture); + void clearOverrideTopTexture(); + void onRemove(Level *level, int x, int y, int z, int id, int data); + virtual Icon *getTexture(int face, int data); + void registerIcons(IconRegister *iconRegister); + virtual int getRenderShape(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual bool mayPlace(Level *level, int x, int y, int z, int face); + virtual int getResourceCount(Random *random); + virtual void addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + static int getFacing(int data); + virtual int cloneTileId(Level *level, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/PistonMovingPiece.cpp b/Minecraft.World/PistonMovingPiece.cpp new file mode 100644 index 00000000..e141cebe --- /dev/null +++ b/Minecraft.World/PistonMovingPiece.cpp @@ -0,0 +1,211 @@ +#include "stdafx.h" +#include "PistonMovingPiece.h" +#include "PistonPieceEntity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" +#include "Facing.h" +#include "AABB.h" + +PistonMovingPiece::PistonMovingPiece(int id) : EntityTile(id, Material::piston, isSolidRender() ) +{ + setDestroyTime(INDESTRUCTIBLE_DESTROY_TIME); +} + +shared_ptr PistonMovingPiece::newTileEntity(Level *level) +{ + return nullptr; +} + +void PistonMovingPiece::onPlace(Level *level, int x, int y, int z) +{ +} + +void PistonMovingPiece::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + shared_ptr tileEntity = level->getTileEntity(x, y, z); + if (tileEntity != NULL && dynamic_pointer_cast(tileEntity) != NULL) + { + dynamic_pointer_cast(tileEntity)->finalTick(); + } + else + { + EntityTile::onRemove(level, x, y, z, id, data); + } +} + +bool PistonMovingPiece::mayPlace(Level *level, int x, int y, int z) +{ + return false; +} + +bool PistonMovingPiece::mayPlace(Level *level, int x, int y, int z, int face) +{ + return false; +} + +int PistonMovingPiece::getRenderShape() +{ + return SHAPE_INVISIBLE; +} + +bool PistonMovingPiece::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool PistonMovingPiece::isCubeShaped() +{ + return false; +} + +bool PistonMovingPiece::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if( soundOnly) return false; + // this is a special case in order to help removing invisible, unbreakable, blocks in the world + if (!level->isClientSide && level->getTileEntity(x, y, z) == NULL) + { + // this block is no longer valid + level->setTile(x, y, z, 0); + return true; + } + return false; +} + +int PistonMovingPiece::getResource(int data, Random *random, int playerBonusLevel) +{ + return 0; +} + +void PistonMovingPiece::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus) +{ + if (level->isClientSide) return; + + shared_ptr entity = getEntity(level, x, y, z); + if (entity == NULL) + { + return; + } + + Tile::tiles[entity->getId()]->spawnResources(level, x, y, z, entity->getData(), 0); +} + +void PistonMovingPiece::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (!level->isClientSide && level->getTileEntity(x, y, z) == NULL) + { + } +} + +shared_ptr PistonMovingPiece::newMovingPieceEntity(int block, int data, int facing, bool extending, bool isSourcePiston) +{ + return shared_ptr(new PistonPieceEntity(block, data, facing, extending, isSourcePiston)); +} + +AABB *PistonMovingPiece::getAABB(Level *level, int x, int y, int z) +{ + shared_ptr entity = getEntity(level, x, y, z); + if (entity == NULL) + { + return NULL; + } + + // move the aabb depending on the animation + float progress = entity->getProgress(0); + if (entity->isExtending()) + { + progress = 1.0f - progress; + } + return getAABB(level, x, y, z, entity->getId(), progress, entity->getFacing()); +} + +void PistonMovingPiece::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + shared_ptr entity = dynamic_pointer_cast(forceEntity); + if( entity == NULL ) entity = getEntity(level, x, y, z); + if (entity != NULL) + { + Tile *tile = Tile::tiles[entity->getId()]; + if (tile == NULL || tile == this) + { + return; + } + tile->updateShape(level, x, y, z); + + float progress = entity->getProgress(0); + if (entity->isExtending()) + { + progress = 1.0f - progress; + } + int facing = entity->getFacing(); + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + tls->xx0 = tile->getShapeX0() - Facing::STEP_X[facing] * progress; + tls->yy0 = tile->getShapeY0() - Facing::STEP_Y[facing] * progress; + tls->zz0 = tile->getShapeZ0() - Facing::STEP_Z[facing] * progress; + tls->xx1 = tile->getShapeX1() - Facing::STEP_X[facing] * progress; + tls->yy1 = tile->getShapeY1() - Facing::STEP_Y[facing] * progress; + tls->zz1 = tile->getShapeZ1() - Facing::STEP_Z[facing] * progress; + } +} + +AABB *PistonMovingPiece::getAABB(Level *level, int x, int y, int z, int tile, float progress, int facing) +{ + if (tile == 0 || tile == id) + { + return NULL; + } + AABB *aabb = Tile::tiles[tile]->getAABB(level, x, y, z); + + if (aabb == NULL) + { + return NULL; + } + + // move the aabb depending on the animation + if (Facing::STEP_X[facing] < 0) + { + aabb->x0 -= Facing::STEP_X[facing] * progress; + } + else + { + aabb->x1 -= Facing::STEP_X[facing] * progress; + } + if (Facing::STEP_Y[facing] < 0) + { + aabb->y0 -= Facing::STEP_Y[facing] * progress; + } + else + { + aabb->y1 -= Facing::STEP_Y[facing] * progress; + } + if (Facing::STEP_Z[facing] < 0) + { + aabb->z0 -= Facing::STEP_Z[facing] * progress; + } + else + { + aabb->z1 -= Facing::STEP_Z[facing] * progress; + } + return aabb; +} + +shared_ptr PistonMovingPiece::getEntity(LevelSource *level, int x, int y, int z) +{ + shared_ptr tileEntity = level->getTileEntity(x, y, z); + if (tileEntity != NULL && dynamic_pointer_cast(tileEntity) != NULL) + { + return dynamic_pointer_cast(tileEntity); + } + return nullptr; +} + +void PistonMovingPiece::registerIcons(IconRegister *iconRegister) +{ + // don't register null, register piston top instead (to get proper + // particle effect) + icon = iconRegister->registerIcon(L"piston_top"); +} + +int PistonMovingPiece::cloneTileId(Level *level, int x, int y, int z) +{ + return 0; +} diff --git a/Minecraft.World/PistonMovingPiece.h b/Minecraft.World/PistonMovingPiece.h new file mode 100644 index 00000000..1b1a61dc --- /dev/null +++ b/Minecraft.World/PistonMovingPiece.h @@ -0,0 +1,37 @@ +#include "EntityTile.h" + +class PistonPieceEntity; + +class PistonMovingPiece : public EntityTile +{ +public: + PistonMovingPiece(int id); + +protected: + virtual shared_ptr newTileEntity(Level *level); + +public: + virtual void onPlace(Level *level, int x, int y, int z); + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual bool mayPlace(Level *level, int x, int y, int z, int face); + virtual int getRenderShape(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param; + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + static shared_ptr newMovingPieceEntity(int block, int data, int facing, bool extending, bool isSourcePiston); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + + AABB *getAABB(Level *level, int x, int y, int z, int tile, float progress, int facing); + +private: + shared_ptr getEntity(LevelSource *level, int x, int y, int z); + +public: + virtual int cloneTileId(Level *level, int x, int y, int z); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/PistonPieceEntity.cpp b/Minecraft.World/PistonPieceEntity.cpp new file mode 100644 index 00000000..3498cec0 --- /dev/null +++ b/Minecraft.World/PistonPieceEntity.cpp @@ -0,0 +1,215 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "PistonPieceEntity.h" +#include "PistonMovingPiece.h" +#include "net.minecraft.world.level.h" +#include "Facing.h" +#include "Tile.h" + + + +PistonPieceEntity::PistonPieceEntity() +{ + // for the tile entity loader + + // 4J - added initialisers + this->id = 0; + this->data = 0; + this->facing = 0; + this->extending = 0; + this->_isSourcePiston = 0; + progress = 0.0f; + progressO = 0.0f; +} + +PistonPieceEntity::PistonPieceEntity(int id, int data, int facing, bool extending, bool isSourcePiston) : TileEntity() +{ + // 4J - added initialisers + progress = 0.0f; + progressO = 0.0f; + + this->id = id; + this->data = data; + this->facing = facing; + this->extending = extending; + this->_isSourcePiston = isSourcePiston; +} + +int PistonPieceEntity::getId() +{ + return id; +} + +int PistonPieceEntity::getData() +{ + return data; +} + +bool PistonPieceEntity::isExtending() +{ + return extending; +} + +int PistonPieceEntity::getFacing() +{ + return facing; +} + +bool PistonPieceEntity::isSourcePiston() +{ + return _isSourcePiston; +} + +float PistonPieceEntity::getProgress(float a) +{ + if (a > 1) + { + a = 1; + } + return progressO + (progress - progressO) * a; +} + +float PistonPieceEntity::getXOff(float a) +{ + if (extending) + { + return (getProgress(a) - 1.0f) * Facing::STEP_X[facing]; + } + else + { + return (1.0f - getProgress(a)) * Facing::STEP_X[facing]; + } +} + +float PistonPieceEntity::getYOff(float a) +{ + if (extending) + { + return (getProgress(a) - 1.0f) * Facing::STEP_Y[facing]; + } + else + { + return (1.0f - getProgress(a)) * Facing::STEP_Y[facing]; + } +} + +float PistonPieceEntity::getZOff(float a) +{ + if (extending) + { + return (getProgress(a) - 1.0f) * Facing::STEP_Z[facing]; + } + else + { + return (1.0f - getProgress(a)) * Facing::STEP_Z[facing]; + } +} + +void PistonPieceEntity::moveCollidedEntities(float progress, float amount) +{ + if (extending) + { + progress = 1.0f - progress; + } + else + { + progress = progress - 1.0f; + } + + AABB *aabb = Tile::pistonMovingPiece->getAABB(level, x, y, z, id, progress, facing); + if (aabb != NULL) + { + vector > *entities = level->getEntities(nullptr, aabb); + if (!entities->empty()) + { + vector< shared_ptr > collisionHolder; + for( AUTO_VAR(it, entities->begin()); it != entities->end(); it++ ) + { + collisionHolder.push_back(*it); + } + + for( AUTO_VAR(it, collisionHolder.begin()); it != collisionHolder.end(); it++ ) + { + (*it)->move(amount * Facing::STEP_X[facing], + amount * Facing::STEP_Y[facing], + amount * Facing::STEP_Z[facing]); + } + } + } +} + +void PistonPieceEntity::finalTick() +{ + if (progressO < 1 && level != NULL) + { + progressO = progress = 1; + level->removeTileEntity(x, y, z); + setRemoved(); + if (level->getTile(x, y, z) == Tile::pistonMovingPiece_Id) + level->setTileAndData(x, y, z, id, data); + } +} + +void PistonPieceEntity::tick() +{ + progressO = progress; + + if (progressO >= 1) + { + moveCollidedEntities(1, 4 / 16.f); + level->removeTileEntity(x, y, z); + setRemoved(); + if (level->getTile(x, y, z) == Tile::pistonMovingPiece_Id) + level->setTileAndData(x, y, z, id, data); + return; + } + + progress += .5f; + if (progress >= 1) + { + progress = 1; + } + + if (extending) + { + moveCollidedEntities(progress, (progress - progressO) + 1.0f / 16.0f); + } +} + +void PistonPieceEntity::load(CompoundTag *tag) +{ + TileEntity::load(tag); + + id = tag->getInt(L"blockId"); + data = tag->getInt(L"blockData"); + facing = tag->getInt(L"facing"); + progressO = progress = tag->getFloat(L"progress"); + extending = tag->getBoolean(L"extending"); +} + +void PistonPieceEntity::save(CompoundTag *tag) +{ + TileEntity::save(tag); + + tag->putInt(L"blockId", id); + tag->putInt(L"blockData", data); + tag->putInt(L"facing", facing); + tag->putFloat(L"progress", progressO); + tag->putBoolean(L"extending", extending); +} + +// 4J Added +shared_ptr PistonPieceEntity::clone() +{ + shared_ptr result = shared_ptr( new PistonPieceEntity() ); + TileEntity::clone(result); + + result->id = id; + result->data = data; + result->facing = facing; + result->extending = extending; + result->_isSourcePiston = _isSourcePiston; + result->progress = progress; + result->progressO = progressO; + return result; +} diff --git a/Minecraft.World/PistonPieceEntity.h b/Minecraft.World/PistonPieceEntity.h new file mode 100644 index 00000000..8d8543ef --- /dev/null +++ b/Minecraft.World/PistonPieceEntity.h @@ -0,0 +1,40 @@ +#include "TileEntity.h" + +class PistonPieceEntity : public TileEntity +{ +public: + eINSTANCEOF GetType() { return eTYPE_PISTONPIECEENTITY; } + static TileEntity *create() { return new PistonPieceEntity(); } +private: + int id; + int data; + int facing; + bool extending; + bool _isSourcePiston; + + float progress, progressO; + +public: + PistonPieceEntity(); + PistonPieceEntity(int id, int data, int facing, bool extending, bool isSourcePiston); + int getId(); + virtual int getData(); + bool isExtending(); + int getFacing(); + bool isSourcePiston(); + float getProgress(float a); + float getXOff(float a); + float getYOff(float a); + float getZOff(float a); +private: +// static List collisionHolder = new ArrayList(); // 4J - just using local vector for this now + void moveCollidedEntities(float progress, float amount); +public: + void finalTick(); + virtual void tick(); + virtual void load(CompoundTag *tag); + virtual void save(CompoundTag *tag); + + // 4J Added + shared_ptr clone(); +}; \ No newline at end of file diff --git a/Minecraft.World/PistonTileItem.cpp b/Minecraft.World/PistonTileItem.cpp new file mode 100644 index 00000000..7f79b653 --- /dev/null +++ b/Minecraft.World/PistonTileItem.cpp @@ -0,0 +1,14 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "PistonTileItem.h" + +PistonTileItem::PistonTileItem(int id) : TileItem(id) +{ +} + +int PistonTileItem::getLevelDataForAuxValue(int auxValue) +{ + // return an undefined facing until the setPlacedBy method is called + return PistonBaseTile::UNDEFINED_FACING; +} \ No newline at end of file diff --git a/Minecraft.World/PistonTileItem.h b/Minecraft.World/PistonTileItem.h new file mode 100644 index 00000000..1cc9d83a --- /dev/null +++ b/Minecraft.World/PistonTileItem.h @@ -0,0 +1,12 @@ +#pragma once +using namespace std; + +#include "TileItem.h" + +class PistonTileItem : public TileItem +{ +public: + PistonTileItem(int id); + + virtual int getLevelDataForAuxValue(int auxValue); +}; \ No newline at end of file diff --git a/Minecraft.World/PlainsBiome.cpp b/Minecraft.World/PlainsBiome.cpp new file mode 100644 index 00000000..e26c664b --- /dev/null +++ b/Minecraft.World/PlainsBiome.cpp @@ -0,0 +1,9 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" + +PlainsBiome::PlainsBiome(int id) : Biome(id) +{ + decorator->treeCount = -999; + decorator->flowerCount = 4; + decorator->grassCount = 10; +} \ No newline at end of file diff --git a/Minecraft.World/PlainsBiome.h b/Minecraft.World/PlainsBiome.h new file mode 100644 index 00000000..b49fd032 --- /dev/null +++ b/Minecraft.World/PlainsBiome.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Biome.h" + +class PlainsBiome : public Biome +{ + friend class Biome; +protected: + PlainsBiome(int id); +}; \ No newline at end of file diff --git a/Minecraft.World/PlayGoal.cpp b/Minecraft.World/PlayGoal.cpp new file mode 100644 index 00000000..304e7758 --- /dev/null +++ b/Minecraft.World/PlayGoal.cpp @@ -0,0 +1,86 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.util.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.npc.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "BasicTypeContainers.h" +#include "PlayGoal.h" + +PlayGoal::PlayGoal(Villager *mob, float speed) +{ + followFriend = weak_ptr(); + wantedX = wantedY = wantedZ = 0.0; + playTime = 0; + + this->mob = mob; + this->speed = speed; + setRequiredControlFlags(Control::MoveControlFlag); +} + +bool PlayGoal::canUse() +{ + if (mob->getAge() >= 0) return false; + if (mob->getRandom()->nextInt(400) != 0) return false; + + vector > *children = mob->level->getEntitiesOfClass(typeid(Villager), mob->bb->grow(6, 3, 6)); + double closestDistSqr = Double::MAX_VALUE; + //for (Entity c : children) + for(AUTO_VAR(it, children->begin()); it != children->end(); ++it) + { + shared_ptr c = *it; + if (c.get() == mob) continue; + shared_ptr friendV = dynamic_pointer_cast(c); + if (friendV->isChasing()) continue; + if (friendV->getAge() >= 0) continue; + double distSqr = friendV->distanceToSqr(mob->shared_from_this()); + if (distSqr > closestDistSqr) continue; + closestDistSqr = distSqr; + followFriend = weak_ptr(friendV); + } + delete children; + + if (followFriend.lock() == NULL) + { + Vec3 *pos = RandomPos::getPos(dynamic_pointer_cast(mob->shared_from_this()), 16, 3); + if (pos == NULL) return false; + } + return true; +} + +bool PlayGoal::canContinueToUse() +{ + return playTime > 0 && followFriend.lock() != NULL; +} + +void PlayGoal::start() +{ + if (followFriend.lock() != NULL) mob->setChasing(true); + playTime = 1000; +} + +void PlayGoal::stop() +{ + mob->setChasing(false); + followFriend = weak_ptr(); +} + +void PlayGoal::tick() +{ + --playTime; + if (followFriend.lock() != NULL) + { + if (mob->distanceToSqr(followFriend.lock()) > 2 * 2) mob->getNavigation()->moveTo(followFriend.lock(), speed); + } + else + { + if (mob->getNavigation()->isDone()) + { + Vec3 *pos = RandomPos::getPos(dynamic_pointer_cast(mob->shared_from_this()), 16, 3); + if (pos == NULL) return; + mob->getNavigation()->moveTo(pos->x, pos->y, pos->z, speed); + } + } +} \ No newline at end of file diff --git a/Minecraft.World/PlayGoal.h b/Minecraft.World/PlayGoal.h new file mode 100644 index 00000000..a1b1a291 --- /dev/null +++ b/Minecraft.World/PlayGoal.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Goal.h" + +class PlayGoal : public Goal +{ +private: + Villager *mob; + weak_ptr followFriend; + float speed; + double wantedX, wantedY, wantedZ; + int playTime; + +public: + PlayGoal(Villager *mob, float speed); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + virtual void stop(); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/Player.cpp b/Minecraft.World/Player.cpp new file mode 100644 index 00000000..d4832e40 --- /dev/null +++ b/Minecraft.World/Player.cpp @@ -0,0 +1,3002 @@ +// 4J TODO + +// All the instanceof s from Java have been converted to dynamic_cast in this file +// Once all the classes are finished it may be that we do not need to use dynamic_cast +// for every test and a simple virtual function should suffice. We probably only need +// dynamic_cast to find one of the classes that an object derives from, and not to find +// the derived class itself (which should own the virtual GetType function) + +#include "stdafx.h" +#include "JavaMath.h" +#include "net.minecraft.h" +#include "net.minecraft.world.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.chunk.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.inventory.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.effect.h" +#include "net.minecraft.world.food.h" +#include "Inventory.h" +#include "Player.h" +#include "ParticleTypes.h" + +#include "..\Minecraft.Client\Textures.h" + +#include "..\Minecraft.Client\LocalPlayer.h" +#include "..\Minecraft.Client\HumanoidModel.h" +#include "SoundTypes.h" + + + +void Player::_init() +{ + + inventory = shared_ptr( new Inventory( this ) ); + + userType = 0; + score = 0; + oBob = bob = 0.0f; + swinging = false; + swingTime = 0; + + //string name; + dimension = 0; + //string cloakTexture; + + xCloakO = yCloakO = zCloakO = 0.0; + xCloak = yCloak = zCloak = 0.0; + + m_isSleeping = false; + + m_uiPlayerCurrentSkin=0; + + bedPosition = NULL; + + + sleepCounter = 0; + deathFadeCounter=0; + + + bedOffsetX = bedOffsetY = bedOffsetZ = 0.0f; + stats = NULL; + + + respawnPosition = NULL; + minecartAchievementPos = NULL; + + + changingDimensionDelay = 20; + + + isInsidePortal = false; + + + portalTime = oPortalTime = 0.0f; + + dmgSpill = 0; + + + fishing = nullptr; + + distanceWalk = distanceSwim = distanceFall = distanceClimb = distanceMinecart = distanceBoat = distancePig = 0; + + m_uiDebugOptions=0L; + + jumpTriggerTime = 0; + takeXpDelay = 0; + experienceLevel = totalExperience = 0; + experienceProgress = 0.0f; + + useItem = nullptr; + useItemDuration = 0; + + defaultWalkSpeed = 0.1f; + defaultFlySpeed = 0.02f; + + m_uiGamePrivileges = 0; + + m_ppAdditionalModelParts=NULL; + m_bCheckedForModelParts=false; + m_bCheckedDLCForModelParts=false; + +#if defined(__PS3__) || defined(__ORBIS__) + m_ePlayerNameValidState=ePlayerNameValid_NotSet; +#endif + + enderChestInventory = shared_ptr(new PlayerEnderChestContainer()); + + m_bAwardedOnARail=false; +} + +Player::Player(Level *level) : Mob( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + _init(); + MemSect(11); + inventoryMenu = new InventoryMenu(inventory, !level->isClientSide, this); + MemSect(0); + + containerMenu = inventoryMenu; + + heightOffset = 1.62f; + Pos *spawnPos = level->getSharedSpawnPos(); + this->moveTo(spawnPos->x + 0.5, spawnPos->y + 1, spawnPos->z + 0.5, 0, 0); + delete spawnPos; + + modelName = L"humanoid"; + rotOffs = 180; + flameTime = 20; + + textureIdx = TN_MOB_CHAR; // 4J - was L"/mob/char.png"; + m_skinIndex = eDefaultSkins_Skin0; + m_playerIndex = 0; + m_dwSkinId = 0; + m_dwCapeId = 0; + + // 4J Added + m_xuid = INVALID_XUID; + m_OnlineXuid = INVALID_XUID; + //m_bShownOnMaps = true; + setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false); + m_bIsGuest = false; + m_UUID = L""; +} + +Player::~Player() +{ + // TODO 4J + //printf("A player has been destroyed.\n"); + delete inventoryMenu; + + // 4J Stu - Fix for #10938 - CRASH - Game hardlocks when client has an open chest and Xbox Guide while host exits without saving. + // If the container menu is not the inventory menu, then the player has a menu open. These get deleted when the xui scene + // is destroyed, so we can not delete it here + //if( containerMenu != inventoryMenu ) delete containerMenu; +} + +int Player::getMaxHealth() +{ + return MAX_HEALTH; +} + +void Player::defineSynchedData() +{ + this->Mob::defineSynchedData(); + + entityData->define(DATA_PLAYER_FLAGS_ID, (byte) 0); + entityData->define(DATA_PLAYER_RUNNING_ID, (byte) 0); +} + +shared_ptr Player::getUseItem() +{ + return useItem; +} + +int Player::getUseItemDuration() +{ + return useItemDuration; +} + +bool Player::isUsingItem() +{ + return useItem != NULL; +} + +int Player::getTicksUsingItem() +{ + if (isUsingItem()) + { + return useItem->getUseDuration() - useItemDuration; + } + return 0; +} + +void Player::releaseUsingItem() +{ + if (useItem != NULL) + { + useItem->releaseUsing(level, dynamic_pointer_cast( shared_from_this() ), useItemDuration); + + // 4J Stu - Fix for various bugs where an incorrect bow was displayed when it broke (#70859,#93972,#93974) + if (useItem->count == 0) + { + removeSelectedItem(); + } + } + stopUsingItem(); +} + +void Player::stopUsingItem() +{ + useItem = nullptr; + useItemDuration = 0; + if (!level->isClientSide) + { + setUsingItemFlag(false); + } +} + +bool Player::isBlocking() +{ + return isUsingItem() && Item::items[useItem->id]->getUseAnimation(useItem) == UseAnim_block; +} + + +void Player::tick() +{ + if (useItem != NULL) + { + shared_ptr item = inventory->getSelected(); + // 4J Stu - Fix for #45508 - TU5: Gameplay: Eating one piece of food will result in a second piece being eaten as well + // Original code was item != useItem. Changed this now to use the equals function, and add the NULL check as well for the other possible not equals (useItem is not NULL if we are here) + // This is because the useItem and item could be different objects due to an inventory update from the server, but still be the same item (with the same id,count and auxvalue) + if (item == NULL || !item->equals(useItem) ) + { + stopUsingItem(); + } + else + { + if (useItemDuration <= 25 && useItemDuration % 4 == 0) + { + spawnEatParticles(item, 5); + } + if (--useItemDuration == 0) + { + if (!level->isClientSide) + { + completeUsingItem(); + } + } + } + } + + if (takeXpDelay > 0) takeXpDelay--; + + if (isSleeping()) + { + sleepCounter++; + if (sleepCounter > SLEEP_DURATION) + { + sleepCounter = SLEEP_DURATION; + } + + if (!level->isClientSide) + { + if (!checkBed()) + { + stopSleepInBed(true, true, false); + } + else if (level->isDay()) + { + stopSleepInBed(false, true, true); + } + } + } + else if (sleepCounter > 0) + { + sleepCounter++; + if (sleepCounter >= (SLEEP_DURATION + WAKE_UP_DURATION)) + { + sleepCounter = 0; + } + } + + if(!isAlive()) + { + deathFadeCounter++; + if (deathFadeCounter > DEATHFADE_DURATION) + { + deathFadeCounter = DEATHFADE_DURATION; + } + } + this->Mob::tick(); + + if (!level->isClientSide) + { + if (containerMenu != NULL && !containerMenu->stillValid( dynamic_pointer_cast( shared_from_this() ) )) + { + closeContainer(); + containerMenu = inventoryMenu; + } + } + + if (isOnFire() && (abilities.invulnerable || hasInvulnerablePrivilege() ) ) + { + clearFire(); + } + + xCloakO = xCloak; + yCloakO = yCloak; + zCloakO = zCloak; + + double xca = x - xCloak; + double yca = y - yCloak; + double zca = z - zCloak; + + double m = 10; + if (xca > m) xCloakO = xCloak = x; + if (zca > m) zCloakO = zCloak = z; + if (yca > m) yCloakO = yCloak = y; + if (xca < -m) xCloakO = xCloak = x; + if (zca < -m) zCloakO = zCloak = z; + if (yca < -m) yCloakO = yCloak = y; + + xCloak += xca * 0.25; + zCloak += zca * 0.25; + yCloak += yca * 0.25; + + if (riding == NULL) + { + if( minecartAchievementPos != NULL ) + { + delete minecartAchievementPos; + minecartAchievementPos = NULL; + } + } + + if (!level->isClientSide) + { + foodData.tick(dynamic_pointer_cast(shared_from_this())); + } + + // 4J Stu Debugging + if (!level->isClientSide) + { + static int count = 0; + if( count++ == 100 ) + { +#if 0 +#ifdef _WINDOWS64 + // Drop some items so we have them in inventory to play with + this->drop( shared_ptr( new ItemInstance(Tile::recordPlayer) ) ); + this->drop( shared_ptr( new ItemInstance(Item::map) ) ); + this->drop( shared_ptr( new ItemInstance(Item::record_01) ) ); + this->drop( shared_ptr( new ItemInstance(Item::record_02) ) ); + this->drop( shared_ptr(new ItemInstance( Item::pickAxe_diamond, 1 )) ); +#endif + +#ifdef __PS3__ + // #ifdef _DEBUG + // // Drop some items so we have them in inventory to play with + // this->drop( shared_ptr( new ItemInstance(Tile::recordPlayer) ) ); + // this->drop( shared_ptr( new ItemInstance(Item::map) ) ); + // this->drop( shared_ptr( new ItemInstance(Item::record_01) ) ); + // this->drop( shared_ptr( new ItemInstance(Item::record_02) ) ); + // this->drop( shared_ptr(new ItemInstance( Item::pickAxe_diamond, 1 )) ); + // #endif +#endif + +#ifdef _DURANGO + // Drop some items so we have them in inventory to play with + this->drop( shared_ptr( new ItemInstance(Tile::recordPlayer) ) ); + this->drop( shared_ptr( new ItemInstance(Item::map) ) ); + this->drop( shared_ptr( new ItemInstance(Item::record_01) ) ); + this->drop( shared_ptr( new ItemInstance(Item::record_02) ) ); + this->drop( shared_ptr(new ItemInstance( Item::pickAxe_diamond, 1 )) ); +#endif +#endif + // 4J-PB - Throw items out at the start of the level + //this->drop( new ItemInstance( Item::pickAxe_diamond, 1 ) ); + //this->drop( new ItemInstance( Tile::workBench, 1 ) ); + //this->drop( new ItemInstance( Tile::treeTrunk, 8 ) ); + //this->drop( shared_ptr( new ItemInstance( Item::milk, 3 ) ) ); + //this->drop( shared_ptr( new ItemInstance( Item::sugar, 2 ) ) ); + //this->drop( new ItemInstance( Tile::stoneBrick, 8 ) ); + //this->drop( shared_ptr( new ItemInstance( Item::wheat, 3 ) ) ); + //this->drop( shared_ptr( new ItemInstance( Item::egg, 1 ) ) ); + //this->drop( new ItemInstance( Item::bow, 1 ) ); + //this->drop( new ItemInstance( Item::arrow, 10 ) ); + //this->drop( shared_ptr( new ItemInstance( Item::saddle, 10 ) ) ); + //this->drop( shared_ptr( new ItemInstance( Tile::fence, 64 ) ) ); + //this->drop( shared_ptr( new ItemInstance( Tile::fence, 64 ) ) ); + //this->drop( shared_ptr( new ItemInstance( Tile::fence, 64 ) ) ); + + + //shared_ptr mob = dynamic_pointer_cast(Pig::_class->newInstance( level )); + //mob->moveTo(x+1, y, z+1, level->random->nextFloat() * 360, 0); + //level->addEntity(mob); + + // 4J : WESTY : Spawn some wolves to befriend! + /* + shared_ptr mob1 = dynamic_pointer_cast(Wolf::_class->newInstance( level )); + mob1->moveTo(x+1, y, z+1, level->random->nextFloat() * 360, 0); + level->addEntity(mob1); + + shared_ptr mob2 = dynamic_pointer_cast(Wolf::_class->newInstance( level )); + mob2->moveTo(x+2, y, z+1, level->random->nextFloat() * 360, 0); + level->addEntity(mob2); + + shared_ptr mob3 = dynamic_pointer_cast(Wolf::_class->newInstance( level )); + mob3->moveTo(x+1, y, z+2, level->random->nextFloat() * 360, 0); + level->addEntity(mob3); + + shared_ptr mob4 = dynamic_pointer_cast(Wolf::_class->newInstance( level )); + mob4->moveTo(x+3, y, z+1, level->random->nextFloat() * 360, 0); + level->addEntity(mob4); + + shared_ptr mob5 = dynamic_pointer_cast(Wolf::_class->newInstance( level )); + mob5->moveTo(x+1, y, z+3, level->random->nextFloat() * 360, 0); + level->addEntity(mob5); + */ + + // inventory.add(new ItemInstance(Item.potion, 1, PotionBrewing.THROWABLE_MASK | 0xc)); + // addEffect(new MobEffectInstance(MobEffect.blindness.id, 60)); + // increaseXp(10); + + { + // ItemInstance itemInstance = new ItemInstance(Item.pickAxe_diamond); + // itemInstance.enchant(Enchantment.diggingBonus, 3); + // inventory.add(itemInstance); + } + } +#if 0 + // 4J Stu - This makes a tunnel with a powered track just over length to get the On A Rail achievement + // It needs a few items at the start to get you going (a level and some powered rails) and of course a + // minecart. For some reason some of the torches come off so it will also need some fixing along the way. + static bool madeTrack = false; + if( !madeTrack ) + { + this->drop( shared_ptr( new ItemInstance( Item::minecart, 1 ) ) ); + this->drop( shared_ptr( new ItemInstance( Tile::goldenRail, 10 ) ) ); + this->drop( shared_ptr( new ItemInstance( Tile::lever, 10 ) ) ); + + level->setTime( 0 ); + int poweredCount = 0; + for(int i = 10; i < 2800; ++i) + { + level->setTile(x+i,y-1,z-2,Tile::quartzBlock_Id); + level->setTile(x+i,y,z-2,Tile::quartzBlock_Id); + level->setTile(x+i,y+1,z-2,Tile::quartzBlock_Id); + level->setTile(x+i,y+2,z-2,Tile::lightGem_Id); + level->setTile(x+i,y+3,z-2,Tile::quartzBlock_Id); + + level->setTile(x+i,y-1,z-1,Tile::stoneBrick_Id); + if(i%20 == 0) + { + level->setTile(x+i,y,z-1,Tile::notGate_on_Id); + poweredCount = 4; + } + else + { + level->setTile(x+i,y,z-1,0); + } + level->setTile(x+i,y+1,z-1,0); + level->setTile(x+i,y+2,z-1,0); + level->setTile(x+i,y+3,z-1,0); + + level->setTile(x+i,y-1,z,Tile::stoneBrick_Id); + if(poweredCount>0) + { + level->setTile(x+i,y,z,Tile::goldenRail_Id); + --poweredCount; + } + else + { + level->setTile(x+i,y,z,Tile::rail_Id); + } + level->setTile(x+i,y+1,z,0); + level->setTile(x+i,y+2,z,0); + level->setTile(x+i,y+3,z,0); + + level->setTile(x+i,y-1,z+1,Tile::stoneBrick_Id); + if((i+5)%20 == 0) + { + level->setTile(x+i,y,z+1,Tile::torch_Id); + } + else + { + level->setTile(x+i,y,z+1,0); + } + level->setTile(x+i,y+1,z+1,0); + level->setTile(x+i,y+2,z+1,0); + level->setTile(x+i,y+3,z+1,0); + + level->setTile(x+i,y-1,z+2,Tile::quartzBlock_Id); + level->setTile(x+i,y,z+2,Tile::quartzBlock_Id); + level->setTile(x+i,y+1,z+2,Tile::quartzBlock_Id); + level->setTile(x+i,y+2,z+2,Tile::lightGem_Id); + level->setTile(x+i,y+3,z+2,Tile::quartzBlock_Id); + } + madeTrack = true; + } +#endif + } + //End 4J sTU +} + +void Player::spawnEatParticles(shared_ptr useItem, int count) +{ + if (useItem->getUseAnimation() == UseAnim_drink) + { + level->playSound(shared_from_this(), eSoundType_RANDOM_DRINK, 0.5f, level->random->nextFloat() * 0.1f + 0.9f); + } + if (useItem->getUseAnimation() == UseAnim_eat) + { + for (int i = 0; i < count; i++) + { + Vec3 *d = Vec3::newTemp((random->nextFloat() - 0.5) * 0.1, Math::random() * 0.1 + 0.1, 0); + + d->xRot(-xRot * PI / 180); + d->yRot(-yRot * PI / 180); + + Vec3 *p = Vec3::newTemp((random->nextFloat() - 0.5) * 0.3, -random->nextFloat() * 0.6 - 0.3, 0.6); + p->xRot(-xRot * PI / 180); + p->yRot(-yRot * PI / 180); + p = p->add(x, y + getHeadHeight(), z); + + level->addParticle(PARTICLE_ICONCRACK(useItem->getItem()->id,0), p->x, p->y, p->z, d->x, d->y + 0.05, d->z); + } + + // 4J Stu - Was L"mob.eat" which doesnt exist + level->playSound(shared_from_this(), eSoundType_RANDOM_EAT, 0.5f + 0.5f * random->nextInt(2), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); + } +} + +void Player::completeUsingItem() +{ + if (useItem != NULL) + { + spawnEatParticles(useItem, 16); + + int oldCount = useItem->count; + shared_ptr itemInstance = useItem->useTimeDepleted(level, dynamic_pointer_cast(shared_from_this())); + if (itemInstance != useItem || (itemInstance != NULL && itemInstance->count != oldCount)) + { + inventory->items[inventory->selected] = itemInstance; + if (itemInstance->count == 0) + { + inventory->items[inventory->selected] = nullptr; + } + } + stopUsingItem(); + } +} + +void Player::handleEntityEvent(byte id) +{ + if (id == EntityEvent::USE_ITEM_COMPLETE) + { + completeUsingItem(); + } + else + { + Mob::handleEntityEvent(id); + } +} + +bool Player::isImmobile() +{ + return getHealth() <= 0 || isSleeping(); +} + + +void Player::closeContainer() +{ + containerMenu = inventoryMenu; +} + +void Player::ride(shared_ptr e) +{ + if (riding != NULL && e == NULL) + { + if (!level->isClientSide) findStandUpPosition(riding); + + if (riding != NULL) + { + riding->rider = weak_ptr(); + } + riding = nullptr; + + return; + } + Mob::ride(e); +} + +void Player::setPlayerDefaultSkin(EDefaultSkins skin) +{ +#ifndef _CONTENT_PACKAGE + wprintf(L"Setting default skin to %d for player %ls\n", skin, name.c_str() ); +#endif + m_skinIndex = skin; +} + +void Player::setCustomSkin(DWORD skinId) +{ +#ifndef _CONTENT_PACKAGE + wprintf(L"Attempting to set skin to %08X for player %ls\n", skinId, name.c_str() ); +#endif + EDefaultSkins playerSkin = eDefaultSkins_ServerSelected; + + // reset the idle + setIsIdle(false); + + setAnimOverrideBitmask(getSkinAnimOverrideBitmask(skinId)); + if( !GET_IS_DLC_SKIN_FROM_BITMASK(skinId) ) + { + // GET_UGC_SKIN_ID_FROM_BITMASK will always be zero - this was for a possible custom skin editor skin + DWORD ugcSkinIndex = GET_UGC_SKIN_ID_FROM_BITMASK(skinId); + DWORD defaultSkinIndex = GET_DEFAULT_SKIN_ID_FROM_BITMASK(skinId); + if( ugcSkinIndex == 0 && defaultSkinIndex > 0 ) + { + playerSkin = (EDefaultSkins) defaultSkinIndex; + } + } + + if( playerSkin == eDefaultSkins_ServerSelected) + { + playerSkin = (EDefaultSkins)(m_playerIndex + 1); + } + + // We always set a default skin, since we may be waiting for the player's custom skin to be transmitted + setPlayerDefaultSkin( playerSkin ); + + m_dwSkinId = skinId; + this->customTextureUrl = app.getSkinPathFromId(skinId); + + // set the new player additional boxes + /*vector *pvModelParts=app.GetAdditionalModelParts(m_dwSkinId); + + if(pvModelParts==NULL) + { + // we don't have the data from the dlc skin yet + app.DebugPrintf("Couldn't get model parts for skin %X\n",m_dwSkinId); + + // do we have it from the DLC pack? + DLCSkinFile *pDLCSkinFile = app.m_dlcManager.getSkinFile(this->customTextureUrl); + + if(pDLCSkinFile!=NULL) + { + DWORD dwBoxC=pDLCSkinFile->getAdditionalBoxesCount(); + if(dwBoxC!=0) + { + app.DebugPrintf("Got model parts from DLCskin for skin %X\n",m_dwSkinId); + pvModelParts=app.SetAdditionalSkinBoxes(m_dwSkinId,pDLCSkinFile->getAdditionalBoxes()); + this->SetAdditionalModelParts(pvModelParts); + } + else + { + this->SetAdditionalModelParts(NULL); + } + app.SetAnimOverrideBitmask(pDLCSkinFile->getSkinID(),pDLCSkinFile->getAnimOverrideBitmask()); + } + else + { + this->SetAdditionalModelParts(NULL); + } + } + else + { + app.DebugPrintf("Got model parts from app.GetAdditionalModelParts for skin %X\n",m_dwSkinId); + + this->SetAdditionalModelParts(pvModelParts); + }*/ + + // reset the check for model parts + m_bCheckedForModelParts=false; + m_bCheckedDLCForModelParts=false; + this->SetAdditionalModelParts(NULL); + + +} + +unsigned int Player::getSkinAnimOverrideBitmask(DWORD skinId) +{ + unsigned long bitmask = 0L; + if( GET_IS_DLC_SKIN_FROM_BITMASK(skinId) ) + { + // Temp check for anim override + switch( GET_DLC_SKIN_ID_FROM_BITMASK(skinId) ) + { + case 0x2://SP1_ZOMBIE: + case 0x3://SP1_HEROBRINE: + case 0xc8://SP3_ZOMBIE_PIGMAN: + case 0xc9://SP3_ZOMBIE_HEROBRINE: + case 0x1f8: // SPH_4JMUMMY + case 0x220: // SPH_AOT_MUMMY + case 0x23a: // SPH_CLIMAX_ZOMBIEBUSINESSMAN + case 0x23d: // SPH_CLIMAX_EVILROBOT + case 0x247: // SPH_CLIMAX_ZOMBIE + case 0x194: // SOA_DEADLIGHT_SKINNY_ZOMBIE + case 0x195: // SOA_DEADLIGHT_FEMALE_ZOMBIE + bitmask = 1< 0) + { + this->customTextureUrl2 = Player::getCapePathFromId(capeId); + } + else + { + MOJANG_DATA *pMojangData=app.GetMojangDataForXuid(getOnlineXuid()); + if(pMojangData) + { + // Cape + if(pMojangData->wchCape) + { + this->customTextureUrl2= pMojangData->wchCape; + } + else + { + if(app.DefaultCapeExists()) + { + this->customTextureUrl2= wstring(L"Special_Cape.png"); + } + else + { + this->customTextureUrl2= wstring(L""); + } + } + + } + else + { + // if there is a custom default cloak, then set it here + if(app.DefaultCapeExists()) + { + this->customTextureUrl2= wstring(L"Special_Cape.png"); + } + else + { + this->customTextureUrl2 =wstring(L""); + } + } + } +} + +DWORD Player::getCapeIdFromPath(const wstring &cape) +{ + bool dlcCape = false; + unsigned int capeId = 0; + + if(cape.size() >= 14) + { + dlcCape = cape.substr(0,3).compare(L"dlc") == 0; + + wstring capeValue = cape.substr(7,cape.size()); + capeValue = capeValue.substr(0,capeValue.find_first_of(L'.')); + + std::wstringstream ss; + // 4J Stu - dlc skins are numbered using decimal to make it easier for artists/people to number manually + // Everything else is numbered using hex + if(dlcCape) + ss << std::dec << capeValue.c_str(); + else + ss << std::hex << capeValue.c_str(); + ss >> capeId; + + capeId = MAKE_SKIN_BITMASK(dlcCape, capeId); + } + return capeId; +} + +wstring Player::getCapePathFromId(DWORD capeId) +{ + // 4J Stu - This function maps the encoded DWORD we store in the player profile + // to a filename that is stored as a memory texture and shared between systems in game + wchar_t chars[256]; + if( GET_IS_DLC_SKIN_FROM_BITMASK(capeId) ) + { + // 4J Stu - DLC skins are numbered using decimal rather than hex to make it easier to number manually + swprintf(chars,256,L"dlccape%08d.png",GET_DLC_SKIN_ID_FROM_BITMASK(capeId)); + + } + else + { + DWORD ugcCapeIndex = GET_UGC_SKIN_ID_FROM_BITMASK(capeId); + DWORD defaultCapeIndex = GET_DEFAULT_SKIN_ID_FROM_BITMASK(capeId); + if( ugcCapeIndex == 0 ) + { + swprintf(chars,256,L"defcape%08X.png",defaultCapeIndex); + } + else + { + swprintf(chars,256,L"ugccape%08X.png",ugcCapeIndex); + } + } + return chars; +} + +void Player::ChangePlayerSkin() +{ + + if(app.vSkinNames.size()>0) + { + + m_uiPlayerCurrentSkin++; + if(m_uiPlayerCurrentSkin>app.vSkinNames.size()) + { + m_uiPlayerCurrentSkin=0; + this->customTextureUrl=L""; + } + else + { + if(m_uiPlayerCurrentSkin>0) + { + // change this players custom texture url + this->customTextureUrl=app.vSkinNames[m_uiPlayerCurrentSkin-1]; + } + } + } +} + +void Player::prepareCustomTextures() +{ + MOJANG_DATA *pMojangData=app.GetMojangDataForXuid(getOnlineXuid()); + + if(pMojangData) + { + // Skin + if(pMojangData->wchSkin) + { + this->customTextureUrl= pMojangData->wchSkin; + } + + // 4J Stu - Don't update the cape here, it gets set elsewhere + // Cape + //if(pMojangData->wchCape) + //{ + // this->customTextureUrl2= pMojangData->wchCape; + //} + //else + //{ + // if(app.DefaultCapeExists()) + // { + // this->customTextureUrl2= wstring(L"Default_Cape.png"); + // } + // else + // { + // this->customTextureUrl2= wstring(L""); + // } + //} + + } + else + { + // 4J Stu - Don't update the cape here, it gets set elsewhere + // if there is a custom default cloak, then set it here + //if(app.DefaultCapeExists()) + //{ + // this->customTextureUrl2= wstring(L"Default_Cape.png"); + //} + //else + //{ + // this->customTextureUrl2 =wstring(L""); + //} + } + + /*cloakTexture = wstring(L"http://s3.amazonaws.com/MinecraftCloaks/").append( name ).append( L".png" );*/ + //this->customTextureUrl2 = cloakTexture; +} + +void Player::rideTick() +{ + double preX = x, preY = y, preZ = z; + float preYRot = yRot, preXRot = xRot; + + this->Mob::rideTick(); + oBob = bob; + bob = 0; + + checkRidingStatistiscs(x - preX, y - preY, z - preZ); + + // riding can be set to null inside 'Entity::rideTick()'. + if ( riding != NULL && (riding->GetType() & eTYPE_PIG) == eTYPE_PIG ) + { + // 4J Stu - I don't know why we would want to do this, but it means that the players head is locked in position and can't move around + //xRot = preXRot; + //yRot = preYRot; + + shared_ptr pig = dynamic_pointer_cast(riding); + yBodyRot = pig->yBodyRot; + + while (yBodyRot - yBodyRotO < -180) + yBodyRotO -= 360; + while (yBodyRot - yBodyRotO >= 180) + yBodyRotO += 360; + } +} + + +void Player::resetPos() +{ + heightOffset = 1.62f; + setSize(0.6f, 1.8f); + this->Mob::resetPos(); + setHealth(getMaxHealth()); + deathTime = 0; +} + +int Player::getCurrentSwingDuration() +{ + if (hasEffect(MobEffect::digSpeed)) + { + return SWING_DURATION - (1 + getEffect(MobEffect::digSpeed)->getAmplifier()) * 1; + } + if (hasEffect(MobEffect::digSlowdown)) + { + return SWING_DURATION + (1 + getEffect(MobEffect::digSlowdown)->getAmplifier()) * 2; + } + return SWING_DURATION; +} + +void Player::serverAiStep() +{ + int currentSwingDuration = getCurrentSwingDuration(); + if (swinging) + { + swingTime++; + if (swingTime >= currentSwingDuration) + { + swingTime = 0; + swinging = false; + } + } + else + { + swingTime = 0; + } + + attackAnim = swingTime / (float) currentSwingDuration; +} + + +void Player::aiStep() +{ + if (jumpTriggerTime > 0) jumpTriggerTime--; + + if (level->difficulty == Difficulty::PEACEFUL && getHealth() < getMaxHealth()) + { + if (tickCount % 20 * 12 == 0) heal(1); + } + inventory->tick(); + oBob = bob; + + this->Mob::aiStep(); + + this->walkingSpeed = abilities.getWalkingSpeed(); + this->flyingSpeed = defaultFlySpeed; + if (isSprinting()) + { + walkingSpeed += abilities.getWalkingSpeed() * 0.3f; + flyingSpeed += defaultFlySpeed * 0.3f; + } + + float tBob = (float) sqrt(xd * xd + zd * zd); + + // 4J added - we were getting a NaN with zero xd & zd + if(( xd * xd + zd * zd ) < 0.00001f ) + { + tBob = 0.0f; + } + + float tTilt = (float) atan(-yd * 0.2f) * 15.0f; + if (tBob > 0.1f) tBob = 0.1f; + if (!onGround || getHealth() <= 0) tBob = 0; + if (onGround || getHealth() <= 0) tTilt = 0; + + bob += (tBob - bob) * 0.4f; + + tilt += (tTilt - tilt) * 0.8f; + + if (getHealth() > 0) + { + vector > *entities = level->getEntities(shared_from_this(), bb->grow(1, 0, 1)); + if (entities != NULL) + { + AUTO_VAR(itEnd, entities->end()); + for (AUTO_VAR(it, entities->begin()); it != itEnd; it++) + { + shared_ptr e = *it; //entities->at(i); + if (!e->removed) + { + touch(e); + } + } + } + } +} + + +void Player::touch(shared_ptr entity) +{ + entity->playerTouch( dynamic_pointer_cast( shared_from_this() ) ); +} + +// 4J - Removed 1.0.1 +//bool Player::addResource(int resource) +//{ +// return inventory->add(shared_ptr( new ItemInstance(resource, 1, 0) ) ); +//} + +int Player::getScore() +{ + return score; +} + +void Player::die(DamageSource *source) +{ + this->Mob::die(source); + this->setSize(0.2f, 0.2f); + setPos(x, y, z); + yd = 0.1f; + + // 4J - TODO need to use a xuid + if ( app.isXuidNotch( m_xuid ) ) + { + drop(shared_ptr( new ItemInstance(Item::apple, 1) ), true); + } + inventory->dropAll(); + + if (source != NULL) + { + xd = -Mth::cos((hurtDir + yRot) * PI / 180) * 0.1f; + zd = -Mth::sin((hurtDir + yRot) * PI / 180) * 0.1f; + } + else + { + xd = zd = 0; + } + this->heightOffset = 0.1f; +} + +void Player::awardKillScore(shared_ptr victim, int score) +{ + this->score += score; +} + +int Player::decreaseAirSupply(int currentSupply) +{ + int oxygenBonus = EnchantmentHelper::getOxygenBonus(inventory); + if (oxygenBonus > 0) + { + if (random->nextInt(oxygenBonus + 1) > 0) + { + // the oxygen bonus prevents us from drowning + return currentSupply; + } + } + return Mob::decreaseAirSupply(currentSupply); +} + +bool Player::isShootable() +{ + return true; +} + +bool Player::isCreativeModeAllowed() +{ + return true; +} + +shared_ptr Player::drop() +{ + return drop(inventory->removeItem(inventory->selected, 1), false); +} + +shared_ptr Player::drop(shared_ptr item) +{ + return drop(item, false); +} + +shared_ptr Player::drop(shared_ptr item, bool randomly) +{ + if (item == NULL) return nullptr; + + shared_ptr thrownItem = shared_ptr( new ItemEntity(level, x, y - 0.3f + getHeadHeight(), z, item) ); + thrownItem->throwTime = 20 * 2; + + thrownItem->setThrower(getName()); + + float pow = 0.1f; + if (randomly) + { + float _pow = random->nextFloat() * 0.5f; + float dir = random->nextFloat() * PI * 2; + thrownItem->xd = -sin(dir) * _pow; + thrownItem->zd = cos(dir) * _pow; + thrownItem->yd = 0.2f; + + } + else + { + pow = 0.3f; + thrownItem->xd = -sin(yRot / 180 * PI) * cos(xRot / 180 * PI) * pow; + thrownItem->zd = cos(yRot / 180 * PI) * cos(xRot / 180 * PI) * pow; + thrownItem->yd = -sin(xRot / 180 * PI) * pow + 0.1f; + pow = 0.02f; + + float dir = random->nextFloat() * PI * 2; + pow *= random->nextFloat(); + thrownItem->xd += cos(dir) * pow; + thrownItem->yd += (random->nextFloat() - random->nextFloat()) * 0.1f; + thrownItem->zd += sin(dir) * pow; + } + + reallyDrop(thrownItem); + + return thrownItem; +} + + +void Player::reallyDrop(shared_ptr thrownItem) +{ + level->addEntity(thrownItem); +} + + +float Player::getDestroySpeed(Tile *tile) +{ + float speed = inventory->getDestroySpeed(tile); + + int efficiency = EnchantmentHelper::getDiggingBonus(inventory); + if (efficiency > 0 && inventory->canDestroy(tile)) + { + speed += (efficiency * efficiency + 1); + } + + if (hasEffect(MobEffect::digSpeed)) + { + speed *= 1.0f + (getEffect(MobEffect::digSpeed)->getAmplifier() + 1) * .2f; + } + if (hasEffect(MobEffect::digSlowdown)) + { + speed *= 1.0f - (getEffect(MobEffect::digSlowdown)->getAmplifier() + 1) * .2f; + } + + if (isUnderLiquid(Material::water) && !EnchantmentHelper::hasWaterWorkerBonus(inventory)) speed /= 5; + + // 4J Stu - onGround is set to true on the client when we are flying, which means + // the dig speed is out of sync with the server. Removing this speed change when + // flying so that we always dig as the same speed + //if (!onGround) speed /= 5; + + return speed; +} + +bool Player::canDestroy(Tile *tile) +{ + return inventory->canDestroy(tile); +} + +void Player::readAdditionalSaveData(CompoundTag *entityTag) +{ + Mob::readAdditionalSaveData(entityTag); + ListTag *inventoryList = (ListTag *) entityTag->getList(L"Inventory"); + inventory->load(inventoryList); + dimension = entityTag->getInt(L"Dimension"); + m_isSleeping = entityTag->getBoolean(L"Sleeping"); + sleepCounter = entityTag->getShort(L"SleepTimer"); + + experienceProgress = entityTag->getFloat(L"XpP"); + experienceLevel = entityTag->getInt(L"XpLevel"); + totalExperience = entityTag->getInt(L"XpTotal"); + + if (m_isSleeping) + { + bedPosition = new Pos( Mth::floor(x), Mth::floor(y), Mth::floor(z)); + stopSleepInBed(true, true, false); + } + + if (entityTag->contains(L"SpawnX") && entityTag->contains(L"SpawnY") && entityTag->contains(L"SpawnZ")) + { + respawnPosition = new Pos(entityTag->getInt(L"SpawnX"), entityTag->getInt(L"SpawnY"), entityTag->getInt(L"SpawnZ")); + } + + foodData.readAdditionalSaveData(entityTag); + abilities.loadSaveData(entityTag); + + if (entityTag->contains(L"EnderItems")) + { + ListTag *enderItemsList = (ListTag *) entityTag->getList(L"EnderItems"); + enderChestInventory->setItemsByTag(enderItemsList); + } + + // 4J Added + m_uiGamePrivileges = entityTag->getInt(L"GamePrivileges"); +} + +void Player::addAdditonalSaveData(CompoundTag *entityTag) +{ + Mob::addAdditonalSaveData(entityTag); + entityTag->put(L"Inventory", inventory->save(new ListTag())); + entityTag->putInt(L"Dimension", dimension); + entityTag->putBoolean(L"Sleeping", m_isSleeping); + entityTag->putShort(L"SleepTimer", (short) sleepCounter); + + entityTag->putFloat(L"XpP", experienceProgress); + entityTag->putInt(L"XpLevel", experienceLevel); + entityTag->putInt(L"XpTotal", totalExperience); + + if (respawnPosition != NULL) + { + entityTag->putInt(L"SpawnX", respawnPosition->x); + entityTag->putInt(L"SpawnY", respawnPosition->y); + entityTag->putInt(L"SpawnZ", respawnPosition->z); + } + + foodData.addAdditonalSaveData(entityTag); + abilities.addSaveData(entityTag); + + entityTag->put(L"EnderItems", enderChestInventory->createTag()); + + // 4J Added + entityTag->putInt(L"GamePrivileges",m_uiGamePrivileges); + +} + +Pos *Player::getRespawnPosition(Level *level, CompoundTag *entityTag) +{ + if (entityTag->contains(L"SpawnX") && entityTag->contains(L"SpawnY") && entityTag->contains(L"SpawnZ")) + { + return new Pos(entityTag->getInt(L"SpawnX"), entityTag->getInt(L"SpawnY"), entityTag->getInt(L"SpawnZ")); + } + return level->getSharedSpawnPos(); +} + +bool Player::openContainer(shared_ptr container) +{ + return true; +} + +bool Player::startEnchanting(int x, int y, int z) +{ + return true; +} + +bool Player::startRepairing(int x, int y, int z) +{ + return true; +} + +bool Player::startCrafting(int x, int y, int z) +{ + return true; +} + +void Player::take(shared_ptr e, int orgCount) +{ +} + +float Player::getHeadHeight() +{ + return 0.12f; +} + + +void Player::setDefaultHeadHeight() +{ + heightOffset = 1.62f; +} + +bool Player::hurt(DamageSource *source, int dmg) +{ + if ( hasInvulnerablePrivilege() || (abilities.invulnerable && !source->isBypassInvul()) ) return false; + + // 4J-JEV: Fix for PSVita: #3987 - [IN GAME] The user can take damage/die, when attempting to re-enter fly mode when falling from a height. + if ( source == DamageSource::fall && isAllowedToFly() && abilities.flying ) return false; + + noActionTime = 0; + if (getHealth() <= 0) return false; + + if (isSleeping() && !level->isClientSide) + { + stopSleepInBed(true, true, false); + } + + if ( source->scalesWithDifficulty() ) + { + if (level->difficulty == Difficulty::PEACEFUL) dmg = 0; + if (level->difficulty == Difficulty::EASY) dmg = dmg / 2 + 1; + if (level->difficulty == Difficulty::HARD) dmg = dmg * 3 / 2; + } + + if (dmg == 0) return false; + + shared_ptr attacker = source->getEntity(); + if ( dynamic_pointer_cast( attacker ) != NULL ) + { + if ((dynamic_pointer_cast(attacker))->owner != NULL) + { + attacker = (dynamic_pointer_cast(attacker))->owner; + } + } + if ( dynamic_pointer_cast( attacker ) != NULL ) + { + // aggreviate all pet wolves nearby + directAllTameWolvesOnTarget(dynamic_pointer_cast(attacker), false); + } + + return this->Mob::hurt(source, dmg); +} + +int Player::getDamageAfterMagicAbsorb(DamageSource *damageSource, int damage) +{ + int remainingDamage = Mob::getDamageAfterMagicAbsorb(damageSource, damage); + if (remainingDamage <= 0) + { + return 0; + } + + int enchantmentArmor = EnchantmentHelper::getDamageProtection(inventory, damageSource); + if (enchantmentArmor > 20) + { + enchantmentArmor = 20; + } + if (enchantmentArmor > 0 && enchantmentArmor <= 20) + { + int absorb = 25 - enchantmentArmor; + int v = remainingDamage * absorb + dmgSpill; + remainingDamage = v / 25; + dmgSpill = v % 25; + } + + return remainingDamage; +} + +bool Player::isPlayerVersusPlayer() +{ + return false; +} + +void Player::directAllTameWolvesOnTarget(shared_ptr target, bool skipSitting) +{ + + // filter un-attackable mobs + if ((dynamic_pointer_cast( target ) != NULL) || (dynamic_pointer_cast( target) != NULL)) + { + return; + } + // never target wolves that has this player as owner + if (dynamic_pointer_cast(target) != NULL) + { + shared_ptr wolfTarget = dynamic_pointer_cast(target); + if (wolfTarget->isTame() && m_UUID.compare( wolfTarget->getOwnerUUID() ) == 0 ) + { + return; + } + } + if ((dynamic_pointer_cast( target ) != NULL) && !isPlayerVersusPlayer()) + { + // pvp is off + return; + } + + + // TODO: Optimize this? Most of the time players wont have pets: + vector > *nearbyWolves = level->getEntitiesOfClass(typeid(Wolf), AABB::newTemp(x, y, z, x + 1, y + 1, z + 1)->grow(16, 4, 16)); + AUTO_VAR(itEnd, nearbyWolves->end()); + for (AUTO_VAR(it, nearbyWolves->begin()); it != itEnd; it++) + { + shared_ptr wolf = dynamic_pointer_cast(*it);; + if (wolf->isTame() && wolf->getAttackTarget() == NULL && m_UUID.compare( wolf->getOwnerUUID() ) == 0) + { + if (!skipSitting || !wolf->isSitting()) + { + wolf->setSitting(false); + wolf->setAttackTarget(target); + } + } + } + delete nearbyWolves; + +} + +void Player::hurtArmor(int damage) +{ + inventory->hurtArmor(damage); +} + +int Player::getArmorValue() +{ + return inventory->getArmorValue(); +} + +float Player::getArmorCoverPercentage() +{ + int count = 0; + for (int i = 0; i < inventory->armor.length; i++) + { + if (inventory->armor[i] != NULL) { + count++; + } + } + return (float) count / (float) inventory->armor.length; +} + +void Player::actuallyHurt(DamageSource *source, int dmg) +{ + if (!source->isBypassArmor() && isBlocking()) + { + dmg = (1 + dmg) >> 1; + } + dmg = getDamageAfterArmorAbsorb(source, dmg); + dmg = getDamageAfterMagicAbsorb(source, dmg); + causeFoodExhaustion(source->getFoodExhaustion()); + //this->Mob::actuallyHurt(source, dmg); + health -= dmg; +} + + +bool Player::openFurnace(shared_ptr container) +{ + return true; +} + +bool Player::openTrap(shared_ptr container) +{ + return true; +} + +void Player::openTextEdit(shared_ptr sign) +{ +} + +bool Player::openBrewingStand(shared_ptr brewingStand) +{ + return true; +} + +bool Player::openTrading(shared_ptr traderTarget) +{ + return true; +} + +/** +* Opens an iteminstance-dependent user interface. +* +* @param itemInstance +*/ +void Player::openItemInstanceGui(shared_ptr itemInstance) +{ +} + +bool Player::interact(shared_ptr entity) +{ + if (entity->interact( dynamic_pointer_cast( shared_from_this() ) )) return true; + shared_ptr item = getSelectedItem(); + if (item != NULL && dynamic_pointer_cast( entity ) != NULL) + { + // 4J - PC Comments + // Hack to prevent item stacks from decrementing if the player has + // the ability to instabuild + if(this->abilities.instabuild) item = item->copy(); + if(item->interactEnemy(dynamic_pointer_cast(entity))) + { + // 4J - PC Comments + // Don't remove the item in hand if the player has the ability + // to + // instabuild + if (item->count <= 0 && !this->abilities.instabuild) + { + removeSelectedItem(); + } + return true; + } + } + return false; +} + +shared_ptr Player::getSelectedItem() +{ + return inventory->getSelected(); +} + +void Player::removeSelectedItem() +{ + inventory->setItem(inventory->selected, nullptr); +} + +double Player::getRidingHeight() +{ + return heightOffset - 0.5f; +} + +void Player::swing() +{ + if (!swinging || swingTime >= getCurrentSwingDuration() / 2 || swingTime < 0) + { + swingTime = -1; + swinging = true; + } +} + +void Player::attack(shared_ptr entity) +{ + if (!entity->isAttackable()) + { + return; + } + + int dmg = inventory->getAttackDamage(entity); + + if (hasEffect(MobEffect::damageBoost)) + { + dmg += (3 << getEffect(MobEffect::damageBoost)->getAmplifier()); + } + if (hasEffect(MobEffect::weakness)) + { + dmg -= (2 << getEffect(MobEffect::weakness)->getAmplifier()); + } + + int knockback = 0; + int magicBoost = 0; + shared_ptr mob = dynamic_pointer_cast(entity); + if (mob != NULL) + { + magicBoost = EnchantmentHelper::getDamageBonus(inventory, mob); + knockback += EnchantmentHelper::getKnockbackBonus(inventory, mob); + } + if (isSprinting()) + { + knockback += 1; + } + + if (dmg > 0 || magicBoost > 0) + { + bool bCrit = fallDistance > 0 && !onGround && !onLadder() && !isInWater() && !hasEffect(MobEffect::blindness) && riding == NULL && mob != NULL; + if (bCrit) + { + dmg += random->nextInt(dmg / 2 + 2); + } + dmg += magicBoost; + + // Ensure we put the entity on fire if we're hitting with a + // fire-enchanted weapon + bool setOnFireTemporatily = false; + int fireAspect = EnchantmentHelper::getFireAspect(dynamic_pointer_cast(shared_from_this())); + if (dynamic_pointer_cast(entity) && fireAspect > 0 && !entity->isOnFire()) + { + setOnFireTemporatily = true; + entity->setOnFire(1); + } + + DamageSource *damageSource = DamageSource::playerAttack(dynamic_pointer_cast(shared_from_this())); + bool wasHurt = entity->hurt(damageSource, dmg); + delete damageSource; + if (wasHurt) + { + if (knockback > 0) + { + entity->push(-Mth::sin(yRot * PI / 180) * knockback * .5f, 0.1, Mth::cos(yRot * PI / 180) * knockback * .5f); + xd *= 0.6; + zd *= 0.6; + setSprinting(false); + } + + if (bCrit) + { + crit(entity); + } + if (magicBoost > 0) + { + magicCrit(entity); + } + + if (dmg >= 18) + { + awardStat(GenericStats::overkill(),GenericStats::param_overkill(dmg)); + } + setLastHurtMob(entity); + + shared_ptr mob = dynamic_pointer_cast(entity); + if (mob) + { + ThornsEnchantment::doThornsAfterAttack(shared_from_this(), mob, random); + } + } + + shared_ptr item = getSelectedItem(); + if (item != NULL && dynamic_pointer_cast( entity ) != NULL) + { + item->hurtEnemy(dynamic_pointer_cast(entity), dynamic_pointer_cast( shared_from_this() ) ); + if (item->count <= 0) + { + removeSelectedItem(); + } + } + if (dynamic_pointer_cast( entity ) != NULL) + { + if (entity->isAlive()) + { + directAllTameWolvesOnTarget(dynamic_pointer_cast(entity), true); + } + // 4J Stu - Brought forward wasHurt check to Fix 66140 - Bug: Fire Aspect bypasses "Player v Player" being Disabled + if (fireAspect > 0 && wasHurt) + { + entity->setOnFire(fireAspect * 4); + } + else if (setOnFireTemporatily) + { + entity->clearFire(); + } + } + + causeFoodExhaustion(FoodConstants::EXHAUSTION_ATTACK); + } +} + +void Player::crit(shared_ptr entity) +{ +} + +void Player::magicCrit(shared_ptr entity) +{ +} + +void Player::respawn() +{ + deathFadeCounter=0; +} + + +void Player::animateRespawn(shared_ptr player, Level *level) +{ + + for (int i = 0; i < 45; i++) + { + float angle = i * PI * 4.0f / 25.0f; + float xo = Mth::cos(angle) * 0.7f; + float zo = Mth::sin(angle) * 0.7f; + + level->addParticle(eParticleType_netherportal, player->x + xo, player->y - player->heightOffset + 1.62f - i * .05f, player->z + zo, 0, 0, 0); + } + +} + +Slot *Player::getInventorySlot(int slotId) +{ + return NULL; +} + +void Player::remove() +{ + this->Mob::remove(); + inventoryMenu->removed( dynamic_pointer_cast( shared_from_this() ) ); + if (containerMenu != NULL) + { + containerMenu->removed( dynamic_pointer_cast( shared_from_this() ) ); + } +} + +bool Player::isInWall() +{ + return !m_isSleeping && this->Mob::isInWall(); +} + +bool Player::isLocalPlayer() +{ + return false; +} + +Player::BedSleepingResult Player::startSleepInBed(int x, int y, int z, bool bTestUse) +{ + if (!level->isClientSide || bTestUse) + { + if (isSleeping() || !isAlive()) + { + return OTHER_PROBLEM; + } + + if (!level->dimension->isNaturalDimension()) + { + // may not sleep in this dimension + return NOT_POSSIBLE_HERE; + } + + // 4J-PB - I'm going to move the position of these tests below + // The distance check should be before the day check, otherwise you can use the bed in daytime from far away + // and you'll get the message about only sleeping at night + + if (abs(this->x - x) > 3 || abs(this->y - y) > 2 || abs(this->z - z) > 3) + { + // too far away + return TOO_FAR_AWAY; + } + + if (!bTestUse) + { + // 4J-PB - We still want the tooltip for Sleep + + double hRange = 8; + double vRange = 5; + vector > *monsters = level->getEntitiesOfClass(typeid(Monster), AABB::newTemp(x - hRange, y - vRange, z - hRange, x + hRange, y + vRange, z + hRange)); + if (!monsters->empty()) + { + return NOT_SAFE; + } + delete monsters; + } + + // This causes a message to be displayed, so we do want to show the tooltip in test mode + if (!bTestUse && level->isDay()) + { + // may not sleep during day + return NOT_POSSIBLE_NOW; + } + } + + if(bTestUse) + { + // 4J-PB - we're just testing use, and we get here, then the bed can be used + return OK; + } + + // 4J Stu - You can use a bed from within a minecart, and this causes all sorts of problems. + ride(nullptr); + + setSize(0.2f, 0.2f); + heightOffset = .2f; + if (level->hasChunkAt(x, y, z)) + { + + + int data = level->getData(x, y, z); + int direction = BedTile::getDirection(data); + float xo = .5f, zo = .5f; + + switch (direction) + { + case Direction::SOUTH: + zo = .9f; + break; + case Direction::NORTH: + zo = .1f; + break; + case Direction::WEST: + xo = .1f; + break; + case Direction::EAST: + xo = .9f; + break; + } + setBedOffset(direction); + setPos(x + xo, y + 15.0f / 16.0f, z + zo); + } + else + { + setPos(x + .5f, y + 15.0f / 16.0f, z + .5f); + } + m_isSleeping = true; + sleepCounter = 0; + bedPosition = new Pos(x, y, z); + xd = zd = yd = 0; + + if (!level->isClientSide) + { + level->updateSleepingPlayerList(); + } + + return OK; +} + + +void Player::setBedOffset(int bedDirection) +{ + // place position on pillow and feet at bottom + bedOffsetX = 0; + bedOffsetZ = 0; + + switch (bedDirection) + { + case Direction::SOUTH: + bedOffsetZ = -1.8f; + break; + case Direction::NORTH: + bedOffsetZ = 1.8f; + break; + case Direction::WEST: + bedOffsetX = 1.8f; + break; + case Direction::EAST: + bedOffsetX = -1.8f; + break; + } +} + + +/** +* +* @param forcefulWakeUp +* If the player has been forced to wake up. When this happens, +* the client will skip the wake-up animation. For example, when +* the player is hurt or the bed is destroyed. +* @param updateLevelList +* If the level's sleeping player list needs to be updated. This +* is usually the case. +* @param saveRespawnPoint +* TODO +*/ +void Player::stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint) +{ + + setSize(0.6f, 1.8f); + setDefaultHeadHeight(); + + Pos *pos = bedPosition; + Pos *standUp = bedPosition; + if (pos != NULL && level->getTile(pos->x, pos->y, pos->z) == Tile::bed_Id) + { + BedTile::setOccupied(level, pos->x, pos->y, pos->z, false); + + standUp = BedTile::findStandUpPosition(level, pos->x, pos->y, pos->z, 0); + if (standUp == NULL) + { + standUp = new Pos(pos->x, pos->y + 1, pos->z); + } + setPos(standUp->x + .5f, standUp->y + heightOffset + .1f, standUp->z + .5f); + } + + m_isSleeping = false; + if (!level->isClientSide && updateLevelList) + { + level->updateSleepingPlayerList(); + } + if (forcefulWakeUp) + { + sleepCounter = 0; + } + else + { + sleepCounter = SLEEP_DURATION; + } + if (saveRespawnPoint) + { + setRespawnPosition(bedPosition); + } +} + + +bool Player::checkBed() +{ + return (level->getTile(bedPosition->x, bedPosition->y, bedPosition->z) == Tile::bed_Id); +} + + +Pos *Player::checkBedValidRespawnPosition(Level *level, Pos *pos) +{ + // make sure the chunks around the bed exist + ChunkSource *chunkSource = level->getChunkSource(); + chunkSource->create((pos->x - 3) >> 4, (pos->z - 3) >> 4); + chunkSource->create((pos->x + 3) >> 4, (pos->z - 3) >> 4); + chunkSource->create((pos->x - 3) >> 4, (pos->z + 3) >> 4); + chunkSource->create((pos->x + 3) >> 4, (pos->z + 3) >> 4); + + // make sure the bed is still standing + if (level->getTile(pos->x, pos->y, pos->z) != Tile::bed_Id) + { + return NULL; + } + // make sure the bed still has a stand-up position + Pos *standUp = BedTile::findStandUpPosition(level, pos->x, pos->y, pos->z, 0); + return standUp; +} + +float Player::getSleepRotation() +{ + if (bedPosition != NULL) + { + int data = level->getData(bedPosition->x, bedPosition->y, bedPosition->z); + int direction = BedTile::getDirection(data); + + switch (direction) + { + case Direction::SOUTH: + return 90; + case Direction::WEST: + return 0; + case Direction::NORTH: + return 270; + case Direction::EAST: + return 180; + } + } + return 0; +} + +bool Player::isSleeping() +{ + return m_isSleeping; +} + +bool Player::isSleepingLongEnough() +{ + return m_isSleeping && sleepCounter >= SLEEP_DURATION; +} + +int Player::getSleepTimer() +{ + return sleepCounter; +} + +// 4J-PB - added for death fade +int Player::getDeathFadeTimer() +{ + return deathFadeCounter; +} + +bool Player::getPlayerFlag(int flag) +{ + return (entityData->getByte(DATA_PLAYER_FLAGS_ID) & (1 << flag)) != 0; +} + +void Player::setPlayerFlag(int flag, bool value) +{ + byte currentValue = entityData->getByte(DATA_PLAYER_FLAGS_ID); + if (value) + { + entityData->set(DATA_PLAYER_FLAGS_ID, (byte) (currentValue | (1 << flag))); + } + else + { + entityData->set(DATA_PLAYER_FLAGS_ID, (byte) (currentValue & ~(1 << flag))); + } +} + + +/** +* This method is currently only relevant to client-side players. It will +* try to load the messageId from the language file and display it to the +* client. +*/ +void Player::displayClientMessage(int messageId) +{ + +} + +Pos *Player::getRespawnPosition() +{ + return respawnPosition; +} + +void Player::setRespawnPosition(Pos *respawnPosition) +{ + if (respawnPosition != NULL) + { + this->respawnPosition = new Pos(*respawnPosition); + } + else + { + this->respawnPosition = NULL; + } +} + +void Player::awardStat(Stat *stat, byteArray paramBlob) +{ + if (paramBlob.data != NULL) + { + delete [] paramBlob.data; + } +} + + +void Player::jumpFromGround() +{ + this->Mob::jumpFromGround(); + + // 4J Stu - This seems to have been missed from 1.7.3, but do we care? + //awardStat(Stats::jump, 1); + + if (isSprinting()) + { + causeFoodExhaustion(FoodConstants::EXHAUSTION_SPRINT_JUMP); + } + else + { + causeFoodExhaustion(FoodConstants::EXHAUSTION_JUMP); + } +} + + +void Player::travel(float xa, float ya) +{ + double preX = x, preY = y, preZ = z; + + if (abilities.flying && riding == NULL) + { + double ydo = this->yd; + float ofs = flyingSpeed; + flyingSpeed = abilities.getFlyingSpeed(); + this->Mob::travel(xa, ya); + this->yd = ydo * 0.6; + flyingSpeed = ofs; + } + else + { + this->Mob::travel(xa, ya); + } + + checkMovementStatistiscs(x - preX, y - preY, z - preZ); +} + + +void Player::checkMovementStatistiscs(double dx, double dy, double dz) +{ + + if (riding != NULL) + { + return; + } + if (isUnderLiquid(Material::water)) + { + int distance = (int) Math::round(sqrt(dx * dx + dy * dy + dz * dz) * 100.0f); + if (distance > 0) + { + //awardStat(Stats::diveOneCm, distance); + causeFoodExhaustion(FoodConstants::EXHAUSTION_SWIM * distance * .01f); + } + } + else if (isInWater()) + { + int horizontalDistance = (int) Math::round(sqrt(dx * dx + dz * dz) * 100.0f); + if (horizontalDistance > 0) + { + distanceSwim += horizontalDistance; + if( distanceSwim >= 100 ) + { + int newDistance = distanceSwim - (distanceSwim % 100); + distanceSwim -= newDistance; + awardStat( GenericStats::swimOneM(), GenericStats::param_swim(newDistance/100) ); + } + causeFoodExhaustion(FoodConstants::EXHAUSTION_SWIM * horizontalDistance * .01f); + } + } + else if (onLadder()) + { + if (dy > 0) + { + distanceClimb += (int) Math::round(dy * 100.0f); + if( distanceClimb >= 100 ) + { + int newDistance = distanceClimb - (distanceClimb % 100); + distanceClimb -= newDistance; + awardStat( GenericStats::climbOneM(), GenericStats::param_climb(newDistance/100) ); + } + } + } + else if (onGround) + { + int horizontalDistance = (int) Math::round(sqrt(dx * dx + dz * dz) * 100.0f); + if (horizontalDistance > 0) + { + distanceWalk += horizontalDistance; + if( distanceWalk >= 100 ) + { + int newDistance = distanceWalk - (distanceWalk % 100); + distanceWalk -= newDistance; + awardStat( GenericStats::walkOneM(), GenericStats::param_walk(newDistance/100) ); + } + if (isSprinting()) + { + causeFoodExhaustion(FoodConstants::EXHAUSTION_SPRINT * horizontalDistance * .01f); + } + else + { + causeFoodExhaustion(FoodConstants::EXHAUSTION_WALK * horizontalDistance * .01f); + } + } + } +} + + +void Player::checkRidingStatistiscs(double dx, double dy, double dz) +{ + if (riding != NULL) + { + int distance = (int) Math::round(sqrt(dx * dx + dy * dy + dz * dz) * 100.0f); + if (distance > 0) + { + if ( dynamic_pointer_cast( riding ) ) + { + distanceMinecart += distance; + if( distanceMinecart >= 100 ) + { + int newDistance = distanceMinecart - (distanceMinecart % 100); + distanceMinecart -= newDistance; + awardStat( GenericStats::minecartOneM(), GenericStats::param_minecart(newDistance/100) ); + } + + int dist = 0; + if (minecartAchievementPos == NULL) + { + minecartAchievementPos = new Pos(Mth::floor(x), Mth::floor(y), Mth::floor(z)); + } + // 4J-PB - changed this because our world isn't big enough to go 1000m + else + { + // 4-JEV, changed slightly to add extra parameters for event on durango. + int dist = minecartAchievementPos->dist(Mth::floor(x), Mth::floor(y), Mth::floor(z)); +#ifdef _XBOX_ONE + // 4J-PB - send the event to cause the progress bar to increase on XB1 + if (m_bAwardedOnARail==false) + { + if(dist < 500) + { + if((dist>0) && (dist%100==0)) + { + awardStat(GenericStats::onARail(), GenericStats::param_onARail(dist)); + } + } + else + { + awardStat(GenericStats::onARail(), GenericStats::param_onARail(dist)); + m_bAwardedOnARail=true; + } + } +#else + if ((m_bAwardedOnARail==false) && (dist >= 500)) + { + awardStat(GenericStats::onARail(), GenericStats::param_onARail(dist)); + m_bAwardedOnARail=true; + } +#endif + } + + } + else if (dynamic_pointer_cast( riding ) != NULL) + { + distanceBoat += distance; + if( distanceBoat >= 100 ) + { + int newDistance = distanceBoat - (distanceBoat % 100); + distanceBoat -= newDistance; + awardStat(GenericStats::boatOneM(), GenericStats::param_boat(newDistance/100) ); + } + } + else if (dynamic_pointer_cast( riding ) != NULL) + { + distancePig += distance; + if( distancePig >= 100 ) + { + int newDistance = distancePig - (distancePig % 100); + distancePig -= newDistance; + awardStat(GenericStats::pigOneM(), GenericStats::param_pig(newDistance/100) ); + } + } + } + } +} + + +void Player::causeFallDamage(float distance) +{ + if (abilities.mayfly) return; + + if (distance >= 2) + { + distanceFall += (int) Math::round(distance * 100.0); + if( distanceFall >= 100 ) + { + int newDistance = distanceFall - (distanceFall % 100); + distanceFall -= newDistance; + awardStat(GenericStats::fallOneM(), GenericStats::param_fall(newDistance/100) ); + } + } + this->Mob::causeFallDamage(distance); +} + + +void Player::killed(shared_ptr mob) +{ + // 4J-PB - added the lavaslime enemy - fix for #64007 - TU7: Code: Achievements: TCR#073: Killing Magma Cubes doesn't unlock "Monster Hunter" Achievement. + if( dynamic_pointer_cast( mob ) != NULL || mob->GetType() == eTYPE_GHAST || mob->GetType() == eTYPE_SLIME || mob->GetType() == eTYPE_LAVASLIME || mob->GetType() == eTYPE_ENDERDRAGON) + { + awardStat(GenericStats::killEnemy(), GenericStats::param_noArgs()); + + switch( mob->GetType() ) + { + case eTYPE_CREEPER: + awardStat(GenericStats::killsCreeper(), GenericStats::param_noArgs()); + break; + case eTYPE_SKELETON: + if( mob->isRiding() && mob->riding->GetType() == eTYPE_SPIDER ) + awardStat(GenericStats::killsSpiderJockey(), GenericStats::param_noArgs()); + else + awardStat(GenericStats::killsSkeleton(), GenericStats::param_noArgs()); + break; + case eTYPE_SPIDER: + if( mob->rider.lock() != NULL && mob->rider.lock()->GetType() == eTYPE_SKELETON ) + awardStat(GenericStats::killsSpiderJockey(), GenericStats::param_noArgs()); + else + awardStat(GenericStats::killsSpider(), GenericStats::param_noArgs()); + break; + case eTYPE_ZOMBIE: + awardStat(GenericStats::killsZombie(), GenericStats::param_noArgs()); + break; + case eTYPE_PIGZOMBIE: + if( level->dimension->id == 0 ) + awardStat(GenericStats::killsZombiePigman(), GenericStats::param_noArgs()); + else + awardStat(GenericStats::killsNetherZombiePigman(), GenericStats::param_noArgs()); + break; + case eTYPE_GHAST: + awardStat(GenericStats::killsGhast(), GenericStats::param_noArgs()); + break; + case eTYPE_SLIME: + awardStat(GenericStats::killsSlime(), GenericStats::param_noArgs()); + break; + case eTYPE_ENDERDRAGON: + awardStat(GenericStats::killsEnderdragon(), GenericStats::param_noArgs()); + break; + } + } + else if( mob->GetType() == eTYPE_COW ) + { + awardStat(GenericStats::killCow(), GenericStats::param_noArgs()); + } +} + +Icon *Player::getItemInHandIcon(shared_ptr item, int layer) +{ + Icon *icon = Mob::getItemInHandIcon(item, layer); + if (item->id == Item::fishingRod->id && fishing != NULL) + { + icon = Item::fishingRod->getEmptyIcon(); + } + else if (item->getItem()->hasMultipleSpriteLayers()) + { + return item->getItem()->getLayerIcon(item->getAuxValue(), layer); + } + else if (useItem != NULL && item->id == Item::bow_Id) + { + int ticksHeld = (item->getUseDuration() - useItemDuration); + if (ticksHeld >= BowItem::MAX_DRAW_DURATION - 2) + { + return Item::bow->getDrawnIcon(2); + } + if (ticksHeld > (2 * BowItem::MAX_DRAW_DURATION) / 3) + { + return Item::bow->getDrawnIcon(1); + } + if (ticksHeld > 0) + { + return Item::bow->getDrawnIcon(0); + } + } + return icon; +} + +shared_ptr Player::getArmor(int pos) +{ + return inventory->getArmor(pos); +} + +void Player::handleInsidePortal() +{ + if (changingDimensionDelay > 0) + { + changingDimensionDelay = 10; + return; + } + + isInsidePortal = true; +} + +void Player::increaseXp(int i) +{ + // Update xp calculations from 1.3 + score += i; + int max = INT_MAX - totalExperience; + if (i > max) + { + i = max; + } + experienceProgress += (float) i / getXpNeededForNextLevel(); + totalExperience += i; + while (experienceProgress >= 1) + { + experienceProgress = (experienceProgress - 1) * getXpNeededForNextLevel(); + levelUp(); + experienceProgress /= getXpNeededForNextLevel(); + } +} + +void Player::withdrawExperienceLevels(int amount) +{ + experienceLevel -= amount; + if (experienceLevel < 0) + { + experienceLevel = 0; + } +} + +int Player::getXpNeededForNextLevel() +{ + // Update xp calculations from 1.3 + if (experienceLevel >= 30) + { + return 17 + 15 * 3 + (experienceLevel - 30) * 7; + } + if (experienceLevel >= 15) + { + return 17 + (experienceLevel - 15) * 3; + } + return 17; +} + +void Player::levelUp() +{ + experienceLevel++; +} + +/** +* This method adds on to the player's exhaustion, which may decrease the +* player's food level. +* +* @param amount +* Amount of exhaustion to add, between 0 and 20 (setting it to +* 20 will guarantee that at least 1, and at most 4, food points +* are deducted). See FoodConstants for cost suggestions. +*/ +void Player::causeFoodExhaustion(float amount) +{ + if( isAllowedToIgnoreExhaustion() || ( isAllowedToFly() && abilities.flying) ) return; + if (abilities.invulnerable || hasInvulnerablePrivilege() ) return; + + // 4J Stu - Added 1.8.2 bug fix (TU6) - If players cannot eat, then their food bar should not decrease due to exhaustion + if(app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild) != 0) return; + + if (!level->isClientSide) + { + foodData.addExhaustion(amount); + } +} + +FoodData *Player::getFoodData() +{ + return &foodData; +} + +bool Player::canEat(bool magicalItem) +{ + return (magicalItem || foodData.needsFood()) && !abilities.invulnerable && !hasInvulnerablePrivilege(); +} + +bool Player::isHurt() +{ + return getHealth() > 0 && getHealth() < getMaxHealth(); +} + +void Player::startUsingItem(shared_ptr instance, int duration) +{ + if (instance == useItem) return; + useItem = instance; + useItemDuration = duration; + if (!level->isClientSide) + { + setUsingItemFlag(true); + } + + // 4J-JEV, hook for ItemUsed event, and ironbelly achievement. + awardStat(GenericStats::itemsUsed(instance->getItem()->id), + GenericStats::param_itemsUsed(dynamic_pointer_cast(shared_from_this()),instance)); + +#if (!defined _DURANGO) && (defined _EXTENDED_ACHIEVEMENTS) + if ( (instance->getItem()->id == Item::rotten_flesh_Id) && (getFoodData()->getFoodLevel() == 0) ) + awardStat(GenericStats::ironBelly(), GenericStats::param_ironBelly()); +#endif +} + +bool Player::mayBuild(int x, int y, int z) +{ + return abilities.mayBuild; +} + +int Player::getExperienceReward(shared_ptr killedBy) +{ + int reward = experienceLevel * 7; + if (reward > 100) + { + return 100; + } + return reward; +} + +bool Player::isAlwaysExperienceDropper() +{ + // players always drop experience + return true; +} + +wstring Player::getAName() +{ + return name; +} + +void Player::changeDimension(int i) +{ +} + +void Player::restoreFrom(shared_ptr oldPlayer, bool restoreAll) +{ + if(restoreAll) + { + inventory->replaceWith(oldPlayer->inventory); + + health = oldPlayer->health; + foodData = oldPlayer->foodData; + + experienceLevel = oldPlayer->experienceLevel; + totalExperience = oldPlayer->totalExperience; + experienceProgress = oldPlayer->experienceProgress; + + score = oldPlayer->score; + + for(AUTO_VAR(it, oldPlayer->activeEffects.begin()); it != oldPlayer->activeEffects.end(); ++it) + { + MobEffectInstance *instance = it->second; + addEffectNoUpdate( instance ); + } + oldPlayer->activeEffects.clear(); + } + enderChestInventory = oldPlayer->enderChestInventory; +} + +bool Player::makeStepSound() +{ + return !abilities.flying; +} + +void Player::onUpdateAbilities() +{ +} + +void Player::setGameMode(GameType *mode) +{ +} + +wstring Player::getName() +{ + return name; +} + +wstring Player::getDisplayName() +{ + return displayName; +} + +//Language getLanguage() { return Language.getInstance(); } +//String localize(String key, Object... args) { return getLanguage().getElement(key, args); } + +shared_ptr Player::getEnderChestInventory() +{ + return enderChestInventory; +} + +shared_ptr Player::getCarriedItem() +{ + return inventory->getSelected(); +} + +bool Player::isInvisibleTo(shared_ptr player) +{ + return isInvisible(); +} + +int Player::getTexture() +{ + switch(m_skinIndex) + { + case eDefaultSkins_Skin0: + return TN_MOB_CHAR; // 4J - was L"/mob/char.png"; + case eDefaultSkins_Skin1: + return TN_MOB_CHAR1; // 4J - was L"/mob/char1.png"; + case eDefaultSkins_Skin2: + return TN_MOB_CHAR2; // 4J - was L"/mob/char2.png"; + case eDefaultSkins_Skin3: + return TN_MOB_CHAR3; // 4J - was L"/mob/char3.png"; + case eDefaultSkins_Skin4: + return TN_MOB_CHAR4; // 4J - was L"/mob/char4.png"; + case eDefaultSkins_Skin5: + return TN_MOB_CHAR5; // 4J - was L"/mob/char5.png"; + case eDefaultSkins_Skin6: + return TN_MOB_CHAR6; // 4J - was L"/mob/char6.png"; + case eDefaultSkins_Skin7: + return TN_MOB_CHAR7; // 4J - was L"/mob/char7.png"; + + default: + return TN_MOB_CHAR; // 4J - was L"/mob/char.png"; + } +} + +int Player::hash_fnct(const shared_ptr k) +{ + // TODO 4J Stu - Should we just be using the pointers and hashing them? +#ifdef __PS3__ + return (int)boost::hash_value( k->name ); // 4J Stu - Names are completely unique? +#else + return (int)std::hash{}( k->name ); // 4J Stu - Names are completely unique? +#endif //__PS3__ +} + +bool Player::eq_test(const shared_ptr x, const shared_ptr y) +{ + // TODO 4J Stu - Should we just be using the pointers and comparing them for equality? + return x->name.compare( y->name ) == 0; // 4J Stu - Names are completely unique? +} + + +unsigned int Player::getPlayerGamePrivilege(EPlayerGamePrivileges privilege) +{ + return Player::getPlayerGamePrivilege(m_uiGamePrivileges,privilege); +} + +unsigned int Player::getPlayerGamePrivilege(unsigned int uiGamePrivileges, EPlayerGamePrivileges privilege) +{ + if( privilege == ePlayerGamePrivilege_All ) + { + return uiGamePrivileges; + } + else if (privilege < ePlayerGamePrivilege_MAX ) + { + return uiGamePrivileges&(1<id) + { + case Tile::door_wood_Id: + case Tile::button_stone_Id: + case Tile::button_wood_Id: + case Tile::lever_Id: + case Tile::fenceGate_Id: + case Tile::trapdoor_Id: + allowed = true; + break; + } + } + + if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseContainers) != 0) + { + switch(tile->id) + { + case Tile::chest_Id: + case Tile::furnace_Id: + case Tile::furnace_lit_Id: + case Tile::dispenser_Id: + case Tile::brewingStand_Id: + case Tile::enchantTable_Id: + case Tile::workBench_Id: + case Tile::anvil_Id: + case Tile::enderChest_Id: + allowed = true; + break; + } + } + + if(!allowed && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild) == 0) + { + switch(tile->id) + { + case Tile::door_wood_Id: + case Tile::button_stone_Id: + case Tile::button_wood_Id: + case Tile::lever_Id: + case Tile::fenceGate_Id: + case Tile::trapdoor_Id: + case Tile::chest_Id: + case Tile::furnace_Id: + case Tile::furnace_lit_Id: + case Tile::dispenser_Id: + case Tile::brewingStand_Id: + case Tile::enchantTable_Id: + case Tile::workBench_Id: + case Tile::anvil_Id: + case Tile::enderChest_Id: + allowed = false; + break; + default: + allowed = true; + break; + } + } + } + + return allowed; +} + +bool Player::isAllowedToUse(shared_ptr item) +{ + bool allowed = true; + if(item != NULL && app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0) + { + if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild) != 0) + { + allowed = false; + } + + // 4J Stu - TU8 Players should always be able to eat food items, even if the build option is turned of + switch(item->id) + { + // food + case Item::mushroomStew_Id: + case Item::apple_Id: + case Item::bread_Id: + case Item::porkChop_raw_Id: + case Item::porkChop_cooked_Id: + case Item::apple_gold_Id: + case Item::fish_raw_Id: + case Item::fish_cooked_Id: + case Item::cookie_Id: + case Item::beef_cooked_Id: + case Item::beef_raw_Id: + case Item::chicken_cooked_Id: + case Item::chicken_raw_Id: + case Item::melon_Id: + case Item::rotten_flesh_Id: + // bow + case Item::bow_Id: + case Item::sword_diamond_Id: + case Item::sword_gold_Id: + case Item::sword_iron_Id: + case Item::sword_stone_Id: + case Item::sword_wood_Id: + allowed = true; + break; + } + } + + return allowed; +} + +bool Player::isAllowedToInteract(shared_ptr target) +{ + bool allowed = true; + if(app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0) + { + if (target->GetType() == eTYPE_MINECART) + { + if (getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanUseContainers) == 0) + { + shared_ptr minecart = dynamic_pointer_cast( target ); + if (minecart->type == Minecart::CHEST) + allowed = false; + } + + } + else + { + if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotBuild) != 0) + { + allowed = false; + } + + if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotMine) != 0) + { + allowed = false; + } + } + } + + return allowed; +} + +bool Player::isAllowedToMine() +{ + bool allowed = true; + if(app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0) + { + if(getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotMine) != 0) + { + allowed = false; + } + } + return allowed; +} + +bool Player::isAllowedToAttackPlayers() +{ + bool allowed = true; + if( hasInvisiblePrivilege() || ((app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0) && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotAttackPlayers)) ) + { + allowed = false; + } + return allowed; +} + +bool Player::isAllowedToAttackAnimals() +{ + bool allowed = true; + if( (app.GetGameHostOption(eGameHostOption_TrustPlayers) == 0) && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CannotAttackAnimals) ) + { + allowed = false; + } + return allowed; +} + +bool Player::isAllowedToHurtEntity(shared_ptr target) +{ + bool allowed = true; + + if(!isAllowedToMine()) + { + switch(target->GetType()) + { + case eTYPE_HANGING_ENTITY: + case eTYPE_PAINTING: + case eTYPE_ITEM_FRAME: + + // 4J-JEV: Fix for #88212, + // Untrusted players shouldn't be able to damage minecarts or boats. + case eTYPE_BOAT: + case eTYPE_MINECART: + + allowed = false; + break; + }; + } + return allowed; +} + +bool Player::isAllowedToFly() +{ + bool allowed = false; + if(app.GetGameHostOption(eGameHostOption_HostCanFly) != 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanFly) != 0) + { + allowed = true; + } + return allowed; +} + +bool Player::isAllowedToIgnoreExhaustion() +{ + bool allowed = false; + if( (app.GetGameHostOption(eGameHostOption_HostCanChangeHunger) != 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_ClassicHunger) != 0) || + (isAllowedToFly() && abilities.flying) ) + { + allowed = true; + } + return allowed; +} + +bool Player::isAllowedToTeleport() +{ + bool allowed = false; + if( isModerator() && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_CanTeleport) != 0) + { + allowed = true; + } + return allowed; +} + +bool Player::hasInvisiblePrivilege() +{ + bool enabled = false; + if(app.GetGameHostOption(eGameHostOption_HostCanBeInvisible) != 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_Invisible) != 0) + { + enabled = true; + } + return enabled; +} + +bool Player::hasInvulnerablePrivilege() +{ + bool enabled = false; + if(app.GetGameHostOption(eGameHostOption_HostCanBeInvisible) != 0 && getPlayerGamePrivilege(Player::ePlayerGamePrivilege_Invulnerable) != 0) + { + enabled = true; + } + return enabled; +} + +bool Player::isModerator() +{ + return getPlayerGamePrivilege(Player::ePlayerGamePrivilege_Op) != 0; +} + +void Player::enableAllPlayerPrivileges(unsigned int &uigamePrivileges, bool enable) +{ + Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CannotMine, enable?0:1); + Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CannotBuild, enable?0:1); + Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CannotAttackPlayers, enable?0:1); + Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CannotAttackAnimals, enable?0:1); + Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CanUseDoorsAndSwitches, enable?1:0); + Player::setPlayerGamePrivilege(uigamePrivileges, Player::ePlayerGamePrivilege_CanUseContainers, enable?1:0); +} + +void Player::enableAllPlayerPrivileges(bool enable) +{ + Player::enableAllPlayerPrivileges(m_uiGamePrivileges,enable); +} + +bool Player::canCreateParticles() +{ + return !hasInvisiblePrivilege(); +} + +vector *Player::GetAdditionalModelParts() +{ + if(m_ppAdditionalModelParts==NULL && !m_bCheckedForModelParts) + { + bool hasCustomTexture = !customTextureUrl.empty(); + bool customTextureIsDefaultSkin = customTextureUrl.substr(0,3).compare(L"def") == 0; + + // see if we can find the parts + m_ppAdditionalModelParts=app.GetAdditionalModelParts(m_dwSkinId); + + // If it's a default texture (which has no parts), we have the parts, or we already have the texture (in which case we should have parts if there are any) then we are done + if(!hasCustomTexture || customTextureIsDefaultSkin || m_ppAdditionalModelParts != NULL || app.IsFileInMemoryTextures(customTextureUrl)) + { + m_bCheckedForModelParts=true; + } + if(m_ppAdditionalModelParts == NULL && !m_bCheckedDLCForModelParts) + { + m_bCheckedDLCForModelParts = true; + + // we don't have the data from the dlc skin yet + app.DebugPrintf("m_bCheckedForModelParts Couldn't get model parts for skin %X\n",m_dwSkinId); + + // do we have it from the DLC pack? + DLCSkinFile *pDLCSkinFile = app.m_dlcManager.getSkinFile(this->customTextureUrl); + + if(pDLCSkinFile!=NULL) + { + DWORD dwBoxC=pDLCSkinFile->getAdditionalBoxesCount(); + if(dwBoxC!=0) + { + app.DebugPrintf("m_bCheckedForModelParts Got model parts from DLCskin for skin %X\n",m_dwSkinId); + m_ppAdditionalModelParts=app.SetAdditionalSkinBoxes(m_dwSkinId,pDLCSkinFile->getAdditionalBoxes()); + } + + app.SetAnimOverrideBitmask(pDLCSkinFile->getSkinID(),pDLCSkinFile->getAnimOverrideBitmask()); + + m_bCheckedForModelParts=true; + } + } + + if(m_bCheckedForModelParts) setAnimOverrideBitmask(getSkinAnimOverrideBitmask(m_dwSkinId)); + } + return m_ppAdditionalModelParts; +} + +void Player::SetAdditionalModelParts(vector *ppAdditionalModelParts) +{ + m_ppAdditionalModelParts=ppAdditionalModelParts; +} + +#if defined(__PS3__) || defined(__ORBIS__) + +Player::ePlayerNameValidState Player::GetPlayerNameValidState(void) +{ + return m_ePlayerNameValidState; +} + +void Player::SetPlayerNameValidState(bool bState) +{ + if(bState) + { + m_ePlayerNameValidState=ePlayerNameValid_True; + } + else + { + m_ePlayerNameValidState=ePlayerNameValid_False; + + } +} +#endif diff --git a/Minecraft.World/Player.h b/Minecraft.World/Player.h new file mode 100644 index 00000000..c323bf05 --- /dev/null +++ b/Minecraft.World/Player.h @@ -0,0 +1,551 @@ +#pragma once +using namespace std; + +#include "Mob.h" +#include "Definitions.h" +#include "Abilities.h" +#include "FoodData.h" +#include "PlayerEnderChestContainer.h" +#include "CommandSender.h" + +class AbstractContainerMenu; +class Stats; +class FishingHook; + +class ItemEntity; +class Slot; +class Pos; + +class FurnaceTileEntity; +class DispenserTileEntity; +class SignTileEntity; +class BrewingStandTileEntity; +class Inventory; +class Container; +class FoodData; +class DamageSource; +class Merchant; +class PlayerEnderChestContainer; +class GameType; + +class Player : public Mob, public CommandSender +{ +public: + static const int MAX_NAME_LENGTH = 16 + 4; + static const int MAX_HEALTH = 20; + static const int SWING_DURATION = 6; + static const int SLEEP_DURATION = 100; + static const int WAKE_UP_DURATION = 10; + + static const int CHAT_VISIBILITY_FULL = 0; + static const int CHAT_VISIBILITY_SYSTEM = 1; + static const int CHAT_VISIBILITY_HIDDEN = 2; + + // 4J-PB - added for a red death fade in the gui + static const int DEATHFADE_DURATION = 21; +private: + static const int FLY_ACHIEVEMENT_SPEED = 25; + + static const int DATA_PLAYER_FLAGS_ID = 16; + static const int DATA_PLAYER_RUNNING_ID = 17; + +public: + shared_ptr inventory; + +private: + shared_ptr enderChestInventory; + +public: + AbstractContainerMenu *inventoryMenu; + AbstractContainerMenu *containerMenu; + +protected: + FoodData foodData; + int jumpTriggerTime; + +public: + BYTE userType; + int score; + float oBob, bob; + bool swinging; + int swingTime; + + wstring name; + int dimension; + int takeXpDelay; + + // 4J-PB - track custom skin + unsigned int m_uiPlayerCurrentSkin; + void ChangePlayerSkin(); + + // 4J-PB - not needed, since cutomtextureurl2 is the same thing wstring cloakTexture; + + double xCloakO, yCloakO, zCloakO; + double xCloak, yCloak, zCloak; + + // 4J-HEG - store display name, added for Xbox One + wstring displayName; + +protected: + // player sleeping in bed? + bool m_isSleeping; + +public: + Pos *bedPosition; + +private: + int sleepCounter; // animation timer + int deathFadeCounter; // animation timer + +public: + float bedOffsetX, bedOffsetY, bedOffsetZ; + Stats *stats; + +private: + Pos *respawnPosition; + Pos *minecartAchievementPos; + + //4J Gordon: These are in cms, every time they go > 1m they are entered into the stats + int distanceWalk, distanceSwim, distanceFall, distanceClimb, distanceMinecart, distanceBoat, distancePig; + +public: + int changingDimensionDelay; + +protected: + bool isInsidePortal; + +public: + float portalTime, oPortalTime; + + Abilities abilities; + + int experienceLevel, totalExperience; + float experienceProgress; + + // 4J Stu - Made protected so that we can access it from MultiPlayerLocalPlayer +protected: + shared_ptr useItem; + int useItemDuration; + +protected: + float defaultWalkSpeed; + float defaultFlySpeed; + +public: + + eINSTANCEOF GetType() { return eTYPE_PLAYER; } + + // 4J Added to default init + void _init(); + + Player(Level *level); + virtual ~Player(); + + virtual int getMaxHealth(); +protected: + virtual void defineSynchedData(); + +public: + shared_ptr getUseItem(); + int getUseItemDuration(); + bool isUsingItem(); int getTicksUsingItem(); + void releaseUsingItem(); + void stopUsingItem(); + virtual bool isBlocking(); + + virtual void tick(); + +protected: + void spawnEatParticles(shared_ptr useItem, int count); + virtual void completeUsingItem(); + +public: + virtual void handleEntityEvent(byte id); + +protected: + bool isImmobile(); + virtual void closeContainer(); + +public: + virtual void ride(shared_ptr e); + void prepareCustomTextures(); + virtual void rideTick(); + virtual void resetPos(); + +private: + int getCurrentSwingDuration(); + +protected: + virtual void serverAiStep(); + +public: + virtual void aiStep(); + +private: + virtual void touch(shared_ptr entity); + +public: + //bool addResource(int resource); // 4J - Removed 1.0.1 + int getScore(); + virtual void die(DamageSource *source); + void awardKillScore(shared_ptr victim, int score); + +protected: + virtual int decreaseAirSupply(int currentSupply); + +public: + virtual bool isShootable(); + bool isCreativeModeAllowed(); + virtual shared_ptr drop(); + shared_ptr drop(shared_ptr item); + shared_ptr drop(shared_ptr item, bool randomly); + +protected: + virtual void reallyDrop(shared_ptr thrownItem); + +public: + float getDestroySpeed(Tile *tile); + bool canDestroy(Tile *tile); + virtual void readAdditionalSaveData(CompoundTag *entityTag); + virtual void addAdditonalSaveData(CompoundTag *entityTag); + static Pos *getRespawnPosition(Level *level, CompoundTag *entityTag); + virtual bool openContainer(shared_ptr container); // 4J - added bool return + virtual bool startEnchanting(int x, int y, int z); // 4J - added bool return + virtual bool startRepairing(int x, int y, int z); // 4J - added bool return + virtual bool startCrafting(int x, int y, int z); // 4J - added boo return + virtual void take(shared_ptr e, int orgCount); + virtual float getHeadHeight(); + + // 4J-PB - added to keep the code happy with the change to make the third person view per player + virtual int ThirdPersonView() {return 0;} + virtual void SetThirdPersonView(int val) {} + +protected: + virtual void setDefaultHeadHeight(); + +public: + shared_ptr fishing; + + virtual bool hurt(DamageSource *source, int dmg); + +protected: + virtual int getDamageAfterMagicAbsorb(DamageSource *damageSource, int damage); + virtual bool isPlayerVersusPlayer(); + void directAllTameWolvesOnTarget(shared_ptr target, bool skipSitting); + virtual void hurtArmor(int damage); + +public: + virtual int getArmorValue(); + float getArmorCoverPercentage(); + +protected: + virtual void actuallyHurt(DamageSource *source, int dmg); + +public: + using Entity::interact; + + virtual bool openFurnace(shared_ptr container); // 4J - added bool return + virtual bool openTrap(shared_ptr container); // 4J - added bool return + virtual void openTextEdit(shared_ptr sign); + virtual bool openBrewingStand(shared_ptr brewingStand); // 4J - added bool return + virtual bool openTrading(shared_ptr traderTarget); // 4J - added bool return + virtual void openItemInstanceGui(shared_ptr itemInstance); + virtual bool interact(shared_ptr entity); + virtual shared_ptr getSelectedItem(); + void removeSelectedItem(); + virtual double getRidingHeight(); + virtual void swing(); + virtual void attack(shared_ptr entity); + virtual void crit(shared_ptr entity); + virtual void magicCrit(shared_ptr entity); + virtual void respawn(); + +protected: + static void animateRespawn(shared_ptr player, Level *level); + +public: + Slot *getInventorySlot(int slotId); + virtual void remove(); + virtual bool isInWall(); + virtual bool isLocalPlayer(); + + enum BedSleepingResult + { + OK, NOT_POSSIBLE_HERE, NOT_POSSIBLE_NOW, TOO_FAR_AWAY, OTHER_PROBLEM, NOT_SAFE + }; + + virtual BedSleepingResult startSleepInBed(int x, int y, int z, bool bTestUse = false); + +private: + void setBedOffset(int bedDirection); + +public: + /** + * + * @param forcefulWakeUp + * If the player has been forced to wake up. When this happens, + * the client will skip the wake-up animation. For example, when + * the player is hurt or the bed is destroyed. + * @param updateLevelList + * If the level's sleeping player list needs to be updated. This + * is usually the case. + * @param saveRespawnPoint + * TODO + */ + virtual void stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint); + +private: + bool checkBed(); + +public: + static Pos *checkBedValidRespawnPosition(Level *level, Pos *pos); + float getSleepRotation(); + bool isSleeping(); + bool isSleepingLongEnough(); + int getSleepTimer(); + int getDeathFadeTimer(); + +protected: + bool getPlayerFlag(int flag); + void setPlayerFlag(int flag, bool value); + +public: + /** + * This method is currently only relevant to client-side players. It will + * try to load the messageId from the language file and display it to the + * client. + */ + virtual void displayClientMessage(int messageId); + Pos *getRespawnPosition(); + void setRespawnPosition(Pos *respawnPosition); + virtual void awardStat(Stat *stat, byteArray param); + +protected: + void jumpFromGround(); + +public: + void travel(float xa, float ya); + void checkMovementStatistiscs(double dx, double dy, double dz); + +private: + void checkRidingStatistiscs(double dx, double dy, double dz); + + bool m_bAwardedOnARail; + +protected: + virtual void causeFallDamage(float distance); + +public: + virtual void killed(shared_ptr mob); + virtual Icon *getItemInHandIcon(shared_ptr item, int layer); + virtual shared_ptr getArmor(int pos); + virtual void handleInsidePortal(); + + void increaseXp(int i); + virtual void withdrawExperienceLevels(int amount); + int getXpNeededForNextLevel(); + +private: + void levelUp(); + +public: + void causeFoodExhaustion(float amount); + FoodData *getFoodData(); + bool canEat(bool magicalItem); + bool isHurt(); + virtual void startUsingItem(shared_ptr instance, int duration); + bool mayBuild(int x, int y, int z); + +protected: + virtual int getExperienceReward(shared_ptr killedBy); + virtual bool isAlwaysExperienceDropper(); + +public: + virtual wstring getAName(); + + virtual void changeDimension(int i); + virtual void restoreFrom(shared_ptr oldPlayer, bool restoreAll); + +protected: + bool makeStepSound(); + +public: + void onUpdateAbilities(); + void setGameMode(GameType *mode); + wstring getName(); + wstring getDisplayName(); // 4J added + + //Language getLanguage() { return Language.getInstance(); } + //String localize(String key, Object... args) { return getLanguage().getElement(key, args); } + + shared_ptr getEnderChestInventory(); + +public: + virtual shared_ptr getCarriedItem(); + + virtual bool isInvisibleTo(shared_ptr player); + + static int hash_fnct(const shared_ptr k); + static bool eq_test(const shared_ptr x, const shared_ptr y); + + // 4J Stu - Added to allow callback to tutorial to stay within Minecraft.Client + // Overidden in LocalPlayer + virtual void onCrafted(shared_ptr item) {} + + // 4J Overriding this so that we can have some different default skins + virtual int getTexture(); // 4J changed from wstring to int + void setPlayerDefaultSkin(EDefaultSkins skin); + EDefaultSkins getPlayerDefaultSkin() { return m_skinIndex; } + virtual void setCustomSkin(DWORD skinId); + DWORD getCustomSkin() {return m_dwSkinId; } + virtual void setCustomCape(DWORD capeId); + DWORD getCustomCape() {return m_dwCapeId; } + + static DWORD getCapeIdFromPath(const wstring &cape); + static wstring getCapePathFromId(DWORD capeId); + static unsigned int getSkinAnimOverrideBitmask(DWORD skinId); + + // 4J Added + void setXuid(PlayerUID xuid); + PlayerUID getXuid() { return m_xuid; } + void setOnlineXuid(PlayerUID xuid) { m_OnlineXuid = xuid; } + PlayerUID getOnlineXuid() { return m_OnlineXuid; } + void setUUID(const wstring &UUID) { m_UUID = UUID; } + wstring getUUID() { return m_UUID; } + + void setPlayerIndex(DWORD dwIndex) { m_playerIndex = dwIndex; } + DWORD getPlayerIndex() { return m_playerIndex; } + + void setIsGuest(bool bVal) { m_bIsGuest = bVal; } + bool isGuest() { return m_bIsGuest; } + + void setShowOnMaps(bool bVal) { m_bShownOnMaps = bVal; } + bool canShowOnMaps() { return m_bShownOnMaps && !getPlayerGamePrivilege(ePlayerGamePrivilege_Invisible); } + + virtual void sendMessage(const wstring& message, ChatPacket::EChatPacketMessage type = ChatPacket::e_ChatCustom, int customData = -1, const wstring& additionalMessage = L"") { } +private: + PlayerUID m_xuid; + PlayerUID m_OnlineXuid; + +protected: + wstring m_UUID; // 4J Added + + bool m_bShownOnMaps; + + bool m_bIsGuest; + +private: + EDefaultSkins m_skinIndex; + DWORD m_dwSkinId,m_dwCapeId; + + // 4J Added - Used to show which colour the player is on the map/behind their name + DWORD m_playerIndex; + + // 4J-PB - to track debug options from the server player + unsigned int m_uiDebugOptions; + +public: + void SetDebugOptions(unsigned int uiVal) { m_uiDebugOptions=uiVal;} + unsigned int GetDebugOptions(void) { return m_uiDebugOptions;} + + void StopSleeping() {} + +public: + // If you add things here, you should also add a message to ClientConnection::displayPrivilegeChanges to alert players to changes + enum EPlayerGamePrivileges + { + ePlayerGamePrivilege_CannotMine = 0, // Only checked if trust system is on + ePlayerGamePrivilege_CannotBuild, // Only checked if trust system is on + ePlayerGamePrivilege_CannotAttackMobs, // Only checked if trust system is on + ePlayerGamePrivilege_CannotAttackPlayers, //Only checked if trust system is on + ePlayerGamePrivilege_Op, + ePlayerGamePrivilege_CanFly, + ePlayerGamePrivilege_ClassicHunger, + ePlayerGamePrivilege_Invisible, + ePlayerGamePrivilege_Invulnerable, + + ePlayerGamePrivilege_CreativeMode, // Used only to transfer across network, should never be used to determine if a player is in creative mode + + ePlayerGamePrivilege_CannotAttackAnimals, // Only checked if trust system is on + ePlayerGamePrivilege_CanUseDoorsAndSwitches, // Only checked if trust system is on + ePlayerGamePrivilege_CanUseContainers, // Only checked if trust system is on + + ePlayerGamePrivilege_CanToggleInvisible, + ePlayerGamePrivilege_CanToggleFly, + ePlayerGamePrivilege_CanToggleClassicHunger, + ePlayerGamePrivilege_CanTeleport, + + // Currently enum is used to bitshift into an unsigned int + ePlayerGamePrivilege_MAX = 32, + ePlayerGamePrivilege_All = 33, + ePlayerGamePrivilege_HOST, + }; +private: + // 4J Added - Used to track what actions players have been allowed to perform by the host + unsigned int m_uiGamePrivileges; + + unsigned int getPlayerGamePrivilege(EPlayerGamePrivileges privilege); +public: + unsigned int getAllPlayerGamePrivileges() { return getPlayerGamePrivilege(ePlayerGamePrivilege_All); } + + static unsigned int getPlayerGamePrivilege(unsigned int uiGamePrivileges, EPlayerGamePrivileges privilege); + void setPlayerGamePrivilege(EPlayerGamePrivileges privilege, unsigned int value); + static void setPlayerGamePrivilege(unsigned int &uiGamePrivileges, EPlayerGamePrivileges privilege, unsigned int value); + + bool isAllowedToUse(Tile *tile); + bool isAllowedToUse(shared_ptr item); + bool isAllowedToInteract(shared_ptr target); + bool isAllowedToMine(); + bool isAllowedToAttackPlayers(); + bool isAllowedToAttackAnimals(); + bool isAllowedToHurtEntity(shared_ptr target); + bool isAllowedToFly(); + bool isAllowedToIgnoreExhaustion(); + bool isAllowedToTeleport(); + bool hasInvisiblePrivilege(); + bool hasInvulnerablePrivilege(); + bool isModerator(); + + static void enableAllPlayerPrivileges(unsigned int &uigamePrivileges, bool enable); + void enableAllPlayerPrivileges(bool enable); + + virtual bool canCreateParticles(); + +public: + // 4J Stu - Added hooks for the game rules + virtual void handleCollectItem(shared_ptr item) {} + + vector *GetAdditionalModelParts(); + void SetAdditionalModelParts(vector *ppAdditionalModelParts); + +#if defined(__PS3__) || defined(__ORBIS__) + enum ePlayerNameValidState + { + ePlayerNameValid_NotSet=0, + ePlayerNameValid_True, + ePlayerNameValid_False + }; + + ePlayerNameValidState GetPlayerNameValidState(); + void SetPlayerNameValidState(bool bState); +#endif +private: + vector *m_ppAdditionalModelParts; + bool m_bCheckedForModelParts; + bool m_bCheckedDLCForModelParts; + +#if defined(__PS3__) || defined(__ORBIS__) + ePlayerNameValidState m_ePlayerNameValidState; // 4J-PB - to ensure we have the characters for this name in our font, or display a player number instead +#endif +}; + +typedef struct +{ + int operator() (const shared_ptr k) const { return Player::hash_fnct (k); } + +} PlayerKeyHash; + +typedef struct +{ + bool operator() (const shared_ptr x, const shared_ptr y) const { return Player::eq_test (x, y); } +} PlayerKeyEq; diff --git a/Minecraft.World/PlayerAbilitiesPacket.cpp b/Minecraft.World/PlayerAbilitiesPacket.cpp new file mode 100644 index 00000000..b1b854a5 --- /dev/null +++ b/Minecraft.World/PlayerAbilitiesPacket.cpp @@ -0,0 +1,137 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.network.packet.h" +#include "PlayerAbilitiesPacket.h" + +const float PlayerAbilitiesPacket::SPEED_ACCURACY = 255.0f; + +PlayerAbilitiesPacket::PlayerAbilitiesPacket() +{ + invulnerable = false; + _isFlying = false; + _canFly = false; + instabuild = false; + flyingSpeed = 0.0f; + walkingSpeed = 0.0f; +} + +PlayerAbilitiesPacket::PlayerAbilitiesPacket(Abilities *abilities) +{ + this->setInvulnerable(abilities->invulnerable); + this->setFlying(abilities->flying); + this->setCanFly(abilities->mayfly); + this->setInstabuild(abilities->instabuild); + this->setFlyingSpeed(abilities->getFlyingSpeed()); + this->setWalkingSpeed(abilities->getWalkingSpeed()); +} + +void PlayerAbilitiesPacket::read(DataInputStream *dis) +{ + byte bitfield = dis->readByte(); + + this->setInvulnerable((bitfield & FLAG_INVULNERABLE) > 0); + this->setFlying((bitfield & FLAG_FLYING) > 0); + this->setCanFly((bitfield & FLAG_CAN_FLY) > 0); + this->setInstabuild((bitfield & FLAG_INSTABUILD) > 0); + this->setFlyingSpeed(dis->readByte() / SPEED_ACCURACY); + this->setWalkingSpeed(dis->readByte() / SPEED_ACCURACY); +} + +void PlayerAbilitiesPacket::write(DataOutputStream *dos) +{ + byte bitfield = 0; + + if (isInvulnerable()) bitfield |= FLAG_INVULNERABLE; + if (isFlying()) bitfield |= FLAG_FLYING; + if (canFly()) bitfield |= FLAG_CAN_FLY; + if (canInstabuild()) bitfield |= FLAG_INSTABUILD; + + dos->writeByte(bitfield); + dos->writeByte((int) (flyingSpeed * SPEED_ACCURACY)); + dos->writeByte((int) (walkingSpeed * SPEED_ACCURACY)); +} + +void PlayerAbilitiesPacket::handle(PacketListener *listener) +{ + listener->handlePlayerAbilities(shared_from_this()); +} + +int PlayerAbilitiesPacket::getEstimatedSize() +{ + return 2; +} + +//wstring getDebugInfo() +//{ +// return String.format("invuln=%b, flying=%b, canfly=%b, instabuild=%b, flyspeed=%.4f, walkspped=%.4f", isInvulnerable(), isFlying(), canFly(), canInstabuild(), getFlyingSpeed(), getWalkingSpeed()); +//} + +bool PlayerAbilitiesPacket::isInvulnerable() +{ + return invulnerable; +} + +void PlayerAbilitiesPacket::setInvulnerable(bool invulnerable) +{ + this->invulnerable = invulnerable; +} + +bool PlayerAbilitiesPacket::isFlying() +{ + return _isFlying; +} + +void PlayerAbilitiesPacket::setFlying(bool flying) +{ + _isFlying = flying; +} + +bool PlayerAbilitiesPacket::canFly() +{ + return _canFly; +} + +void PlayerAbilitiesPacket::setCanFly(bool canFly) +{ + this->_canFly = canFly; +} + +bool PlayerAbilitiesPacket::canInstabuild() +{ + return instabuild; +} + +void PlayerAbilitiesPacket::setInstabuild(bool instabuild) +{ + this->instabuild = instabuild; +} + +float PlayerAbilitiesPacket::getFlyingSpeed() +{ + return flyingSpeed; +} + +void PlayerAbilitiesPacket::setFlyingSpeed(float flySpeed) +{ + this->flyingSpeed = flySpeed; +} + +float PlayerAbilitiesPacket::getWalkingSpeed() +{ + return walkingSpeed; +} + +void PlayerAbilitiesPacket::setWalkingSpeed(float walkingSpeed) +{ + this->walkingSpeed = walkingSpeed; +} + +bool PlayerAbilitiesPacket::canBeInvalidated() +{ + return true; +} + +bool PlayerAbilitiesPacket::isInvalidatedBy(shared_ptr packet) +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/PlayerAbilitiesPacket.h b/Minecraft.World/PlayerAbilitiesPacket.h new file mode 100644 index 00000000..21c1fdc2 --- /dev/null +++ b/Minecraft.World/PlayerAbilitiesPacket.h @@ -0,0 +1,50 @@ +#pragma once + +#include "Packet.h" + +class Abilities; + +class PlayerAbilitiesPacket : public Packet, public enable_shared_from_this +{ +private: + static const int FLAG_INVULNERABLE = 1 << 0; + static const int FLAG_FLYING = 1 << 1; + static const int FLAG_CAN_FLY = 1 << 2; + static const int FLAG_INSTABUILD = 1 << 3; + static const float SPEED_ACCURACY; + + bool invulnerable; + bool _isFlying; + bool _canFly; + bool instabuild; + float flyingSpeed; + float walkingSpeed; + +public: + PlayerAbilitiesPacket(); + PlayerAbilitiesPacket(Abilities *abilities); + + void read(DataInputStream *dis); + void write(DataOutputStream *dos); + void handle(PacketListener *listener); + int getEstimatedSize(); + //wstring getDebugInfo(); + bool isInvulnerable(); + void setInvulnerable(bool invulnerable); + bool isFlying(); + void setFlying(bool flying); + bool canFly(); + void setCanFly(bool canFly); + bool canInstabuild(); + void setInstabuild(bool instabuild); + float getFlyingSpeed(); + void setFlyingSpeed(float flySpeed); + float getWalkingSpeed(); + void setWalkingSpeed(float walkingSpeed); + bool canBeInvalidated(); + bool isInvalidatedBy(shared_ptr packet); + +public: + static shared_ptr create() { return shared_ptr(new PlayerAbilitiesPacket()); } + virtual int getId() { return 202; } +}; \ No newline at end of file diff --git a/Minecraft.World/PlayerActionPacket.cpp b/Minecraft.World/PlayerActionPacket.cpp new file mode 100644 index 00000000..ce438cb6 --- /dev/null +++ b/Minecraft.World/PlayerActionPacket.cpp @@ -0,0 +1,58 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "PlayerActionPacket.h" + +const int PlayerActionPacket::START_DESTROY_BLOCK = 0; +const int PlayerActionPacket::ABORT_DESTROY_BLOCK = 1; +const int PlayerActionPacket::STOP_DESTROY_BLOCK = 2; +const int PlayerActionPacket::GET_UPDATED_BLOCK = 3; +const int PlayerActionPacket::DROP_ITEM = 4; +const int PlayerActionPacket::RELEASE_USE_ITEM = 5; + +PlayerActionPacket::PlayerActionPacket() +{ + x = 0; + y = 0; + z = 0; + face = 0; + action = 0; +} + +PlayerActionPacket::PlayerActionPacket(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; +} + +void PlayerActionPacket::read(DataInputStream *dis) //throws IOException +{ + action = dis->read(); + x = dis->readInt(); + y = dis->read(); + z = dis->readInt(); + face = dis->read(); +} + +void PlayerActionPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->write(action); + dos->writeInt(x); + dos->write(y); + dos->writeInt(z); + dos->write(face); +} + +void PlayerActionPacket::handle(PacketListener *listener) +{ + listener->handlePlayerAction(shared_from_this()); +} + +int PlayerActionPacket::getEstimatedSize() +{ + return 11; +} diff --git a/Minecraft.World/PlayerActionPacket.h b/Minecraft.World/PlayerActionPacket.h new file mode 100644 index 00000000..0228ebb4 --- /dev/null +++ b/Minecraft.World/PlayerActionPacket.h @@ -0,0 +1,31 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class PlayerActionPacket : public Packet, public enable_shared_from_this +{ +public: + static const int START_DESTROY_BLOCK; + static const int ABORT_DESTROY_BLOCK; + static const int STOP_DESTROY_BLOCK; + static const int GET_UPDATED_BLOCK; + static const int DROP_ITEM; + static const int RELEASE_USE_ITEM; + + int x, y, z, face, action; + + PlayerActionPacket(); + PlayerActionPacket(int action, int x, int y, int z, int face); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new PlayerActionPacket()); } + virtual int getId() { return 14; } +}; + + diff --git a/Minecraft.World/PlayerCommandPacket.cpp b/Minecraft.World/PlayerCommandPacket.cpp new file mode 100644 index 00000000..6aecc0e6 --- /dev/null +++ b/Minecraft.World/PlayerCommandPacket.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "net.minecraft.world.entity.h" +#include "PlayerCommandPacket.h" + + + +const int PlayerCommandPacket::START_SNEAKING = 1; +const int PlayerCommandPacket::STOP_SNEAKING = 2; +const int PlayerCommandPacket::STOP_SLEEPING = 3; +const int PlayerCommandPacket::START_SPRINTING = 4; +const int PlayerCommandPacket::STOP_SPRINTING = 5; +const int PlayerCommandPacket::START_IDLEANIM = 6; +const int PlayerCommandPacket::STOP_IDLEANIM = 7; + + +PlayerCommandPacket::PlayerCommandPacket() +{ + id = -1; + action = 0; +} + +PlayerCommandPacket::PlayerCommandPacket(shared_ptr e, int action) +{ + id = e->entityId; + this->action = action; +} + +void PlayerCommandPacket::read(DataInputStream *dis) //throws IOException +{ + id = dis->readInt(); + action = dis->readByte(); +} + +void PlayerCommandPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(id); + dos->writeByte(action); +} + +void PlayerCommandPacket::handle(PacketListener *listener) +{ + listener->handlePlayerCommand(shared_from_this()); +} + +int PlayerCommandPacket::getEstimatedSize() +{ + return 5; +} diff --git a/Minecraft.World/PlayerCommandPacket.h b/Minecraft.World/PlayerCommandPacket.h new file mode 100644 index 00000000..dd6c8cb1 --- /dev/null +++ b/Minecraft.World/PlayerCommandPacket.h @@ -0,0 +1,37 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class PlayerCommandPacket : public Packet, public enable_shared_from_this +{ +public: + static const int START_SNEAKING; + static const int STOP_SNEAKING; + static const int STOP_SLEEPING; + static const int START_SPRINTING; + static const int STOP_SPRINTING; + static const int START_IDLEANIM; + static const int STOP_IDLEANIM; + + // 4J Added + // 4J-PB - Making this host only setting + /* + static const int SHOW_ON_MAPS; + static const int HIDE_ON_MAPS; + */ + + int id; + int action; + + PlayerCommandPacket(); + PlayerCommandPacket(shared_ptr e, int action); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); +public: + static shared_ptr create() { return shared_ptr(new PlayerCommandPacket()); } + virtual int getId() { return 19; } +}; \ No newline at end of file diff --git a/Minecraft.World/PlayerEnderChestContainer.cpp b/Minecraft.World/PlayerEnderChestContainer.cpp new file mode 100644 index 00000000..466d9710 --- /dev/null +++ b/Minecraft.World/PlayerEnderChestContainer.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "PlayerEnderChestContainer.h" + +PlayerEnderChestContainer::PlayerEnderChestContainer() : SimpleContainer(IDS_TILE_ENDERCHEST, 9 * 3) +{ + activeChest = nullptr; +} + +void PlayerEnderChestContainer::setActiveChest(shared_ptr activeChest) +{ + this->activeChest = activeChest; +} + +void PlayerEnderChestContainer::setItemsByTag(ListTag *enderItemsList) +{ + for (int i = 0; i < getContainerSize(); i++) + { + setItem(i, nullptr); + } + for (int i = 0; i < enderItemsList->size(); i++) + { + CompoundTag *tag = enderItemsList->get(i); + int slot = tag->getByte(L"Slot") & 0xff; + if (slot >= 0 && slot < getContainerSize()) setItem(slot, ItemInstance::fromTag(tag)); + } +} + +ListTag *PlayerEnderChestContainer::createTag() +{ + ListTag *items = new ListTag(L"EnderItems"); + for (int i = 0; i < getContainerSize(); i++) + { + shared_ptr item = getItem(i); + if (item != NULL) + { + CompoundTag *tag = new CompoundTag(); + tag->putByte(L"Slot", (byte) i); + item->save(tag); + items->add(tag); + } + } + return items; +} + +bool PlayerEnderChestContainer::stillValid(shared_ptr player) +{ + if (activeChest != NULL && !activeChest->stillValid(player)) + { + return false; + } + return SimpleContainer::stillValid(player); +} + +void PlayerEnderChestContainer::startOpen() +{ + if (activeChest != NULL) + { + activeChest->startOpen(); + } + SimpleContainer::startOpen(); +} + +void PlayerEnderChestContainer::stopOpen() +{ + if (activeChest) + { + activeChest->stopOpen(); + } + SimpleContainer::stopOpen(); + activeChest = nullptr; +} \ No newline at end of file diff --git a/Minecraft.World/PlayerEnderChestContainer.h b/Minecraft.World/PlayerEnderChestContainer.h new file mode 100644 index 00000000..523417aa --- /dev/null +++ b/Minecraft.World/PlayerEnderChestContainer.h @@ -0,0 +1,21 @@ +#pragma once + +#include "SimpleContainer.h" + +class EnderChestTileEntity; + +class PlayerEnderChestContainer : public SimpleContainer +{ +private: + shared_ptr activeChest; + +public: + PlayerEnderChestContainer(); + + void setActiveChest(shared_ptr activeChest); + void setItemsByTag(ListTag *enderItemsList); + ListTag *createTag(); + bool stillValid(shared_ptr player); + void startOpen(); + void stopOpen(); +}; \ No newline at end of file diff --git a/Minecraft.World/PlayerIO.h b/Minecraft.World/PlayerIO.h new file mode 100644 index 00000000..559ab2ee --- /dev/null +++ b/Minecraft.World/PlayerIO.h @@ -0,0 +1,22 @@ +#pragma once +using namespace std; + +// If we have more than MAX_PLAYER_DATA_SAVES player.dat's then we delete the oldest ones +// This value can be no higher than MAXIMUM_MAP_SAVE_DATA/3 (3 being the number of dimensions in future versions) +#define MAX_PLAYER_DATA_SAVES 80 + +class Player; + +class PlayerIO +{ +public: + virtual void save(shared_ptr player) = 0; + virtual bool load(shared_ptr player) = 0; // 4J Changed return val to bool to check if new player or loaded player + virtual CompoundTag *loadPlayerDataTag(PlayerUID xuid) = 0; // 4J Changed from string name to xuid + + // 4J Added + virtual void clearOldPlayerFiles() = 0; + virtual void saveMapIdLookup() = 0; + virtual void deleteMapFilesForPlayer(shared_ptr player) = 0; + virtual void saveAllCachedData() = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/PlayerInfoPacket.cpp b/Minecraft.World/PlayerInfoPacket.cpp new file mode 100644 index 00000000..9cbe5f7c --- /dev/null +++ b/Minecraft.World/PlayerInfoPacket.cpp @@ -0,0 +1,61 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "..\Minecraft.Client\ServerPlayer.h" +#include "..\Minecraft.Client\PlayerConnection.h" +#include +#include "PacketListener.h" +#include "InputOutputStream.h" +#include "PlayerInfoPacket.h" + + + +PlayerInfoPacket::PlayerInfoPacket() +{ + m_networkSmallId = 0; + m_playerColourIndex = -1; + m_playerPrivileges = 0; + m_entityId = -1; +} + +PlayerInfoPacket::PlayerInfoPacket(BYTE networkSmallId, short playerColourIndex, unsigned int playerPrivileges) +{ + m_networkSmallId = networkSmallId; + m_playerColourIndex = playerColourIndex; + m_playerPrivileges = playerPrivileges; + m_entityId = -1; +} + +PlayerInfoPacket::PlayerInfoPacket(shared_ptr player) +{ + m_networkSmallId = 0; + if(player->connection != NULL && player->connection->getNetworkPlayer() != NULL) m_networkSmallId = player->connection->getNetworkPlayer()->GetSmallId(); + m_playerColourIndex = player->getPlayerIndex(); + m_playerPrivileges = player->getAllPlayerGamePrivileges(); + m_entityId = player->entityId; +} + +void PlayerInfoPacket::read(DataInputStream *dis) +{ + m_networkSmallId = dis->readByte(); + m_playerColourIndex = dis->readShort(); + m_playerPrivileges = dis->readInt(); + m_entityId = dis->readInt(); +} + +void PlayerInfoPacket::write(DataOutputStream *dos) +{ + dos->writeByte(m_networkSmallId); + dos->writeShort(m_playerColourIndex); + dos->writeInt(m_playerPrivileges); + dos->writeInt(m_entityId); +} + +void PlayerInfoPacket::handle(PacketListener *listener) +{ + listener->handlePlayerInfo(shared_from_this()); +} + +int PlayerInfoPacket::getEstimatedSize() +{ + return 2 + 2 + 4 + 4; +} \ No newline at end of file diff --git a/Minecraft.World/PlayerInfoPacket.h b/Minecraft.World/PlayerInfoPacket.h new file mode 100644 index 00000000..85e2ed64 --- /dev/null +++ b/Minecraft.World/PlayerInfoPacket.h @@ -0,0 +1,32 @@ +#pragma once +using namespace std; +#include "Packet.h" + +class ServerPlayer; + +class PlayerInfoPacket : public Packet, public enable_shared_from_this +{ + public: + // 4J Stu - I have re-purposed this packet for our uses + //wstring name; + //bool add; + //int latency; + short m_networkSmallId; + short m_playerColourIndex; + unsigned int m_playerPrivileges; + int m_entityId; + + PlayerInfoPacket(); + //PlayerInfoPacket(const wstring &name, bool add, int latency); + PlayerInfoPacket(BYTE networkSmallId, short playerColourIndex, unsigned int playerPrivileges = 0); + PlayerInfoPacket(shared_ptr player); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new PlayerInfoPacket()); } + virtual int getId() { return 201; } +}; \ No newline at end of file diff --git a/Minecraft.World/PlayerInputPacket.cpp b/Minecraft.World/PlayerInputPacket.cpp new file mode 100644 index 00000000..6dd6fcae --- /dev/null +++ b/Minecraft.World/PlayerInputPacket.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "PlayerInputPacket.h" + + + +PlayerInputPacket::PlayerInputPacket() +{ + xa = 0.0f; + ya = 0.0f; + isJumpingVar = false; + isSneakingVar = false; + xRot = 0.0f; + yRot = 0.0f; +} + +PlayerInputPacket::PlayerInputPacket(float xa, float ya, bool isJumpingVar, bool isSneakingVar, float xRot, float yRot) +{ + this->xa = xa; + this->ya = ya; + this->isJumpingVar = isJumpingVar; + this->isSneakingVar = isSneakingVar; + this->xRot = xRot; + this->yRot = yRot; +} + +void PlayerInputPacket::read(DataInputStream *dis) //throws IOException +{ + xa = dis->readFloat(); + ya = dis->readFloat(); + xRot = dis->readFloat(); + yRot = dis->readFloat(); + isJumpingVar = dis->readBoolean(); + isSneakingVar = dis->readBoolean(); +} + +void PlayerInputPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeFloat(xa); + dos->writeFloat(ya); + dos->writeFloat(xRot); + dos->writeFloat(yRot); + dos->writeBoolean(isJumpingVar); + dos->writeBoolean(isSneakingVar); +} + +void PlayerInputPacket::handle(PacketListener *listener) +{ + listener->handlePlayerInput(shared_from_this()); +} + +int PlayerInputPacket::getEstimatedSize() +{ + return 18; +} + +float PlayerInputPacket::getXa() +{ + return xa; +} + +float PlayerInputPacket::getXRot() +{ + return xRot; +} + +float PlayerInputPacket::getYa() +{ + return ya; +} + +float PlayerInputPacket::getYRot() +{ + return yRot; +} + +bool PlayerInputPacket::isJumping() +{ + return isJumpingVar; +} + +bool PlayerInputPacket::isSneaking() +{ + return isSneakingVar; +} diff --git a/Minecraft.World/PlayerInputPacket.h b/Minecraft.World/PlayerInputPacket.h new file mode 100644 index 00000000..8bc11d3e --- /dev/null +++ b/Minecraft.World/PlayerInputPacket.h @@ -0,0 +1,36 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class PlayerInputPacket : public Packet, public enable_shared_from_this +{ + +private: + float xa; + float ya; + bool isJumpingVar; + bool isSneakingVar; + float xRot; + float yRot; + +public: + PlayerInputPacket(); + PlayerInputPacket(float xa, float ya, bool isJumpingVar, bool isSneakingVar, float xRot, float yRot); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + + float getXa(); + float getXRot(); + float getYa(); + float getYRot(); + bool isJumping(); + bool isSneaking(); + +public: + static shared_ptr create() { return shared_ptr(new PlayerInputPacket()); } + virtual int getId() { return 27; } +}; \ No newline at end of file diff --git a/Minecraft.World/PortalForcer.cpp b/Minecraft.World/PortalForcer.cpp new file mode 100644 index 00000000..7c9b7a86 --- /dev/null +++ b/Minecraft.World/PortalForcer.cpp @@ -0,0 +1,381 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.dimension.h" +#include "PortalForcer.h" + +PortalForcer::PortalForcer() +{ + random = new Random(); +} + + +void PortalForcer::force(Level *level, shared_ptr e) +{ + if (level->dimension->id == 1) + { + int x = Mth::floor(e->x); + int y = Mth::floor(e->y) - 1; + int z = Mth::floor(e->z); + + int xa = 1; + int za = 0; + for (int b = -2; b <= 2; b++) + { + for (int s = -2; s <= 2; s++) + { + for (int h = -1; h < 3; h++) + { + int xt = x + s * xa + b * za; + int yt = y + h; + int zt = z + s * za - b * xa; + + bool border = h < 0; + + level->setTile(xt, yt, zt, border ? Tile::obsidian_Id : 0); + } + } + } + + e->moveTo(x, y, z, e->yRot, 0); + e->xd = e->yd = e->zd = 0; + + return; + } + + if (findPortal(level, e)) + { + return; + } + + createPortal(level, e); + findPortal(level, e); +} + + +bool PortalForcer::findPortal(Level *level, shared_ptr e) +{ + // 4J Stu - Decrease the range at which we search for a portal in the nether given our smaller nether + int r = 16;//* 8; + if(level->dimension->id == -1) + { + r *= 3; + } + else + { +#ifdef __PSVITA__ + // AP poor little Vita takes 30 seconds to leave the Nether. This should help + r *= 5; +#else + r *= 8; +#endif + } + double closest = -1; + int xTarget = 0; + int yTarget = 0; + int zTarget = 0; + + int xc = Mth::floor(e->x); + int zc = Mth::floor(e->z); + + for (int x = xc - r; x <= xc + r; x++) + { + double xd = (x + 0.5) - e->x; + for (int z = zc - r; z <= zc + r; z++) + { + double zd = (z + 0.5) - e->z; + for (int y = level->getHeight() - 1; y >= 0; y--) + { + if (level->getTile(x, y, z) == Tile::portalTile_Id) + { + while (level->getTile(x, y - 1, z) == Tile::portalTile_Id) + { + y--; + } + + double yd = (y + 0.5) - e->y; + double dist = xd * xd + yd * yd + zd * zd; + if (closest < 0 || dist < closest) + { + closest = dist; + xTarget = x; + yTarget = y; + zTarget = z; + } + } + } + } + } + + if (closest >= 0) + { + int x = xTarget; + int y = yTarget; + int z = zTarget; + + double xt = x + 0.5; + double yt = y + 0.5; + double zt = z + 0.5; + + if (level->getTile(x - 1, y, z) == Tile::portalTile_Id) xt -= 0.5; + if (level->getTile(x + 1, y, z) == Tile::portalTile_Id) xt += 0.5; + + if (level->getTile(x, y, z - 1) == Tile::portalTile_Id) zt -= 0.5; + if (level->getTile(x, y, z + 1) == Tile::portalTile_Id) zt += 0.5; + + e->moveTo(xt, yt, zt, e->yRot, 0); + e->xd = e->yd = e->zd = 0; + return true; + } + + return false; +} + + +bool PortalForcer::createPortal(Level *level, shared_ptr e) +{ + // 4J Stu - Increase the range at which we try and create a portal to stop creating them floating in mid air over lava + int r = 16 * 3; + double closest = -1; + + int xc = Mth::floor(e->x); + int yc = Mth::floor(e->y); + int zc = Mth::floor(e->z); + + // 4J Stu - Changes to stop Portals being created at the border of the nether inside the bedrock + int XZSIZE = level->dimension->getXZSize() * 16; // XZSize is chunks, convert to blocks + int XZOFFSET = (XZSIZE / 2) - 4; // Subtract 4 to stay away from the edges // TODO Make the 4 a constant in HellRandomLevelSource + + // Move the positions that we want to check away from the edge of the world + if( (xc - r) < -XZOFFSET ) + { + app.DebugPrintf("Adjusting portal creation x due to being too close to the edge\n"); + xc -= ( (xc - r) + XZOFFSET); + } + else if ( (xc + r) >= XZOFFSET ) + { + app.DebugPrintf("Adjusting portal creation x due to being too close to the edge\n"); + xc -= ( (xc + r) - XZOFFSET); + } + if( (zc - r) < -XZOFFSET ) + { + app.DebugPrintf("Adjusting portal creation z due to being too close to the edge\n"); + zc -= ( (zc - r) + XZOFFSET); + } + else if ( (zc + r) >= XZOFFSET ) + { + app.DebugPrintf("Adjusting portal creation z due to being too close to the edge\n"); + zc -= ( (zc + r) - XZOFFSET); + } + + int xTarget = xc; + int yTarget = yc; + int zTarget = zc; + int dirTarget = 0; + + int dirOffs = random->nextInt(4); + + { + for (int x = xc - r; x <= xc + r; x++) + { + double xd = (x + 0.5) - e->x; + for (int z = zc - r; z <= zc + r; z++) + { + double zd = (z + 0.5) - e->z; + + for (int y = level->getHeight() - 1; y >= 0; y--) + { + if (level->isEmptyTile(x, y, z)) + { + while (y>0 && level->isEmptyTile(x, y - 1, z)) + { + y--; + } + + for (int dir = dirOffs; dir < dirOffs + 4; dir++) + { + int xa = dir % 2; + int za = 1 - xa; + + if (dir % 4 >= 2) + { + xa = -xa; + za = -za; + } + + + for (int b = 0; b < 3; b++) + { + for (int s = 0; s < 4; s++) + { + for (int h = -1; h < 4; h++) + { + int xt = x + (s - 1) * xa + b * za; + int yt = y + h; + int zt = z + (s - 1) * za - b * xa; + + // 4J Stu - Changes to stop Portals being created at the border of the nether inside the bedrock + if( ( xt < -XZOFFSET ) || ( xt >= XZOFFSET ) || ( zt < -XZOFFSET ) || ( zt >= XZOFFSET ) ) + { + app.DebugPrintf("Skipping possible portal location as at least one block is too close to the edge\n"); + goto next_first; + } + + if (h < 0 && !level->getMaterial(xt, yt, zt)->isSolid()) goto next_first; + if (h >= 0 && !level->isEmptyTile(xt, yt, zt)) goto next_first; + } + } + } + + double yd = (y + 0.5) - e->y; + double dist = xd * xd + yd * yd + zd * zd; + if (closest < 0 || dist < closest) + { + closest = dist; + xTarget = x; + yTarget = y; + zTarget = z; + dirTarget = dir % 4; + } + } + } + next_first: continue; + } + } + } + } + if (closest < 0) + { + for (int x = xc - r; x <= xc + r; x++) + { + double xd = (x + 0.5) - e->x; + for (int z = zc - r; z <= zc + r; z++) + { + double zd = (z + 0.5) - e->z; + + for (int y = level->getHeight() - 1; y >= 0; y--) + { + if (level->isEmptyTile(x, y, z)) + { + while (y > 0 && level->isEmptyTile(x, y - 1, z)) + { + y--; + } + + for (int dir = dirOffs; dir < dirOffs + 2; dir++) + { + int xa = dir % 2; + int za = 1 - xa; + for (int s = 0; s < 4; s++) + { + for (int h = -1; h < 4; h++) + { + int xt = x + (s - 1) * xa; + int yt = y + h; + int zt = z + (s - 1) * za; + + // 4J Stu - Changes to stop Portals being created at the border of the nether inside the bedrock + if( ( xt < -XZOFFSET ) || ( xt >= XZOFFSET ) || ( zt < -XZOFFSET ) || ( zt >= XZOFFSET ) ) + { + app.DebugPrintf("Skipping possible portal location as at least one block is too close to the edge\n"); + goto next_second; + } + + if (h < 0 && !level->getMaterial(xt, yt, zt)->isSolid()) goto next_second; + if (h >= 0 && !level->isEmptyTile(xt, yt, zt)) goto next_second; + } + } + + double yd = (y + 0.5) - e->y; + double dist = xd * xd + yd * yd + zd * zd; + if (closest < 0 || dist < closest) + { + closest = dist; + xTarget = x; + yTarget = y; + zTarget = z; + dirTarget = dir % 2; + } + } + } + next_second: continue; + } + } + } + } + + + + int dir = dirTarget; + + int x = xTarget; + int y = yTarget; + int z = zTarget; + + int xa = dir % 2; + int za = 1 - xa; + + if (dir % 4 >= 2) + { + xa = -xa; + za = -za; + } + + + if (closest < 0) + { + if (yTarget < 70) yTarget = 70; + if (yTarget > level->getHeight() - 10) yTarget = level->getHeight() - 10; + y = yTarget; + + for (int b = -1; b <= 1; b++) + { + for (int s = 1; s < 3; s++) + { + for (int h = -1; h < 3; h++) + { + int xt = x + (s - 1) * xa + b * za; + int yt = y + h; + int zt = z + (s - 1) * za - b * xa; + + bool border = h < 0; + + level->setTile(xt, yt, zt, border ? Tile::obsidian_Id : 0); + } + } + } + } + + for (int pass = 0; pass < 4; pass++) + { + level->noNeighborUpdate = true; + for (int s = 0; s < 4; s++) + { + for (int h = -1; h < 4; h++) + { + int xt = x + (s - 1) * xa; + int yt = y + h; + int zt = z + (s - 1) * za; + + bool border = s == 0 || s == 3 || h == -1 || h == 3; + level->setTile(xt, yt, zt, border ? Tile::obsidian_Id : Tile::portalTile_Id); + } + } + level->noNeighborUpdate = false; + + for (int s = 0; s < 4; s++) + { + for (int h = -1; h < 4; h++) + { + int xt = x + (s - 1) * xa; + int yt = y + h; + int zt = z + (s - 1) * za; + + level->updateNeighborsAt(xt, yt, zt, level->getTile(xt, yt, zt)); + } + } + } + + return true; +} diff --git a/Minecraft.World/PortalForcer.h b/Minecraft.World/PortalForcer.h new file mode 100644 index 00000000..feb2f129 --- /dev/null +++ b/Minecraft.World/PortalForcer.h @@ -0,0 +1,21 @@ +#pragma once + +class Random; + +class PortalForcer +{ +private: + Random *random; + +public: + // 4J Stu Added - Java has no ctor, but we need to initialise random + PortalForcer(); + + void force(Level *level, shared_ptr e); + +public: + bool findPortal(Level *level, shared_ptr e); + +public: + bool createPortal(Level *level, shared_ptr e); +}; \ No newline at end of file diff --git a/Minecraft.World/PortalMaterial.h b/Minecraft.World/PortalMaterial.h new file mode 100644 index 00000000..ae40dbec --- /dev/null +++ b/Minecraft.World/PortalMaterial.h @@ -0,0 +1,12 @@ +#pragma once +#include "Material.h" + +class PortalMaterial : public Material +{ +public: + PortalMaterial(MaterialColor *color) : Material(color) { } + + virtual bool isSolid() { return false; } + virtual bool blocksLight() { return false; } + virtual bool blocksMotion() { return false; } +}; \ No newline at end of file diff --git a/Minecraft.World/PortalTile.cpp b/Minecraft.World/PortalTile.cpp new file mode 100644 index 00000000..1b500a36 --- /dev/null +++ b/Minecraft.World/PortalTile.cpp @@ -0,0 +1,246 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.item.h" +#include "PortalTile.h" +#include "FireTile.h" + +PortalTile::PortalTile(int id) : HalfTransparentTile(id, L"portal", Material::portal, false) +{ + setTicking(true); +} + +void PortalTile::tick(Level *level, int x, int y, int z, Random *random) +{ + HalfTransparentTile::tick(level, x, y, z, random); + + if (level->dimension->isNaturalDimension() && random->nextInt(2000) < level->difficulty) + { + // locate floor + int y0 = y; + while (!level->isTopSolidBlocking(x, y0, z) && y0 > 0) + { + y0--; + } + if (y0 > 0 && !level->isSolidBlockingTile(x, y0 + 1, z)) + { + // spawn a pig man here + int result = 0; + bool spawned = MonsterPlacerItem::spawnMobAt(level, 57, x + .5, y0 + 1.1, z + .5, &result) != NULL; + } + } +} + +AABB *PortalTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +void PortalTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + if (level->getTile(x - 1, y, z) == id || level->getTile(x + 1, y, z) == id) + { + float xr = 8 / 16.0f; + float yr = 2 / 16.0f; + this->setShape(0.5f - xr, 0, 0.5f - yr, 0.5f + xr, 1, 0.5f + yr); + } + else + { + float xr = 2 / 16.0f; + float yr = 8 / 16.0f; + this->setShape(0.5f - xr, 0, 0.5f - yr, 0.5f + xr, 1, 0.5f + yr); + } +} + +bool PortalTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool PortalTile::isCubeShaped() +{ + return false; +} + +bool PortalTile::trySpawnPortal(Level *level, int x, int y, int z, bool actuallySpawn) +{ + int xd = 0; + int zd = 0; + if (level->getTile(x - 1, y, z) == Tile::obsidian_Id || level->getTile(x + 1, y, z) == Tile::obsidian_Id) xd = 1; + if (level->getTile(x, y, z - 1) == Tile::obsidian_Id || level->getTile(x, y, z + 1) == Tile::obsidian_Id) zd = 1; + + if (xd == zd) return false; + + if (level->getTile(x - xd, y, z - zd) == 0) + { + x -= xd; + z -= zd; + } + + for (int xx = -1; xx <= 2; xx++) + { + for (int yy = -1; yy <= 3; yy++) + { + bool edge = (xx == -1) || (xx == 2) || (yy == -1) || (yy == 3); + if ((xx == -1 || xx == 2) && (yy == -1 || yy == 3)) continue; + + int t = level->getTile(x + xd * xx, y + yy, z + zd * xx); + + if (edge) + { + if (t != Tile::obsidian_Id) return false; + } + else + { + if (t != 0 && t != Tile::fire_Id) return false; + } + } + } + + if( !actuallySpawn ) + return true; + + level->noNeighborUpdate = true; + for (int xx = 0; xx < 2; xx++) + { + for (int yy = 0; yy < 3; yy++) + { + level->setTile(x + xd * xx, y + yy, z + zd * xx, Tile::portalTile_Id); + } + } + level->noNeighborUpdate = false; + + return true; + +} + +void PortalTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + int xd = 0; + int zd = 1; + if (level->getTile(x - 1, y, z) == id || level->getTile(x + 1, y, z) == id) + { + xd = 1; + zd = 0; + } + + int yBottom = y; + while (level->getTile(x, yBottom - 1, z) == id) + yBottom--; + + if (level->getTile(x, yBottom - 1, z) != Tile::obsidian_Id) + { + level->setTile(x, y, z, 0); + return; + } + + int height = 1; + while (height < 4 && level->getTile(x, yBottom + height, z) == id) + height++; + + if (height != 3 || level->getTile(x, yBottom + height, z) != Tile::obsidian_Id) + { + level->setTile(x, y, z, 0); + return; + } + + bool we = level->getTile(x - 1, y, z) == id || level->getTile(x + 1, y, z) == id; + bool ns = level->getTile(x, y, z - 1) == id || level->getTile(x, y, z + 1) == id; + if (we && ns) + { + level->setTile(x, y, z, 0); + return; + } + + if (!(// + (level->getTile(x + xd, y, z + zd) == Tile::obsidian_Id && level->getTile(x - xd, y, z - zd) == id) || // + (level->getTile(x - xd, y, z - zd) == Tile::obsidian_Id && level->getTile(x + xd, y, z + zd) == id)// + )) + { + level->setTile(x, y, z, 0); + return; + } + +} + +bool PortalTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + if (level->getTile(x, y, z) == id) return false; + + bool w = level->getTile(x - 1, y, z) == id && level->getTile(x - 2, y, z) != id; + bool e = level->getTile(x + 1, y, z) == id && level->getTile(x + 2, y, z) != id; + + bool n = level->getTile(x, y, z - 1) == id && level->getTile(x, y, z - 2) != id; + bool s = level->getTile(x, y, z + 1) == id && level->getTile(x, y, z + 2) != id; + + bool we = w || e; + bool ns = n || s; + + if (we && face == 4) return true; + if (we && face == 5) return true; + if (ns && face == 2) return true; + if (ns && face == 3) return true; + + return false; +} + +int PortalTile::getResourceCount(Random *random) +{ + return 0; +} + +int PortalTile::getRenderLayer() +{ + return 1; +} + +void PortalTile::entityInside(Level *level, int x, int y, int z, shared_ptr entity) +{ + if (entity->riding == NULL && entity->rider.lock() == NULL) entity->handleInsidePortal(); +} + +void PortalTile::animateTick(Level *level, int xt, int yt, int zt, Random *random) +{ + if (random->nextInt(100) == 0) + { + level->playLocalSound(xt + 0.5, yt + 0.5, zt + 0.5, eSoundType_PORTAL_PORTAL, 0.5f, random->nextFloat() * 0.4f + 0.8f); + } + for (int i = 0; i < 4; i++) + { + double x = xt + random->nextFloat(); + double y = yt + random->nextFloat(); + double z = zt + random->nextFloat(); + double xa = 0; + double ya = 0; + double za = 0; + int flip = random->nextInt(2) * 2 - 1; + xa = (random->nextFloat() - 0.5) * 0.5; + ya = (random->nextFloat() - 0.5) * 0.5; + za = (random->nextFloat() - 0.5) * 0.5; + if (level->getTile(xt - 1, yt, zt) == id || level->getTile(xt + 1, yt, zt) == id) + { + z = zt + 0.5 + (0.25) * flip; + za = (random->nextFloat() * 2) * flip; + } + else + { + x = xt + 0.5 + (0.25) * flip; + xa = (random->nextFloat() * 2) * flip; + } + + level->addParticle(eParticleType_netherportal, x, y, z, xa, ya, za); + } +} + +int PortalTile::cloneTileId(Level *level, int x, int y, int z) +{ + return 0; +} + +// 4J Added - We cannot collect the portal tile, so don't consider it as a hit result +// Bug #754 - Riding a minecart into a portal will trap the player +bool PortalTile::mayPick() +{ + return false; +} diff --git a/Minecraft.World/PortalTile.h b/Minecraft.World/PortalTile.h new file mode 100644 index 00000000..7be4320f --- /dev/null +++ b/Minecraft.World/PortalTile.h @@ -0,0 +1,25 @@ +#pragma once +#include "HalfTransparentTile.h" +#include "Definitions.h" + +class Random; + +class PortalTile : public HalfTransparentTile +{ +public: + PortalTile(int id); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual bool trySpawnPortal(Level *level, int x, int y, int z, bool actuallySpawn); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + virtual int getResourceCount(Random *random); + virtual int getRenderLayer(); + virtual void entityInside(Level *level, int x, int y, int z, shared_ptr entity); + virtual void animateTick(Level *level, int xt, int yt, int zt, Random *random); + virtual int cloneTileId(Level *level, int x, int y, int z); + virtual bool mayPick(); // 4J Added override +}; diff --git a/Minecraft.World/Pos.cpp b/Minecraft.World/Pos.cpp new file mode 100644 index 00000000..3fad76e8 --- /dev/null +++ b/Minecraft.World/Pos.cpp @@ -0,0 +1,248 @@ +#include "stdafx.h" + +#include "Pos.h" + + + +Pos::Pos() +{ + x = y = z = 0; +} + +Pos::Pos(int x, int y, int z) +{ + this->x = x; + this->y = y; + this->z = z; +} + +Pos::Pos(Pos *position) +{ + this->x = position->x; + this->y = position->y; + this->z = position->z; +} + +//@Override +//public boolean equals(Object other) +bool Pos::equals(void *other) +{ + // TODO 4J Stu I cannot do a dynamic_cast from a void pointer + // If I cast it to a Pos then do a dynamic_cast will it still return NULL if it wasn't originally a Pos? + if (!( dynamic_cast( (Pos *)other ) != NULL )) + { + return false; + } + + Pos *p = (Pos *) other; + return x == p->x && y == p->y && z == p->z; +} + +//@Override +int Pos::hashCode() +{ + return x + (z << 8) + (y << 16); +} + +int Pos::compareTo(Pos *pos) +{ + if (y == pos->y) + { + if (z == pos->z) + { + return x - pos->x; + } + return z - pos->z; + } + return y - pos->y; +} + +Pos *Pos::offset(int x, int y, int z) +{ + return new Pos(this->x + x, this->y + y, this->z + z); +} + +void Pos::set(int x, int y, int z) +{ + this->x = x; + this->y = y; + this->z = z; +} + +void Pos::set(Pos *pos) +{ + this->x = pos->x; + this->y = pos->y; + this->z = pos->z; +} + +Pos *Pos::above() +{ + return new Pos(x, y + 1, z); +} + +Pos *Pos::above(int steps) +{ + return new Pos(x, y + steps, z); +} + +Pos *Pos::below() +{ + return new Pos(x, y - 1, z); +} + +Pos *Pos::below(int steps) +{ + return new Pos(x, y - steps, z); +} + +Pos *Pos::north() +{ + return new Pos(x, y, z - 1); +} + +Pos *Pos::north(int steps) +{ + return new Pos(x, y, z - steps); +} + +Pos *Pos::south() +{ + return new Pos(x, y, z + 1); +} + +Pos *Pos::south(int steps) +{ + return new Pos(x, y, z + steps); +} + +Pos *Pos::west() +{ + return new Pos(x - 1, y, z); +} + +Pos *Pos::west(int steps) +{ + return new Pos(x - 1, y, z); +} + +Pos *Pos::east() +{ + return new Pos(x + 1, y, z); +} + +Pos *Pos::east(int steps) +{ + return new Pos(x + steps, y, z); +} + +void Pos::move(int x, int y, int z) +{ + this->x += x; + this->y += y; + this->z += z; +} + +void Pos::move(Pos pos) +{ + this->x += pos.x; + this->y += pos.y; + this->z += pos.z; +} + +void Pos::moveX(int steps) +{ + this->x += steps; +} + +void Pos::moveY(int steps) +{ + this->y += steps; +} + +void Pos::moveZ(int steps) +{ + this->z += steps; +} + +void Pos::moveUp(int steps) +{ + this->y += steps; +} + +void Pos::moveUp() +{ + this->y++; +} + +void Pos::moveDown(int steps) +{ + this->y -= steps; +} + +void Pos::moveDown() +{ + this->y--; +} + +void Pos::moveEast(int steps) +{ + this->x += steps; +} + +void Pos::moveEast() +{ + this->x++; +} + +void Pos::moveWest(int steps) +{ + this->x -= steps; +} + +void Pos::moveWest() +{ + this->x--; +} + +void Pos::moveNorth(int steps) +{ + this->z -= steps; +} + +void Pos::moveNorth() +{ + this->z--; +} + +void Pos::moveSouth(int steps) +{ + this->z += steps; +} + +void Pos::moveSouth() +{ + this->z++; +} + +double Pos::dist(int x, int y, int z) +{ + int dx = this->x - x; + int dy = this->y - y; + int dz = this->z - z; + + return sqrt( (double) dx * dx + dy * dy + dz * dz); +} + +double Pos::dist(Pos *pos) +{ + return dist(pos->x, pos->y, pos->z); +} + +float Pos::distSqr(int x, int y, int z) +{ + int dx = this->x - x; + int dy = this->y - y; + int dz = this->z - z; + return dx * dx + dy * dy + dz * dz; +} \ No newline at end of file diff --git a/Minecraft.World/Pos.h b/Minecraft.World/Pos.h new file mode 100644 index 00000000..a07c7fe3 --- /dev/null +++ b/Minecraft.World/Pos.h @@ -0,0 +1,97 @@ +#pragma once +// Pos implements Comparable +// We don't REALLY need it as it's main use it to make things easy +// to handle in the java array/list classes, but adding to help +// maintain as much original code as possible + +//class Pos //implements Comparable +class Pos +{ +public: + int x; + int y; + int z; + + Pos(); + + Pos(int x, int y, int z); + + Pos(Pos *position); + + //@Override + //public boolean equals(Object other) + bool equals(void *other); + + int hashCode(); + + int compareTo(Pos *pos); + + Pos *offset(int x, int y, int z); + + void set(int x, int y, int z); + + void set(Pos *pos); + + Pos *above(); + + Pos *above(int steps); + + Pos *below(); + + Pos *below(int steps); + + Pos *north(); + + Pos *north(int steps); + + Pos *south(); + + Pos *south(int steps); + + Pos *west(); + + Pos *west(int steps); + + Pos *east(); + + Pos *east(int steps); + + void move(int x, int y, int z); + + void move(Pos pos); + + void moveX(int steps); + + void moveY(int steps); + + void moveZ(int steps); + + void moveUp(int steps); + + void moveUp(); + + void moveDown(int steps); + + void moveDown(); + + void moveEast(int steps); + + void moveEast(); + + void moveWest(int steps); + + void moveWest(); + + void moveNorth(int steps); + + void moveNorth(); + + void moveSouth(int steps); + + void moveSouth(); + + double dist(int x, int y, int z); + + double dist(Pos *pos); + float distSqr(int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/PotatoTile.cpp b/Minecraft.World/PotatoTile.cpp new file mode 100644 index 00000000..27ca1984 --- /dev/null +++ b/Minecraft.World/PotatoTile.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" +#include "PotatoTile.h" + +PotatoTile::PotatoTile(int id) : CropTile(id) +{ +} + +Icon *PotatoTile::getTexture(int face, int data) +{ + if (data < 7) + { + if (data == 6) + { + data = 5; + } + return icons[data >> 1]; + } + else + { + return icons[3]; + } +} + +int PotatoTile::getBaseSeedId() +{ + return Item::potato_Id; +} + +int PotatoTile::getBasePlantId() +{ + return Item::potato_Id; +} + +void PotatoTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus) +{ + CropTile::spawnResources(level, x, y, z, data, odds, playerBonus); + + if (level->isClientSide) + { + return; + } + if (data >= 7) + { + if (level->random->nextInt(50) == 0) + { + popResource(level, x, y, z, shared_ptr(new ItemInstance(Item::potatoPoisonous))); + } + } +} + +void PotatoTile::registerIcons(IconRegister *iconRegister) +{ + for (int i = 0; i < 4; i++) + { + icons[i] = iconRegister->registerIcon(L"potatoes_" + _toString(i)); + } +} \ No newline at end of file diff --git a/Minecraft.World/PotatoTile.h b/Minecraft.World/PotatoTile.h new file mode 100644 index 00000000..8ce5960a --- /dev/null +++ b/Minecraft.World/PotatoTile.h @@ -0,0 +1,23 @@ +#pragma once + +#include "CropTile.h" + +class PotatoTile : public CropTile +{ + friend class ChunkRebuildData; +private: + Icon *icons[4]; + +public: + PotatoTile(int id); + + Icon *getTexture(int face, int data); + +protected: + int getBaseSeedId(); + int getBasePlantId(); + +public: + void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/PotionBrewing.cpp b/Minecraft.World/PotionBrewing.cpp new file mode 100644 index 00000000..eb973655 --- /dev/null +++ b/Minecraft.World/PotionBrewing.cpp @@ -0,0 +1,910 @@ +#include "stdafx.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "net.minecraft.world.effect.h" +#include "JavaMath.h" +#include "SharedConstants.h" +#include "PotionBrewing.h" + +const int PotionBrewing::DEFAULT_APPEARANCES[] = +{ + IDS_POTION_PREFIX_MUNDANE, + IDS_POTION_PREFIX_UNINTERESTING, + IDS_POTION_PREFIX_BLAND, + IDS_POTION_PREFIX_CLEAR, + IDS_POTION_PREFIX_MILKY, + IDS_POTION_PREFIX_DIFFUSE, + IDS_POTION_PREFIX_ARTLESS, + IDS_POTION_PREFIX_THIN, + IDS_POTION_PREFIX_AWKWARD, + IDS_POTION_PREFIX_FLAT, + IDS_POTION_PREFIX_BULKY, + IDS_POTION_PREFIX_BUNGLING, + IDS_POTION_PREFIX_BUTTERED, + IDS_POTION_PREFIX_SMOOTH, + IDS_POTION_PREFIX_SUAVE, + IDS_POTION_PREFIX_DEBONAIR, + IDS_POTION_PREFIX_THICK, + IDS_POTION_PREFIX_ELEGANT, + IDS_POTION_PREFIX_FANCY, + IDS_POTION_PREFIX_CHARMING, + IDS_POTION_PREFIX_DASHING, + IDS_POTION_PREFIX_REFINED, + IDS_POTION_PREFIX_CORDIAL, + IDS_POTION_PREFIX_SPARKLING, + IDS_POTION_PREFIX_POTENT, + IDS_POTION_PREFIX_FOUL, + IDS_POTION_PREFIX_ODORLESS, + IDS_POTION_PREFIX_RANK, + IDS_POTION_PREFIX_HARSH, + IDS_POTION_PREFIX_ACRID, + IDS_POTION_PREFIX_GROSS, + IDS_POTION_PREFIX_STINKY, +}; + +// bit 4 is the "enabler," lit by nether seeds + + // bits 0-3 are effect identifiers + // 0001 - regeneration + // 0010 - move speed + // 0011 - fire resist + // 0100 - poison + // 0101 - heal + // 0110 - night vision + // 0111 - invisibility + // 1000 - weakness + // 1001 - damage boost + // 1010 - move slow + // 1011 - + // 1100 - harm + // 1101 - + // 1110 - + // 1111 - + +/* 4J-JEV: Fix for #81196, + * Bit 13 is always set in functional potions. + * Therefore if bit 13 is on, don't use netherwart! + * Added "&!13" which requires that bit 13 be turned off. + */ +const wstring PotionBrewing::MOD_NETHERWART = L"+4&!13"; // L"+4" + +#if _SIMPLIFIED_BREWING +const wstring PotionBrewing::MOD_WATER = L""; +const wstring PotionBrewing::MOD_SUGAR = L"-0+1-2-3&4-4+13"; +const wstring PotionBrewing::MOD_GHASTTEARS = L"+0-1-2-3&4-4+13"; +const wstring PotionBrewing::MOD_SPIDEREYE = L"-0-1+2-3&4-4+13"; +const wstring PotionBrewing::MOD_FERMENTEDEYE = L"-0+3-4+13"; +const wstring PotionBrewing::MOD_SPECKLEDMELON = L"+0-1+2-3&4-4+13"; +const wstring PotionBrewing::MOD_BLAZEPOWDER = L"+0-1-2+3&4-4+13"; +const wstring PotionBrewing::MOD_GOLDENCARROT = L"-0+1+2-3+13&4-4"; +const wstring PotionBrewing::MOD_MAGMACREAM = L"+0+1-2-3&4-4+13"; +const wstring PotionBrewing::MOD_REDSTONE = L"-5+6-7"; // redstone increases duration +const wstring PotionBrewing::MOD_GLOWSTONE = L"+5-6-7"; // glowstone increases amplification +// 4J Stu - Don't require bit 13 to be set. We don't use it in the creative menu. Side effect is you can make a (virtually useless) Splash Mundane potion with water bottle and gunpowder +const wstring PotionBrewing::MOD_GUNPOWDER = L"+14";//&13-13"; // gunpowder makes them throwable! // gunpowder requires 13 and sets 14 +#else +const wstring PotionBrewing::MOD_WATER = L"-1-3-5-7-9-11-13"; +const wstring PotionBrewing::MOD_SUGAR = L"+0"; +const wstring PotionBrewing::MOD_GHASTTEARS = L"+11"; +const wstring PotionBrewing::MOD_SPIDEREYE = L"+10+7+5"; +const wstring PotionBrewing::MOD_FERMENTEDEYE = L"+14+9"; +const wstring PotionBrewing::MOD_SPECKLEDMELON = L""; +const wstring PotionBrewing::MOD_BLAZEPOWDER = L"+14"; +const wstring PotionBrewing::MOD_MAGMACREAM = L"+14+6+1"; +const wstring PotionBrewing::MOD_REDSTONE = L""; // redstone increases duration +const wstring PotionBrewing::MOD_GLOWSTONE = L""; // glowstone increases amplification +const wstring PotionBrewing::MOD_GUNPOWDER = L""; // gunpowder makes them throwable! // gunpowder requires 13 and sets 14 +#endif + +PotionBrewing::intStringMap PotionBrewing::potionEffectDuration; +PotionBrewing::intStringMap PotionBrewing::potionEffectAmplifier; + +unordered_map PotionBrewing::cachedColors; + +void PotionBrewing::staticCtor() +{ +#if _SIMPLIFIED_BREWING + potionEffectDuration.insert(intStringMap::value_type( MobEffect::regeneration->getId(), L"0 & !1 & !2 & !3 & 0+6" )); + potionEffectDuration.insert(intStringMap::value_type( MobEffect::movementSpeed->getId(), L"!0 & 1 & !2 & !3 & 1+6" )); + potionEffectDuration.insert(intStringMap::value_type( MobEffect::fireResistance->getId(), L"0 & 1 & !2 & !3 & 0+6" )); + potionEffectDuration.insert(intStringMap::value_type( MobEffect::heal->getId(), L"0 & !1 & 2 & !3" )); + potionEffectDuration.insert(intStringMap::value_type( MobEffect::poison->getId(), L"!0 & !1 & 2 & !3 & 2+6" )); + potionEffectDuration.insert(intStringMap::value_type( MobEffect::weakness->getId(), L"!0 & !1 & !2 & 3 & 3+6" )); + potionEffectDuration.insert(intStringMap::value_type( MobEffect::harm->getId(), L"!0 & !1 & 2 & 3" )); + potionEffectDuration.insert(intStringMap::value_type( MobEffect::movementSlowdown->getId(), L"!0 & 1 & !2 & 3 & 3+6" )); + potionEffectDuration.insert(intStringMap::value_type( MobEffect::damageBoost->getId(), L"0 & !1 & !2 & 3 & 3+6" )); + potionEffectDuration.insert(intStringMap::value_type( MobEffect::nightVision->getId(), L"!0 & 1 & 2 & !3 & 2+6" )); + potionEffectDuration.insert(intStringMap::value_type( MobEffect::invisibility->getId(), L"!0 & 1 & 2 & 3 & 2+6" )); + + // glowstone increases amplification + potionEffectAmplifier.insert(intStringMap::value_type( MobEffect::movementSpeed->getId(), L"5" )); + potionEffectAmplifier.insert(intStringMap::value_type( MobEffect::digSpeed->getId(), L"5" )); + potionEffectAmplifier.insert(intStringMap::value_type( MobEffect::damageBoost->getId(), L"5" )); + potionEffectAmplifier.insert(intStringMap::value_type( MobEffect::regeneration->getId(), L"5" )); + potionEffectAmplifier.insert(intStringMap::value_type( MobEffect::harm->getId(), L"5" )); + potionEffectAmplifier.insert(intStringMap::value_type( MobEffect::heal->getId(), L"5" )); + potionEffectAmplifier.insert(intStringMap::value_type( MobEffect::damageResistance->getId(), L"5" )); + potionEffectAmplifier.insert(intStringMap::value_type( MobEffect::poison->getId(), L"5" )); +#else + potionEffectDuration.put(movementSpeed.getId(), "!10 & !4 & 5*2+0 & >1 | !7 & !4 & 5*2+0 & >1"); + potionEffectDuration.put(movementSlowdown.getId(), "10 & 7 & !4 & 7+5+1-0"); + potionEffectDuration.put(digSpeed.getId(), "2 & 12+2+6-1-7 & <8"); + potionEffectDuration.put(digSlowdown.getId(), "!2 & !1*2-9 & 14-5"); + potionEffectDuration.put(damageBoost.getId(), "9 & 3 & 9+4+5 & <11"); + potionEffectDuration.put(weakness.getId(), "=1>5>7>9+3-7-2-11 & !9 & !0"); + potionEffectDuration.put(heal.getId(), "11 & <6"); + potionEffectDuration.put(harm.getId(), "!11 & 1 & 10 & !7"); + potionEffectDuration.put(jump.getId(), "8 & 2+0 & <5"); + potionEffectDuration.put(confusion.getId(), "8*2-!7+4-11 & !2 | 13 & 11 & 2*3-1-5"); + potionEffectDuration.put(regeneration.getId(), "!14 & 13*3-!0-!5-8"); + potionEffectDuration.put(damageResistance.getId(), "10 & 4 & 10+5+6 & <9"); + potionEffectDuration.put(fireResistance.getId(), "14 & !5 & 6-!1 & 14+13+12"); + potionEffectDuration.put(waterBreathing.getId(), "0+1+12 & !6 & 10 & !11 & !13"); + potionEffectDuration.put(invisibility.getId(), "2+5+13-0-4 & !7 & !1 & >5"); + potionEffectDuration.put(blindness.getId(), "9 & !1 & !5 & !3 & =3"); + potionEffectDuration.put(nightVision.getId(), "8*2-!7 & 5 & !0 & >3"); + potionEffectDuration.put(hunger.getId(), ">4>6>8-3-8+2"); + potionEffectDuration.put(poison.getId(), "12+9 & !13 & !0"); + + potionEffectAmplifier.put(movementSpeed.getId(), "7+!3-!1"); + potionEffectAmplifier.put(digSpeed.getId(), "1+0-!11"); + potionEffectAmplifier.put(damageBoost.getId(), "2+7-!12"); + potionEffectAmplifier.put(heal.getId(), "11+!0-!1-!14"); + potionEffectAmplifier.put(harm.getId(), "!11-!14+!0-!1"); + potionEffectAmplifier.put(damageResistance.getId(), "12-!2"); + potionEffectAmplifier.put(poison.getId(), "14>5"); +#endif +} + +bool PotionBrewing::isWrappedLit(int brew, int position) +{ + return (brew & (1 << (position % NUM_BITS))) != 0; +} + +bool PotionBrewing::isLit(int brew, int position) +{ + return (brew & (1 << position)) != 0; +} + +int PotionBrewing::isBit(int brew, int position) +{ + return isLit(brew, position) ? 1 : 0; +} + +int PotionBrewing::isNotBit(int brew, int position) +{ + return isLit(brew, position) ? 0 : 1; +} + +int PotionBrewing::getAppearanceValue(int brew) +{ + return valueOf(brew, 5, 4, 3, 2, 1); +} + +int PotionBrewing::getColorValue(vector *effects) +{ + ColourTable *colourTable = Minecraft::GetInstance()->getColourTable(); + + int baseColor = colourTable->getColor( eMinecraftColour_Potion_BaseColour ); + + if (effects == NULL || effects->empty()) + { + return baseColor; + } + + float red = 0; + float green = 0; + float blue = 0; + float count = 0; + + //for (MobEffectInstance effect : effects){ + for(AUTO_VAR(it, effects->begin()); it != effects->end(); ++it) + { + MobEffectInstance *effect = *it; + int potionColor = colourTable->getColor( MobEffect::effects[effect->getId()]->getColor() ); + + for (int potency = 0; potency <= effect->getAmplifier(); potency++) + { + red += (float) ((potionColor >> 16) & 0xff) / 255.0f; + green += (float) ((potionColor >> 8) & 0xff) / 255.0f; + blue += (float) ((potionColor >> 0) & 0xff) / 255.0f; + count++; + } + } + + red = (red / count) * 255.0f; + green = (green / count) * 255.0f; + blue = (blue / count) * 255.0f; + + return ((int) red) << 16 | ((int) green) << 8 | ((int) blue); +} + +int PotionBrewing::getColorValue(int brew, bool includeDisabledEffects) +{ + if (!includeDisabledEffects) + { + AUTO_VAR(colIt, cachedColors.find(brew)); + if (colIt != cachedColors.end()) + { + return colIt->second;//cachedColors.get(brew); + } + vector *effects = getEffects(brew, false); + int color = getColorValue(effects); + if(effects != NULL) + { + for(AUTO_VAR(it, effects->begin()); it != effects->end(); ++it) + { + MobEffectInstance *effect = *it; + delete effect; + } + delete effects; + } + cachedColors.insert( std::pair(brew, color) ); + return color; + } + + return getColorValue(getEffects(brew, includeDisabledEffects)); +} + +int PotionBrewing::getSmellValue(int brew) +{ + return valueOf(brew, 12, 11, 6, 4, 0); +} + +int PotionBrewing::getAppearanceName(int brew) +{ + int value = getAppearanceValue(brew); + return DEFAULT_APPEARANCES[value]; +} + +int PotionBrewing::constructParsedValue(bool isNot, bool hasMultiplier, bool isNeg, int countCompare, int valuePart, int multiplierPart, int brew) +{ + int value = 0; + if (isNot) + { + value = isNotBit(brew, valuePart); + } +#if !(_SIMPLIFIED_BREWING) + else if (countCompare != NO_COUNT) // Never true for simplified brewing + { + if (countCompare == EQUAL_COUNT && countOnes(brew) == valuePart) + { + value = 1; + } + else if (countCompare == GREATER_COUNT && countOnes(brew) > valuePart) + { + value = 1; + } + else if (countCompare == LESS_COUNT && countOnes(brew) < valuePart) + { + value = 1; + } + } +#endif + else + { + value = isBit(brew, valuePart); + } +#if !(_SIMPLIFIED_BREWING) + if (hasMultiplier) // Always false for simplified brewing + { + value *= multiplierPart; + } +#endif + if (isNeg) + { + value *= -1; + } + return value; +} + +int PotionBrewing::countOnes(int brew) +{ + int c = 0; + for (; brew > 0; c++) + { + brew &= brew - 1; + } + return c; +} + +#if _SIMPLIFIED_BREWING +// 4J Stu - Trimmed this function to remove all the unused features for simplified brewing +int PotionBrewing::parseEffectFormulaValue(const wstring &definition, int start, int end, int brew) +{ + if (start >= definition.length() || end < 0 || start >= end) + { + return 0; + } + + // split by and + int andIndex = (int)definition.find_first_of(L'&', start); + if (andIndex >= 0 && andIndex < end) + { + int leftSide = parseEffectFormulaValue(definition, start, andIndex - 1, brew); + if (leftSide <= 0) + { + return 0; + } + + int rightSide = parseEffectFormulaValue(definition, andIndex + 1, end, brew); + if (rightSide <= 0) + { + return 0; + } + + if (leftSide > rightSide) + { + return leftSide; + } + return rightSide; + } + + bool hasMultiplier = false; + bool hasValue = false; + bool isNot = false; + bool isNeg = false; + int bitCount = NO_COUNT; + int valuePart = 0; + int multiplierPart = 0; + int result = 0; + for (int i = start; i < end; i++) + { + + char current = definition.at(i); + if (current >= L'0' && current <= L'9') + { + valuePart *= 10; + valuePart += (int) (current - L'0'); + hasValue = true; + } + else if (current == L'!') + { + if (hasValue) + { + result += constructParsedValue(isNot, hasMultiplier, isNeg, bitCount, valuePart, multiplierPart, brew); + hasValue = isNeg = isNot = false; + valuePart = 0; + } + + isNot = true; + } + else if (current == L'-') + { + if (hasValue) + { + result += constructParsedValue(isNot, hasMultiplier, isNeg, bitCount, valuePart, multiplierPart, brew); + hasValue = isNeg = isNot = false; + valuePart = 0; + } + + isNeg = true; + } + else if (current == L'+') + { + if (hasValue) + { + result += constructParsedValue(isNot, hasMultiplier, isNeg, bitCount, valuePart, multiplierPart, brew); + hasValue = isNeg = isNot = false; + valuePart = 0; + } + } + } + if (hasValue) + { + result += constructParsedValue(isNot, hasMultiplier, isNeg, bitCount, valuePart, multiplierPart, brew); + } + + return result; +} +#else +int PotionBrewing::parseEffectFormulaValue(const wstring &definition, int start, int end, int brew) +{ + if (start >= definition.length() || end < 0 || start >= end) + { + return 0; + } + + // split by or + int orIndex = definition.find_first_of(L'|', start); + if (orIndex >= 0 && orIndex < end) + { + int leftSide = parseEffectFormulaValue(definition, start, orIndex - 1, brew); + if (leftSide > 0) + { + return leftSide; + } + + int rightSide = parseEffectFormulaValue(definition, orIndex + 1, end, brew); + if (rightSide > 0) + { + return rightSide; + } + return 0; + } + // split by and + int andIndex = definition.find_first_of(L'&', start); + if (andIndex >= 0 && andIndex < end) + { + int leftSide = parseEffectFormulaValue(definition, start, andIndex - 1, brew); + if (leftSide <= 0) + { + return 0; + } + + int rightSide = parseEffectFormulaValue(definition, andIndex + 1, end, brew); + if (rightSide <= 0) + { + return 0; + } + + if (leftSide > rightSide) + { + return leftSide; + } + return rightSide; + } + + bool isMultiplier = false; + bool hasMultiplier = false; + bool hasValue = false; + bool isNot = false; + bool isNeg = false; + int bitCount = NO_COUNT; + int valuePart = 0; + int multiplierPart = 0; + int result = 0; + for (int i = start; i < end; i++) + { + + char current = definition.at(i); + if (current >= L'0' && current <= L'9') + { + if (isMultiplier) + { + multiplierPart = (int) (current - L'0'); + hasMultiplier = true; + } + else + { + valuePart *= 10; + valuePart += (int) (current - L'0'); + hasValue = true; + } + } + else if (current == L'*') + { + isMultiplier = true; + } + else if (current == L'!') + { + if (hasValue) + { + result += constructParsedValue(isNot, hasMultiplier, isNeg, bitCount, valuePart, multiplierPart, brew); + hasValue = hasMultiplier = isMultiplier = isNeg = isNot = false; + valuePart = multiplierPart = 0; + bitCount = NO_COUNT; + } + + isNot = true; + } + else if (current == L'-') + { + if (hasValue) + { + result += constructParsedValue(isNot, hasMultiplier, isNeg, bitCount, valuePart, multiplierPart, brew); + hasValue = hasMultiplier = isMultiplier = isNeg = isNot = false; + valuePart = multiplierPart = 0; + bitCount = NO_COUNT; + } + + isNeg = true; + } + else if (current == L'=' || current == L'<' || current == L'>') + { + if (hasValue) + { + result += constructParsedValue(isNot, hasMultiplier, isNeg, bitCount, valuePart, multiplierPart, brew); + hasValue = hasMultiplier = isMultiplier = isNeg = isNot = false; + valuePart = multiplierPart = 0; + bitCount = NO_COUNT; + } + + if (current == L'=') + { + bitCount = EQUAL_COUNT; + } + else if (current == L'<') + { + bitCount = LESS_COUNT; + } + else if (current == L'>') + { + bitCount = GREATER_COUNT; + } + } + else if (current == L'+') + { + if (hasValue) + { + result += constructParsedValue(isNot, hasMultiplier, isNeg, bitCount, valuePart, multiplierPart, brew); + hasValue = hasMultiplier = isMultiplier = isNeg = isNot = false; + valuePart = multiplierPart = 0; + bitCount = NO_COUNT; + } + } + } + if (hasValue) + { + result += constructParsedValue(isNot, hasMultiplier, isNeg, bitCount, valuePart, multiplierPart, brew); + } + + return result; +} +#endif + +vector *PotionBrewing::getEffects(int brew, bool includeDisabledEffects) +{ + vector *list = NULL; + + //for (MobEffect effect : MobEffect.effects) + for(unsigned int i = 0; i < MobEffect::NUM_EFFECTS; ++i) + { + MobEffect *effect = MobEffect::effects[i]; + if (effect == NULL || (effect->isDisabled() && !includeDisabledEffects)) + { + continue; + } + //wstring durationString = potionEffectDuration.get(effect->getId()); + AUTO_VAR(effIt, potionEffectDuration.find(effect->getId())); + if ( effIt == potionEffectDuration.end() ) + { + continue; + } + wstring durationString = effIt->second; + + int duration = parseEffectFormulaValue(durationString, 0, (int)durationString.length(), brew); + if (duration > 0) + { + int amplifier = 0; + AUTO_VAR(ampIt, potionEffectAmplifier.find(effect->getId())); + if (ampIt != potionEffectAmplifier.end()) + { + wstring amplifierString = ampIt->second; + amplifier = parseEffectFormulaValue(amplifierString, 0, (int)amplifierString.length(), brew); + if (amplifier < 0) + { + amplifier = 0; + } + } + + if (effect->isInstantenous()) + { + duration = 1; + } + else + { + // 3, 8, 13, 18.. minutes + duration = (SharedConstants::TICKS_PER_SECOND * 60) * (duration * 3 + (duration - 1) * 2); + duration >>= amplifier; + duration = (int) Math::round((double) duration * effect->getDurationModifier()); + + if ((brew & THROWABLE_MASK) != 0) + { + duration = (int) Math::round((double) duration * .75 + .5); + } + } + + if (list == NULL) + { + list = new vector(); + } + list->push_back(new MobEffectInstance(effect->getId(), duration, amplifier)); + } + } + + return list; +} + +#if !(_SIMPLIFIED_BREWING) +int PotionBrewing::boil(int brew) +{ + if ((brew & 1) == 0) + { + return brew; + } + + // save highest bit + int savedBit = NUM_BITS - 1; + while ((brew & (1 << savedBit)) == 0 && savedBit >= 0) + { + savedBit--; + } + // it's not possible to boil if there are no "empty slots" in front of + // the last bit + if (savedBit < 2 || (brew & (1 << (savedBit - 1))) != 0) + { + return brew; + } + if (savedBit >= 0) + { + brew &= ~(1 << savedBit); + } + + brew <<= 1; + + if (savedBit >= 0) + { + brew |= (1 << savedBit); + brew |= (1 << (savedBit - 1)); + } + + return brew & BREW_MASK; +} + +int PotionBrewing::shake(int brew) +{ + + // save highest bit + int savedBit = NUM_BITS - 1; + while ((brew & (1 << savedBit)) == 0 && savedBit >= 0) + { + savedBit--; + } + if (savedBit >= 0) + { + brew &= ~(1 << savedBit); + } + + int currentResult = 0; + int nextResult = brew; + + while (nextResult != currentResult) + { + nextResult = brew; + currentResult = 0; + // evaluate each bit + for (int bit = 0; bit < NUM_BITS; bit++) + { + + bool on = isWrappedLit(brew, bit); + if (on) + { + if (!isWrappedLit(brew, bit + 1) && isWrappedLit(brew, bit + 2)) + { + on = false; + } + else if (!isWrappedLit(brew, bit - 1) && isWrappedLit(brew, bit - 2)) + { + on = false; + } + } + else + { + // turn on if both neighbors are on + on = isWrappedLit(brew, bit - 1) && isWrappedLit(brew, bit + 1); + } + if (on) + { + currentResult |= (1 << bit); + } + } + brew = currentResult; + } + + if (savedBit >= 0) + { + currentResult |= (1 << savedBit); + } + + return currentResult & BREW_MASK; +} + +int PotionBrewing::stirr(int brew) +{ + if ((brew & 1) != 0) + { + brew = boil(brew); + } + return shake(brew); +} +#endif + +int PotionBrewing::applyBrewBit(int currentBrew, int bit, bool isNeg, bool isNot, bool isRequired) +{ + if (isRequired) + { + // 4J-JEV: I wanted to be able to specify that a + // bit is required to be false. + if (isLit(currentBrew, bit) == isNot) + { + return 0; + } + } + else if (isNeg) + { + currentBrew &= ~(1 << bit); + } + else if (isNot) + { + if ((currentBrew & (1 << bit)) == 0) + { + currentBrew |= (1 << bit); + } + else + { + currentBrew &= ~(1 << bit); + } + } + else + { + currentBrew |= (1 << bit); + } + return currentBrew; +} + +int PotionBrewing::applyBrew(int currentBrew, const wstring &formula) +{ + + int start = 0; + int end = (int)formula.length(); + + bool hasValue = false; + bool isNot = false; + bool isNeg = false; + bool isRequired = false; + int valuePart = 0; + for (int i = start; i < end; i++) + { + char current = formula.at(i); + if (current >= L'0' && current <= L'9') + { + valuePart *= 10; + valuePart += (int) (current - L'0'); + hasValue = true; + } + else if (current == L'!') + { + if (hasValue) + { + currentBrew = applyBrewBit(currentBrew, valuePart, isNeg, isNot, isRequired); + hasValue = isNeg = isNot = isRequired = false; + valuePart = 0; + } + + isNot = true; + } + else if (current == L'-') + { + if (hasValue) + { + currentBrew = applyBrewBit(currentBrew, valuePart, isNeg, isNot, isRequired); + hasValue = isNeg = isNot = isRequired = false; + valuePart = 0; + } + + isNeg = true; + } + else if (current == L'+') + { + if (hasValue) + { + currentBrew = applyBrewBit(currentBrew, valuePart, isNeg, isNot, isRequired); + hasValue = isNeg = isNot = isRequired = false; + valuePart = 0; + } + } + else if (current == L'&') + { + if (hasValue) + { + currentBrew = applyBrewBit(currentBrew, valuePart, isNeg, isNot, isRequired); + hasValue = isNeg = isNot = isRequired = false; + valuePart = 0; + } + isRequired = true; + } + } + if (hasValue) + { + currentBrew = applyBrewBit(currentBrew, valuePart, isNeg, isNot, isRequired); + } + + return currentBrew & BREW_MASK; +} + +int PotionBrewing::setBit(int brew, int position, bool onOff) +{ + if (onOff) + { + return brew | (1 << position); + } + return brew & ~(1 << position); +} + +int PotionBrewing::valueOf(int brew, int p1, int p2, int p3, int p4) +{ + return ((isLit(brew, p1) ? 0x08 : 0) | (isLit(brew, p2) ? 0x04 : 0) | (isLit(brew, p3) ? 0x02 : 0) | (isLit(brew, p4) ? 0x01 : 0)); +} + +int PotionBrewing::valueOf(int brew, int p1, int p2, int p3, int p4, int p5) +{ + return (isLit(brew, p1) ? 0x10 : 0) | (isLit(brew, p2) ? 0x08 : 0) | (isLit(brew, p3) ? 0x04 : 0) | (isLit(brew, p4) ? 0x02 : 0) | (isLit(brew, p5) ? 0x01 : 0); +} + +wstring PotionBrewing::toString(int brew) +{ + wstring string; + + int bit = NUM_BITS - 1; + while (bit >= 0) + { + if ((brew & (1 << bit)) != 0) + { + string.append(L"O"); + } + else + { + string.append(L"x"); + } + bit--; + } + + return string; +} + +//void main(String[] args) +//{ + +// HashMap existingCombinations = new HashMap(); +// HashMap distinctCombinations = new HashMap(); +// int noEffects = 0; +// for (int brew = 0; brew <= BREW_MASK; brew++) { +// List effects = PotionBrewing.getEffects(brew, true); +// if (effects != null) { + +// { +// StringBuilder builder = new StringBuilder(); +// for (MobEffectInstance effect : effects) { +// builder.append(effect.toString()); +// builder.append(" "); +// } +// String string = builder.toString(); +// Integer count = existingCombinations.get(string); +// if (count != null) { +// count++; +// } else { +// count = 1; +// } +// existingCombinations.put(string, count); +// } +// { +// StringBuilder builder = new StringBuilder(); +// for (MobEffectInstance effect : effects) { +// builder.append(effect.getDescriptionId()); +// builder.append(" "); +// } +// String string = builder.toString(); +// Integer count = distinctCombinations.get(string); +// if (count != null) { +// count++; +// } else { +// count = 1; +// } +// distinctCombinations.put(string, count); +// } +// } else { +// noEffects++; +// } +// } + +// for (String combination : existingCombinations.keySet()) { +// Integer count = existingCombinations.get(combination); +// if (count > 20) { +// System.out.println(combination + ": " + count); +// } +// } + +// System.out.println("Combination with no effects: " + noEffects + " (" + ((double) noEffects / BREW_MASK * 100.0) + " %)"); +// System.out.println("Unique combinations: " + existingCombinations.size()); +// System.out.println("Distinct combinations: " + distinctCombinations.size()); +//} \ No newline at end of file diff --git a/Minecraft.World/PotionBrewing.h b/Minecraft.World/PotionBrewing.h new file mode 100644 index 00000000..68679293 --- /dev/null +++ b/Minecraft.World/PotionBrewing.h @@ -0,0 +1,101 @@ +#pragma once +using namespace std; +class MobEffectInstance; + +class PotionBrewing +{ +public: + static const bool SIMPLIFIED_BREWING = true; + // 4J Stu - Made #define so we can use it to select const initialisation +#define _SIMPLIFIED_BREWING 1 + + static const int BREWING_TIME_SECONDS = 20; + + static const int THROWABLE_BIT = 14; + static const int THROWABLE_MASK = (1 << THROWABLE_BIT); + + static const wstring MOD_WATER; + static const wstring MOD_SUGAR; + static const wstring MOD_GHASTTEARS; + static const wstring MOD_SPIDEREYE; + static const wstring MOD_FERMENTEDEYE; + static const wstring MOD_SPECKLEDMELON; + static const wstring MOD_BLAZEPOWDER; + static const wstring MOD_MAGMACREAM; + static const wstring MOD_REDSTONE; + static const wstring MOD_GLOWSTONE; + static const wstring MOD_NETHERWART; + static const wstring MOD_GUNPOWDER; + static const wstring MOD_GOLDENCARROT; + +private: + typedef unordered_map intStringMap; + static intStringMap potionEffectDuration; + static intStringMap potionEffectAmplifier; + +public: + static void staticCtor(); + + static const int NUM_BITS = 15; + + // 4J Stu - Made public + static const int BREW_MASK = 0x7fff; +private: + static const int TOP_BIT = 0x4000; + + static bool isWrappedLit(int brew, int position); + +public: + static bool isLit(int brew, int position); + +private: + static int isBit(int brew, int position); + static int isNotBit(int brew, int position); + +public: + static int getAppearanceValue(int brew); + static int getColorValue(vector *effects); + +private: + static unordered_map cachedColors; + +public: + static int getColorValue(int brew, bool includeDisabledEffects); + static int getSmellValue(int brew); + +private: + static const int DEFAULT_APPEARANCES[]; + +public: + static int getAppearanceName(int brew); + +private: + static const int NO_COUNT = -1; + static const int EQUAL_COUNT = 0; + static const int GREATER_COUNT = 1; + static const int LESS_COUNT = 2; + + static int constructParsedValue(bool isNot, bool hasMultiplier, bool isNeg, int countCompare, int valuePart, int multiplierPart, int brew); + static int countOnes(int brew); + static int parseEffectFormulaValue(const wstring &definition, int start, int end, int brew); + +public: + static vector *getEffects(int brew, bool includeDisabledEffects); + +#if !(_SIMPLIFIED_BREWING) + static int boil(int brew); + static int shake(int brew); + static int stirr(int brew); +#endif + +private: + static int applyBrewBit(int currentBrew, int bit, bool isNeg, bool isNot, bool isRequired); + +public: + static int applyBrew(int currentBrew, const wstring &formula); + static int setBit(int brew, int position, bool onOff); + static int valueOf(int brew, int p1, int p2, int p3, int p4); + static int valueOf(int brew, int p1, int p2, int p3, int p4, int p5); + static wstring toString(int brew); + //static void main(String[] args); +}; \ No newline at end of file diff --git a/Minecraft.World/PotionItem.cpp b/Minecraft.World/PotionItem.cpp new file mode 100644 index 00000000..643e9661 --- /dev/null +++ b/Minecraft.World/PotionItem.cpp @@ -0,0 +1,372 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.alchemy.h" +#include "net.minecraft.world.effect.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.h" +#include "MobEffectInstance.h" +#include "StringHelpers.h" +#include "SharedConstants.h" +#include "PotionItem.h" +#include "SoundTypes.h" + +const wstring PotionItem::DEFAULT_ICON = L"potion"; +const wstring PotionItem::THROWABLE_ICON = L"potion_splash"; +const wstring PotionItem::CONTENTS_ICON = L"potion_contents"; + +// 4J Added +vector > PotionItem::s_uniquePotionValues; + +PotionItem::PotionItem(int id) : Item(id) +{ + setMaxStackSize(1); + setStackedByData(true); + setMaxDamage(0); + + iconThrowable = NULL; + iconDrinkable = NULL; + iconOverlay = NULL; +} + +vector *PotionItem::getMobEffects(shared_ptr potion) +{ + return getMobEffects(potion->getAuxValue()); +} + +vector *PotionItem::getMobEffects(int auxValue) +{ + vector *effects = NULL; + AUTO_VAR(it, cachedMobEffects.find(auxValue)); + if(it != cachedMobEffects.end()) effects = it->second; + if (effects == NULL) + { + effects = PotionBrewing::getEffects(auxValue, false); + if(effects != NULL) cachedMobEffects.insert( std::pair *>(auxValue, effects) ); + } + return effects; +} + +shared_ptr PotionItem::useTimeDepleted(shared_ptr instance, Level *level, shared_ptr player) +{ + if (!player->abilities.instabuild) instance->count--; + + if (!level->isClientSide) + { + vector *effects = getMobEffects(instance); + if (effects != NULL) + { + //for (MobEffectInstance effect : effects) + for(AUTO_VAR(it, effects->begin()); it != effects->end(); ++it) + { + player->addEffect(new MobEffectInstance(*it)); + } + } + } + if (!player->abilities.instabuild) + { + if (instance->count <= 0) + { + return shared_ptr( new ItemInstance(Item::glassBottle) ); + } + else + { + player->inventory->add( shared_ptr( new ItemInstance(Item::glassBottle) ) ); + } + } + + return instance; +} + +int PotionItem::getUseDuration(shared_ptr itemInstance) +{ + return DRINK_DURATION; +} + +UseAnim PotionItem::getUseAnimation(shared_ptr itemInstance) +{ + return UseAnim_drink; +} + +bool PotionItem::TestUse(Level *level, shared_ptr player) +{ + return true; +} + +shared_ptr PotionItem::use(shared_ptr instance, Level *level, shared_ptr player) +{ + if (isThrowable(instance->getAuxValue())) + { + if (!player->abilities.instabuild) instance->count--; + level->playSound(player, eSoundType_RANDOM_BOW, 0.5f, 0.4f / (random->nextFloat() * 0.4f + 0.8f)); + if (!level->isClientSide) level->addEntity(shared_ptr( new ThrownPotion(level, player, instance->getAuxValue()) )); + return instance; + } + player->startUsingItem(instance, getUseDuration(instance)); + return instance; +} + +bool PotionItem::useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + return false; +} + +Icon *PotionItem::getIcon(int auxValue) +{ + if (isThrowable(auxValue)) + { + return iconThrowable; + } + return iconDrinkable; +} + +Icon *PotionItem::getLayerIcon(int auxValue, int spriteLayer) +{ + if (spriteLayer == 0) + { + return iconOverlay; + } + return Item::getLayerIcon(auxValue, spriteLayer); +} + +bool PotionItem::isThrowable(int auxValue) +{ + return ((auxValue & PotionBrewing::THROWABLE_MASK) != 0); +} + +int PotionItem::getColor(int data) +{ + return PotionBrewing::getColorValue(data, false); +} + +int PotionItem::getColor(shared_ptr item, int spriteLayer) +{ + if (spriteLayer > 0) + { + return 0xffffff; + } + return PotionBrewing::getColorValue(item->getAuxValue(), false); +} + +bool PotionItem::hasMultipleSpriteLayers() +{ + return true; +} + +bool PotionItem::hasInstantenousEffects(int itemAuxValue) +{ + vector *mobEffects = getMobEffects(itemAuxValue); + if (mobEffects == NULL || mobEffects->empty()) + { + return false; + } + //for (MobEffectInstance effect : mobEffects) { + for(AUTO_VAR(it, mobEffects->begin()); it != mobEffects->end(); ++it) + { + MobEffectInstance *effect = *it; + if (MobEffect::effects[effect->getId()]->isInstantenous()) + { + return true; + } + } + return false; +} + +wstring PotionItem::getHoverName(shared_ptr itemInstance) +{ + if (itemInstance->getAuxValue() == 0) + { + return app.GetString(IDS_ITEM_WATER_BOTTLE); // I18n.get("item.emptyPotion.name").trim(); + } + + wstring elementName = Item::getHoverName(itemInstance); + if (isThrowable(itemInstance->getAuxValue())) + { + //elementName = I18n.get("potion.prefix.grenade").trim() + " " + elementName; + elementName = replaceAll(elementName,L"{*splash*}",app.GetString(IDS_POTION_PREFIX_GRENADE)); + } + else + { + elementName = replaceAll(elementName,L"{*splash*}",L""); + } + + vector *effects = ((PotionItem *) Item::potion)->getMobEffects(itemInstance); + if (effects != NULL && !effects->empty()) + { + //String postfixString = effects.get(0).getDescriptionId(); + //postfixString += ".postfix"; + //return elementName + " " + I18n.get(postfixString).trim(); + + elementName = replaceAll(elementName,L"{*prefix*}",L""); + elementName = replaceAll(elementName,L"{*postfix*}",app.GetString(effects->at(0)->getPostfixDescriptionId())); + } + else + { + //String appearanceName = PotionBrewing.getAppearanceName(itemInstance.getAuxValue()); + //return I18n.get(appearanceName).trim() + " " + elementName; + + elementName = replaceAll(elementName,L"{*prefix*}",app.GetString( PotionBrewing::getAppearanceName(itemInstance->getAuxValue()))); + elementName = replaceAll(elementName,L"{*postfix*}",L""); + } + return elementName; +} + +void PotionItem::appendHoverText(shared_ptr itemInstance, shared_ptr player, vector *lines, bool advanced, vector &unformattedStrings) +{ + if (itemInstance->getAuxValue() == 0) + { + return; + } + vector *effects = ((PotionItem *) Item::potion)->getMobEffects(itemInstance); + if (effects != NULL && !effects->empty()) + { + //for (MobEffectInstance effect : effects) + for(AUTO_VAR(it, effects->begin()); it != effects->end(); ++it) + { + MobEffectInstance *effect = *it; + wstring effectString = app.GetString( effect->getDescriptionId() );//I18n.get(effect.getDescriptionId()).trim(); + if (effect->getAmplifier() > 0) + { + wstring potencyString = L""; + switch(effect->getAmplifier()) + { + case 1: + potencyString = L" "; + potencyString += app.GetString( IDS_POTION_POTENCY_1 ); + break; + case 2: + potencyString = L" "; + potencyString += app.GetString( IDS_POTION_POTENCY_2 ); + break; + case 3: + potencyString = L" "; + potencyString += app.GetString( IDS_POTION_POTENCY_3 ); + break; + default: + potencyString = app.GetString( IDS_POTION_POTENCY_0 ); + break; + } + effectString += potencyString;// + I18n.get("potion.potency." + effect.getAmplifier()).trim(); + } + if (effect->getDuration() > SharedConstants::TICKS_PER_SECOND) + { + effectString += L" (" + MobEffect::formatDuration(effect) + L")"; + } + unformattedStrings.push_back(effectString); + wchar_t formatted[256]; + ZeroMemory(formatted, 256 * sizeof(wchar_t)); + eMinecraftColour colour = eMinecraftColour_NOT_SET; + if (MobEffect::effects[effect->getId()]->isHarmful()) + { + colour = eHTMLColor_c; + //lines->push_back(L"§c + effectString); //"§c" + } + else + { + colour = eHTMLColor_7; + //lines->push_back(L"§7" + effectString); //"§7" + } + swprintf(formatted, 256, L"%ls",app.GetHTMLColour(colour),effectString.c_str()); + lines->push_back(formatted); + } + } + else + { + wstring effectString = app.GetString(IDS_POTION_EMPTY); //I18n.get("potion.empty").trim(); + //eHTMLColor_7 + wchar_t formatted[256]; + swprintf(formatted,256,L"%ls",app.GetHTMLColour(eHTMLColor_7),effectString.c_str()); + lines->push_back(formatted); //"§7" + } +} + +bool PotionItem::isFoil(shared_ptr itemInstance) +{ + vector *mobEffects = getMobEffects(itemInstance); + return mobEffects != NULL && !mobEffects->empty(); +} + +unsigned int PotionItem::getUseDescriptionId(shared_ptr instance) +{ + int brew = instance->getAuxValue(); + + +#define MACRO_POTION_IS_NIGHTVISION(aux) ((aux & 0x200F) == MASK_NIGHTVISION) +#define MACRO_POTION_IS_INVISIBILITY(aux) ((aux & 0x200F) == MASK_INVISIBILITY) + + if(brew == 0) return IDS_POTION_DESC_WATER_BOTTLE; + else if( MACRO_POTION_IS_REGENERATION(brew)) return IDS_POTION_DESC_REGENERATION; + else if( MACRO_POTION_IS_SPEED(brew) ) return IDS_POTION_DESC_MOVESPEED; + else if( MACRO_POTION_IS_FIRE_RESISTANCE(brew)) return IDS_POTION_DESC_FIRERESISTANCE; + else if( MACRO_POTION_IS_INSTANTHEALTH(brew)) return IDS_POTION_DESC_HEAL; + else if( MACRO_POTION_IS_NIGHTVISION(brew)) return IDS_POTION_DESC_NIGHTVISION; + else if( MACRO_POTION_IS_INVISIBILITY(brew)) return IDS_POTION_DESC_INVISIBILITY; + else if( MACRO_POTION_IS_WEAKNESS(brew)) return IDS_POTION_DESC_WEAKNESS; + else if( MACRO_POTION_IS_STRENGTH(brew)) return IDS_POTION_DESC_DAMAGEBOOST; + else if( MACRO_POTION_IS_SLOWNESS(brew)) return IDS_POTION_DESC_MOVESLOWDOWN; + else if( MACRO_POTION_IS_POISON(brew)) return IDS_POTION_DESC_POISON; + else if( MACRO_POTION_IS_INSTANTDAMAGE(brew)) return IDS_POTION_DESC_HARM; + return IDS_POTION_DESC_EMPTY; +} + +void PotionItem::registerIcons(IconRegister *iconRegister) +{ + iconDrinkable = iconRegister->registerIcon(DEFAULT_ICON); + iconThrowable = iconRegister->registerIcon(THROWABLE_ICON); + iconOverlay = iconRegister->registerIcon(CONTENTS_ICON); +} + +Icon *PotionItem::getTexture(const wstring &name) +{ + if (name.compare(DEFAULT_ICON) == 0) return Item::potion->iconDrinkable; + if (name.compare(THROWABLE_ICON) == 0) return Item::potion->iconThrowable; + if (name.compare(CONTENTS_ICON) == 0) return Item::potion->iconOverlay; + return NULL; +} + + +// 4J Stu - Based loosely on a function that gets added in java much later on (1.3) +vector > *PotionItem::getUniquePotionValues() +{ + if (s_uniquePotionValues.empty()) + { + for (int brew = 0; brew <= PotionBrewing::BREW_MASK; ++brew) + { + vector *effects = PotionBrewing::getEffects(brew, false); + + if (effects != NULL) + { + if(!effects->empty()) + { + // 4J Stu - Based on implementation of Java List.hashCode() at http://docs.oracle.com/javase/6/docs/api/java/util/List.html#hashCode() + // and adding deleting to clear up as we go + int effectsHashCode = 1; + for(AUTO_VAR(it, effects->begin()); it != effects->end(); ++it) + { + MobEffectInstance *mei = *it; + effectsHashCode = 31*effectsHashCode + (mei==NULL ? 0 : mei->hashCode()); + delete (*it); + } + + bool toAdd = true; + for(AUTO_VAR(it, s_uniquePotionValues.begin()); it != s_uniquePotionValues.end(); ++it) + { + // Some potions hash the same (identical effects) but are throwable so account for that + if(it->first == effectsHashCode && !(!isThrowable(it->second) && isThrowable(brew)) ) + { + toAdd = false; + break; + } + } + if( toAdd ) + { + s_uniquePotionValues.push_back(pair(effectsHashCode, brew) ); + } + } + delete effects; + } + } + } + return &s_uniquePotionValues; +} \ No newline at end of file diff --git a/Minecraft.World/PotionItem.h b/Minecraft.World/PotionItem.h new file mode 100644 index 00000000..d03d3308 --- /dev/null +++ b/Minecraft.World/PotionItem.h @@ -0,0 +1,57 @@ +#pragma once +using namespace std; +#include "Item.h" + +class MobEffectInstance; + +class PotionItem : public Item +{ +private: + static const int DRINK_DURATION = (int) (20 * 1.6); + +public: + static const wstring DEFAULT_ICON; + static const wstring THROWABLE_ICON; + static const wstring CONTENTS_ICON; + +private: + unordered_map *> cachedMobEffects; + + Icon *iconThrowable; + Icon *iconDrinkable; + Icon *iconOverlay; + +public: + PotionItem(int id); + + virtual vector *getMobEffects(shared_ptr potion); + virtual vector *getMobEffects(int auxValue); + virtual shared_ptr useTimeDepleted(shared_ptr instance, Level *level, shared_ptr player); + virtual int getUseDuration(shared_ptr itemInstance); + virtual UseAnim getUseAnimation(shared_ptr itemInstance); + virtual shared_ptr use(shared_ptr instance, Level *level, shared_ptr player); + virtual bool TestUse(Level *level, shared_ptr player); + virtual bool useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + virtual Icon *getIcon(int auxValue); + virtual Icon *getLayerIcon(int auxValue, int spriteLayer); + static bool isThrowable(int auxValue); + int getColor(int data); + virtual int getColor(shared_ptr item, int spriteLayer); + virtual bool hasMultipleSpriteLayers(); + virtual bool hasInstantenousEffects(int itemAuxValue); + virtual wstring getHoverName(shared_ptr itemInstance); + virtual void appendHoverText(shared_ptr itemInstance, shared_ptr player, vector *lines, bool advanced, vector &unformattedStrings); + virtual bool isFoil(shared_ptr itemInstance); + + virtual unsigned int getUseDescriptionId(shared_ptr instance); + + //@Override + void registerIcons(IconRegister *iconRegister); + static Icon *getTexture(const wstring &name); + + // 4J Stu - Based loosely on a function that gets added in java much later on (1.3) + static vector > *getUniquePotionValues(); +private: + // 4J Stu - Added to support function above, different from Java implementation + static vector > s_uniquePotionValues; +}; \ No newline at end of file diff --git a/Minecraft.World/PreLoginPacket.cpp b/Minecraft.World/PreLoginPacket.cpp new file mode 100644 index 00000000..4b474214 --- /dev/null +++ b/Minecraft.World/PreLoginPacket.cpp @@ -0,0 +1,119 @@ +#include "stdafx.h" +#include +#include "PacketListener.h" +#include "PreloginPacket.h" +#include "InputOutputStream.h" + + + +PreLoginPacket::PreLoginPacket() +{ + loginKey = L""; + m_playerXuids = NULL; + m_dwPlayerCount = 0; + m_friendsOnlyBits = 0; + m_ugcPlayersVersion = 0; + ZeroMemory(m_szUniqueSaveName,m_iSaveNameLen); + m_serverSettings = 0; + m_hostIndex = 0; + m_texturePackId = 0; + m_netcodeVersion = 0; +} + +PreLoginPacket::PreLoginPacket(wstring userName) +{ + this->loginKey = userName; + m_playerXuids = NULL; + m_dwPlayerCount = 0; + m_friendsOnlyBits = 0; + m_ugcPlayersVersion = 0; + ZeroMemory(m_szUniqueSaveName,m_iSaveNameLen); + m_serverSettings = 0; + m_hostIndex = 0; + m_texturePackId = 0; + m_netcodeVersion = 0; +} + +PreLoginPacket::PreLoginPacket(wstring userName, PlayerUID *playerXuids, DWORD playerCount, BYTE friendsOnlyBits, DWORD ugcPlayersVersion,char *pszUniqueSaveName, DWORD serverSettings, BYTE hostIndex, DWORD texturePackId) +{ + this->loginKey = userName; + m_playerXuids = playerXuids; + m_dwPlayerCount = playerCount; + m_friendsOnlyBits = friendsOnlyBits; + m_ugcPlayersVersion = ugcPlayersVersion; + memcpy(m_szUniqueSaveName,pszUniqueSaveName,m_iSaveNameLen); + m_serverSettings = serverSettings; + m_hostIndex = hostIndex; + m_texturePackId = texturePackId; + m_netcodeVersion = 0; +} + +PreLoginPacket::~PreLoginPacket() +{ + if( m_playerXuids != NULL ) delete [] m_playerXuids; +} + +void PreLoginPacket::read(DataInputStream *dis) //throws IOException +{ + m_netcodeVersion = dis->readShort(); + + loginKey = readUtf(dis, 32); + + m_friendsOnlyBits = dis->readByte(); + m_ugcPlayersVersion = dis->readInt(); + m_dwPlayerCount = dis->readByte(); + if( m_dwPlayerCount > 0 ) + { + m_playerXuids = new PlayerUID[m_dwPlayerCount]; + for(DWORD i = 0; i < m_dwPlayerCount; ++i) + { + m_playerXuids[i] = dis->readPlayerUID(); + } + } + for(DWORD i = 0; i < m_iSaveNameLen; ++i) + { + m_szUniqueSaveName[i]=dis->readByte(); + } + m_serverSettings = dis->readInt(); + m_hostIndex = dis->readByte(); + + INT texturePackId = dis->readInt(); + m_texturePackId = *(DWORD *)&texturePackId; + + // Set the name of the map so we can check it for players banned lists + app.SetUniqueMapName((char *)m_szUniqueSaveName); +} + +void PreLoginPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeShort(MINECRAFT_NET_VERSION); + + writeUtf(loginKey, dos); + + dos->writeByte(m_friendsOnlyBits); + dos->writeInt(m_ugcPlayersVersion); + dos->writeByte((byte)m_dwPlayerCount); + for(DWORD i = 0; i < m_dwPlayerCount; ++i) + { + dos->writePlayerUID( m_playerXuids[i] ); + } + + app.DebugPrintf("*** PreLoginPacket::write - %s\n",m_szUniqueSaveName); + for(DWORD i = 0; i < m_iSaveNameLen; ++i) + { + dos->writeByte(m_szUniqueSaveName[i]); + } + dos->writeInt(m_serverSettings); + dos->writeByte(m_hostIndex); + dos->writeInt(m_texturePackId); +} + +void PreLoginPacket::handle(PacketListener *listener) +{ + listener->handlePreLogin(shared_from_this()); +} + +int PreLoginPacket::getEstimatedSize() +{ + return 4 + 4 + (int)loginKey.length() + 4 +14 + 4 + 1 + 4; +} diff --git a/Minecraft.World/PreLoginPacket.h b/Minecraft.World/PreLoginPacket.h new file mode 100644 index 00000000..243f2f36 --- /dev/null +++ b/Minecraft.World/PreLoginPacket.h @@ -0,0 +1,39 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class PreLoginPacket : public Packet, public enable_shared_from_this +{ + // the login key is username client->server and sessionid server->client +public: + static const int m_iSaveNameLen=14; + //4J Added more info to this packet so that we can check if anyone has a UGC privilege that won't let us + // join, and so that we can inform the server if we have that privilege set. Anyone with UGC turned off completely + // can't play the game online at all, so we only need to specify players with friends only set + PlayerUID *m_playerXuids; + DWORD m_dwPlayerCount; + BYTE m_friendsOnlyBits; + DWORD m_ugcPlayersVersion; + BYTE m_szUniqueSaveName[m_iSaveNameLen]; // added for checking if the level is in the ban list + DWORD m_serverSettings; // A bitfield of server settings constructed with the MAKE_SERVER_SETTINGS macro + BYTE m_hostIndex; // Rather than sending the xuid of the host again, send an index into the m_playerXuids array + DWORD m_texturePackId; + SHORT m_netcodeVersion; + + wstring loginKey; + + PreLoginPacket(); + PreLoginPacket(wstring userName); + PreLoginPacket(wstring userName, PlayerUID *playerXuids, DWORD playerCount, BYTE friendsOnlyBits, DWORD ugcPlayersVersion,char *pszUniqueSaveName, DWORD serverSettings, BYTE hostIndex, DWORD texturePackId); + ~PreLoginPacket(); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new PreLoginPacket()); } + virtual int getId() { return 2; } +}; \ No newline at end of file diff --git a/Minecraft.World/PressurePlateTile.cpp b/Minecraft.World/PressurePlateTile.cpp new file mode 100644 index 00000000..eacdeb07 --- /dev/null +++ b/Minecraft.World/PressurePlateTile.cpp @@ -0,0 +1,211 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.h" +#include "PressurePlateTile.h" +#include "SoundTypes.h" + +PressurePlateTile::PressurePlateTile(int id, const wstring &tex, Material *material, Sensitivity sensitivity) : Tile(id, material, isSolidRender()) +{ + this->sensitivity = sensitivity; + this->setTicking(true); + this->texture = tex; + + float o = 1 / 16.0f; + setShape(o, 0, o, 1 - o, 0.5f / 16.0f, 1 - o); +} + +int PressurePlateTile::getTickDelay() +{ + return 20; +} + +AABB *PressurePlateTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +bool PressurePlateTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool PressurePlateTile::blocksLight() +{ + return false; +} + +bool PressurePlateTile::isCubeShaped() +{ + return false; +} + +bool PressurePlateTile::isPathfindable(LevelSource *level, int x, int y, int z) +{ + return true; +} + +bool PressurePlateTile::mayPlace(Level *level, int x, int y, int z) +{ + return level->isTopSolidBlocking(x, y - 1, z) || FenceTile::isFence(level->getTile(x, y - 1, z)); +} + +void PressurePlateTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + bool replace = false; + + if (!level->isTopSolidBlocking(x, y - 1, z) && !FenceTile::isFence(level->getTile(x, y - 1, z))) replace = true; + + if (replace) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } +} + +void PressurePlateTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (level->isClientSide) return; + if (level->getData(x, y, z) == 0) + { + return; + } + + checkPressed(level, x, y, z); +} + +void PressurePlateTile::entityInside(Level *level, int x, int y, int z, shared_ptr entity) +{ + if (level->isClientSide) return; + + if (level->getData(x, y, z) == 1) + { + return; + } + + checkPressed(level, x, y, z); +} + +void PressurePlateTile::checkPressed(Level *level, int x, int y, int z) +{ + bool wasPressed = level->getData(x, y, z) == 1; + bool shouldBePressed = false; + + float b = 2 / 16.0f; + vector > *entities = NULL; + + bool entitiesToBeFreed = false; + if (sensitivity == PressurePlateTile::everything) entities = level->getEntities(nullptr, AABB::newTemp(x + b, y, z + b, x + 1 - b, y + 0.25, z + 1 - b)); + + if (sensitivity == PressurePlateTile::mobs) + { + entities = level->getEntitiesOfClass(typeid(Mob), AABB::newTemp(x + b, y, z + b, x + 1 - b, y + 0.25, z + 1 - b)); + entitiesToBeFreed = true; + } + if (sensitivity == PressurePlateTile::players) + { + entities = level->getEntitiesOfClass(typeid(Player), AABB::newTemp(x + b, y, z + b, x + 1 - b, y + 0.25, z + 1 - b)); + entitiesToBeFreed = true; + } + + if (!entities->empty()) + { + shouldBePressed = true; + } + + if (shouldBePressed && !wasPressed) + { + level->setData(x, y, z, 1); + level->updateNeighborsAt(x, y, z, id); + level->updateNeighborsAt(x, y - 1, z, id); + level->setTilesDirty(x, y, z, x, y, z); + + level->playSound(x + 0.5, y + 0.1, z + 0.5, eSoundType_RANDOM_CLICK, 0.3f, 0.6f); + } + if (!shouldBePressed && wasPressed) + { + level->setData(x, y, z, 0); + level->updateNeighborsAt(x, y, z, id); + level->updateNeighborsAt(x, y - 1, z, id); + level->setTilesDirty(x, y, z, x, y, z); + + level->playSound(x + 0.5, y + 0.1, z + 0.5, eSoundType_RANDOM_CLICK, 0.3f, 0.5f); + } + + if (shouldBePressed) + { + level->addToTickNextTick(x, y, z, id, getTickDelay()); + } + + if( entitiesToBeFreed ) + { + delete entities; + } +} + +void PressurePlateTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + if (data > 0) + { + level->updateNeighborsAt(x, y, z, this->id); + level->updateNeighborsAt(x, y - 1, z, this->id); + } + Tile::onRemove(level, x, y, z, id, data); +} + +void PressurePlateTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + bool pressed = level->getData(x, y, z) == 1; + + float o = 1 / 16.0f; + if (pressed) + { + this->setShape(o, 0, o, 1 - o, 0.5f / 16.0f, 1 - o); + } + else + { + setShape(o, 0, o, 1 - o, 1 / 16.0f, 1 - o); + } +} + +bool PressurePlateTile::getSignal(LevelSource *level, int x, int y, int z, int dir) +{ + return (level->getData(x, y, z)) > 0; +} + +bool PressurePlateTile::getDirectSignal(Level *level, int x, int y, int z, int dir) +{ + if (level->getData(x, y, z) == 0) return false; + return (dir == 1); +} + +bool PressurePlateTile::isSignalSource() +{ + return true; +} + +void PressurePlateTile::updateDefaultShape() +{ + float x = 8 / 16.0f; + float y = 2 / 16.0f; + float z = 8 / 16.0f; + setShape(0.5f - x, 0.5f - y, 0.5f - z, 0.5f + x, 0.5f + y, 0.5f + z); + +} + +bool PressurePlateTile::shouldTileTick(Level *level, int x,int y,int z) +{ + return level->getData(x, y, z) != 0; +} + +int PressurePlateTile::getPistonPushReaction() +{ + return Material::PUSH_DESTROY; +} + +void PressurePlateTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(texture); +} \ No newline at end of file diff --git a/Minecraft.World/PressurePlateTile.h b/Minecraft.World/PressurePlateTile.h new file mode 100644 index 00000000..0b70fca9 --- /dev/null +++ b/Minecraft.World/PressurePlateTile.h @@ -0,0 +1,49 @@ +#pragma once +#include "Tile.h" +#include "Definitions.h" + +class Random; + +class PressurePlateTile : public Tile +{ + friend class Tile; +private: + wstring texture; +public: + enum Sensitivity + { + everything, + mobs, + players + }; + +private: + Sensitivity sensitivity; +protected: + PressurePlateTile(int id, const wstring &tex, Material *material, Sensitivity sensitivity); +public: + virtual int getTickDelay(); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool blocksLight(); + virtual bool isCubeShaped(); + virtual bool isPathfindable(LevelSource *level, int x, int y, int z); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual void entityInside(Level *level, int x, int y, int z, shared_ptr entity); +private: + virtual void checkPressed(Level *level, int x, int y, int z); +public: + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual bool getSignal(LevelSource *level, int x, int y, int z, int dir); + virtual bool getDirectSignal(Level *level, int x, int y, int z, int dir); + virtual bool isSignalSource(); + virtual void updateDefaultShape(); + virtual int getPistonPushReaction(); + void registerIcons(IconRegister *iconRegister); + + // 4J Added so we can check before we try to add a tile to the tick list if it's actually going to do seomthing + virtual bool shouldTileTick(Level *level, int x,int y,int z); +}; diff --git a/Minecraft.World/PrimedTnt.cpp b/Minecraft.World/PrimedTnt.cpp new file mode 100644 index 00000000..09926d50 --- /dev/null +++ b/Minecraft.World/PrimedTnt.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" +#include "JavaMath.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.level.h" +#include "PrimedTnt.h" + + + +void PrimedTnt::_init() +{ + life = 0; + + // Original Java Ctor + blocksBuilding = true; + setSize(0.98f, 0.98f); + heightOffset = bbHeight / 2.0f; +} + +PrimedTnt::PrimedTnt(Level *level) : Entity( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + _init(); +} + +PrimedTnt::PrimedTnt(Level *level, double x, double y, double z) : Entity( level ) +{ + _init(); + + setPos(x, y, z); + + float rot = (float) (Math::random() * PI * 2); + xd = -sin(rot) * 0.02f; + yd = +0.2f; + zd = -cos(rot) * 0.02f; + + life = 80; + + xo = x; + yo = y; + zo = z; +} + +void PrimedTnt::defineSynchedData() +{ +} + +bool PrimedTnt::makeStepSound() +{ + return false; +} + +bool PrimedTnt::isPickable() +{ + return !removed; +} + +void PrimedTnt::tick() +{ + xo = x; + yo = y; + zo = z; + + yd -= 0.04f; + move(xd, yd, zd); + xd *= 0.98f; + yd *= 0.98f; + zd *= 0.98f; + + if (onGround) + { + xd *= 0.7f; + zd *= 0.7f; + yd *= -0.5f; + } + + if (life-- <= 0) + { + remove(); + if (!level->isClientSide) + { + explode(); + } + } + else + { + level->addParticle(eParticleType_smoke, x, y + 0.5f, z, 0, 0, 0); + } + +} + + +void PrimedTnt::explode() +{ + float r = 4.0f; + level->explode(nullptr, x, y, z, r, true); +} + + +void PrimedTnt::addAdditonalSaveData(CompoundTag *entityTag) +{ + entityTag->putByte(L"Fuse", (byte) life); +} + +void PrimedTnt::readAdditionalSaveData(CompoundTag *tag) +{ + life = tag->getByte(L"Fuse"); +} + + +float PrimedTnt::getShadowHeightOffs() +{ + return 0; +} \ No newline at end of file diff --git a/Minecraft.World/PrimedTnt.h b/Minecraft.World/PrimedTnt.h new file mode 100644 index 00000000..8f1b11dd --- /dev/null +++ b/Minecraft.World/PrimedTnt.h @@ -0,0 +1,37 @@ +#pragma once +#include "Entity.h" + +class PrimedTnt : public Entity +{ +public: + eINSTANCEOF GetType() { return eTYPE_PRIMEDTNT; }; + static Entity *create(Level *level) { return new PrimedTnt(level); } + +public: + static const int serialVersionUID = 0; + + int life; + + void _init(); + + PrimedTnt(Level *level); + PrimedTnt(Level *level, double x, double y, double z); + +protected: + virtual void defineSynchedData(); + virtual bool makeStepSound(); + +public: + virtual bool isPickable(); + virtual void tick(); + +private: + void explode(); + +protected: + virtual void addAdditonalSaveData(CompoundTag *entityTag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +public: + virtual float getShadowHeightOffs(); +}; diff --git a/Minecraft.World/ProgressListener.h b/Minecraft.World/ProgressListener.h new file mode 100644 index 00000000..f0148ce7 --- /dev/null +++ b/Minecraft.World/ProgressListener.h @@ -0,0 +1,15 @@ +#pragma once +using namespace std; + +class ProgressListener +{ +public: + // 4J Stu - Changed all messages here to be string Id's + virtual void progressStagePercentage(int p) = 0; + virtual void progressStart(int stringId) = 0; + virtual void progressStartNoAbort(int stringId) = 0; + virtual void progressStage(int stringId) = 0; + + // 4J-PB - added to display bytes retrieved of a save transfer + virtual void progressStage(wstring &wstrText) = 0; +}; \ No newline at end of file diff --git a/Minecraft.World/ProtectionEnchantment.cpp b/Minecraft.World/ProtectionEnchantment.cpp new file mode 100644 index 00000000..39e3eca1 --- /dev/null +++ b/Minecraft.World/ProtectionEnchantment.cpp @@ -0,0 +1,94 @@ +#include "stdafx.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.entity.h" +#include "ProtectionEnchantment.h" + +const int ProtectionEnchantment::names[] = {IDS_ENCHANTMENT_PROTECT_ALL, IDS_ENCHANTMENT_PROTECT_FIRE, IDS_ENCHANTMENT_PROTECT_FALL, IDS_ENCHANTMENT_PROTECT_EXPLOSION, IDS_ENCHANTMENT_PROTECT_PROJECTILE}; +const int ProtectionEnchantment::minCost[] = {1, 10, 5, 5, 3}; +const int ProtectionEnchantment::levelCost[] = {11, 8, 6, 8, 6}; +const int ProtectionEnchantment::levelCostSpan[] = {20, 12, 10, 12, 15}; + +ProtectionEnchantment::ProtectionEnchantment(int id, int frequency, int type) : Enchantment(id, frequency, EnchantmentCategory::armor), type(type) +{ + if (type == FALL) + { + category = EnchantmentCategory::armor_feet; + } +} + +int ProtectionEnchantment::getMinCost(int level) +{ + return minCost[type] + (level - 1) * levelCost[type]; +} + +int ProtectionEnchantment::getMaxCost(int level) +{ + return getMinCost(level) + levelCostSpan[type]; +} + +int ProtectionEnchantment::getMaxLevel() +{ + return 4; +} + +int ProtectionEnchantment::getDamageProtection(int level, DamageSource *source) +{ + if (source->isBypassInvul()) return 0; + + float protect = (6 + level * level) / 3.0f; + + if (type == ALL) return Mth::floor(protect * 0.75f); + if (type == FIRE && source->isFire()) return Mth::floor(protect * 1.25f); + if (type == FALL && source == DamageSource::fall) return Mth::floor(protect * 2.5f); + if (type == EXPLOSION && source == DamageSource::explosion) return Mth::floor(protect * 1.5f); + if (type == PROJECTILE && source->isProjectile()) return Mth::floor(protect * 1.5f); + return 0; +} + +int ProtectionEnchantment::getDescriptionId() +{ + return names[type]; +} + +bool ProtectionEnchantment::isCompatibleWith(Enchantment *other) const +{ + ProtectionEnchantment *pe = dynamic_cast( other ); + if (pe != NULL) + { + if (pe->type == this->type) + { + return false; + } + if (this->type == FALL || pe->type == FALL) + { + return true; + } + return false; + } + return Enchantment::isCompatibleWith(other); +} + +int ProtectionEnchantment::getFireAfterDampener(shared_ptr entity, int time) +{ + int level = EnchantmentHelper::getEnchantmentLevel(Enchantment::fireProtection->id, entity->getEquipmentSlots()); + + if (level > 0) + { + time -= Mth::floor(time * (level * 0.15f)); + } + + return time; +} + +double ProtectionEnchantment::getExplosionKnockbackAfterDampener(shared_ptr entity, double power) +{ + int level = EnchantmentHelper::getEnchantmentLevel(Enchantment::explosionProtection->id, entity->getEquipmentSlots()); + + if (level > 0) + { + power -= Mth::floor(power * (level * 0.15f)); + } + + return power; +} \ No newline at end of file diff --git a/Minecraft.World/ProtectionEnchantment.h b/Minecraft.World/ProtectionEnchantment.h new file mode 100644 index 00000000..910e32df --- /dev/null +++ b/Minecraft.World/ProtectionEnchantment.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Enchantment.h" + +class ProtectionEnchantment : public Enchantment +{ +public: + static const int ALL = 0; + static const int FIRE = 1; + static const int FALL = 2; + static const int EXPLOSION = 3; + static const int PROJECTILE = 4; + +private: + static const int names[]; + static const int minCost[]; + static const int levelCost[]; + static const int levelCostSpan[]; + +public: + const int type; + + ProtectionEnchantment(int id, int frequency, int type); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); + virtual int getDamageProtection(int level, DamageSource *source); + virtual int getDescriptionId(); + virtual bool isCompatibleWith(Enchantment *other) const; + static int getFireAfterDampener(shared_ptr entity, int time); + static double getExplosionKnockbackAfterDampener(shared_ptr entity, double power); +}; \ No newline at end of file diff --git a/Minecraft.World/PumpkinFeature.cpp b/Minecraft.World/PumpkinFeature.cpp new file mode 100644 index 00000000..6af6a9a1 --- /dev/null +++ b/Minecraft.World/PumpkinFeature.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "PumpkinFeature.h" + +bool PumpkinFeature::place(Level *level, Random *random, int x, int y, int z) +{ + for (int i = 0; i < 64; i++) + { + int x2 = x + random->nextInt(8) - random->nextInt(8); + int y2 = y + random->nextInt(4) - random->nextInt(4); + int z2 = z + random->nextInt(8) - random->nextInt(8); + if (level->isEmptyTile(x2, y2, z2) && level->getTile(x2, y2 - 1, z2) == Tile::grass_Id) + { + if (Tile::pumpkin->mayPlace(level, x2, y2, z2)) + { + level->setTileAndDataNoUpdate(x2, y2, z2, Tile::pumpkin_Id, random->nextInt(4)); + } + } + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/PumpkinFeature.h b/Minecraft.World/PumpkinFeature.h new file mode 100644 index 00000000..f4fc0be8 --- /dev/null +++ b/Minecraft.World/PumpkinFeature.h @@ -0,0 +1,9 @@ +#pragma once +#include "Feature.h" + + +class PumpkinFeature : public Feature +{ +public: + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/PumpkinTile.cpp b/Minecraft.World/PumpkinTile.cpp new file mode 100644 index 00000000..577b9a17 --- /dev/null +++ b/Minecraft.World/PumpkinTile.cpp @@ -0,0 +1,169 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.h" +#include "net.minecraft.world.h" +#include "PumpkinTile.h" +#include "Mob.h" +#include "SnowMan.h" +#include "MobCategory.h" + +const wstring PumpkinTile::TEXTURE_FACE = L"pumpkin_face"; +const wstring PumpkinTile::TEXTURE_LANTERN = L"pumpkin_jack"; + +PumpkinTile::PumpkinTile(int id, bool lit) : DirectionalTile(id, Material::vegetable) +{ + iconTop = NULL; + iconFace = NULL; + setTicking(true); + this->lit = lit; +} + +Icon *PumpkinTile::getTexture(int face, int data) +{ + if (face == Facing::UP) return iconTop; + if (face == Facing::DOWN) return iconTop; + + if (data == DIR_NORTH && face == Facing::NORTH) return iconFace; + if (data == DIR_EAST && face == Facing::EAST) return iconFace; + if (data == DIR_SOUTH && face == Facing::SOUTH) return iconFace; + if (data == DIR_WEST && face == Facing::WEST) return iconFace; + + else return icon; +} + +void PumpkinTile::onPlace(Level *level, int x, int y, int z) +{ + Tile::onPlace(level, x, y, z); + if (level->getTile(x, y - 1, z) == Tile::snow_Id && level->getTile(x, y - 2, z) == Tile::snow_Id) + { + if (!level->isClientSide) + { + // 4J - added limit of number of snowmen that can be spawned + if( level->canCreateMore( eTYPE_SNOWMAN, Level::eSpawnType_Egg) ) + { + level->setTileNoUpdate(x, y, z, 0); + level->setTileNoUpdate(x, y - 1, z, 0); + level->setTileNoUpdate(x, y - 2, z, 0); + shared_ptr snowMan = shared_ptr(new SnowMan(level)); + snowMan->moveTo(x + 0.5, y - 1.95, z + 0.5, 0, 0); + level->addEntity(snowMan); + + level->tileUpdated(x, y, z, 0); + level->tileUpdated(x, y - 1, z, 0); + level->tileUpdated(x, y - 2, z, 0); + } + else + { + // If we can't spawn it, at least give the resources back + Tile::spawnResources(level, x, y, z, level->getData(x, y, z), 0); + Tile::tiles[Tile::snow_Id]->spawnResources(level, x, y - 1, z, level->getData(x, y - 1, z), 0); + Tile::tiles[Tile::snow_Id]->spawnResources(level, x, y - 2, z, level->getData(x, y - 2, z), 0); + level->setTile(x, y, z, 0); + level->setTile(x, y - 1, z, 0); + level->setTile(x, y - 2, z, 0); + } + } + for (int i = 0; i < 120; i++) + { + level->addParticle(eParticleType_snowshovel, x + level->random->nextDouble(), y - 2 + level->random->nextDouble() * 2.5, z + level->random->nextDouble(), 0, 0, 0); + } + } + else if (level->getTile(x, y - 1, z) == Tile::ironBlock_Id && level->getTile(x, y - 2, z) == Tile::ironBlock_Id) + { + bool xArms = level->getTile(x - 1, y - 1, z) == Tile::ironBlock_Id && level->getTile(x + 1, y - 1, z) == Tile::ironBlock_Id; + bool zArms = level->getTile(x, y - 1, z - 1) == Tile::ironBlock_Id && level->getTile(x, y - 1, z + 1) == Tile::ironBlock_Id; + if (xArms || zArms) + { + if (!level->isClientSide) + { + // 4J - added limit of number of golems that can be spawned + if( level->canCreateMore( eTYPE_VILLAGERGOLEM, Level::eSpawnType_Egg) ) + { + level->setTileNoUpdate(x, y, z, 0); + level->setTileNoUpdate(x, y - 1, z, 0); + level->setTileNoUpdate(x, y - 2, z, 0); + if (xArms) + { + level->setTileNoUpdate(x - 1, y - 1, z, 0); + level->setTileNoUpdate(x + 1, y - 1, z, 0); + } + else + { + level->setTileNoUpdate(x, y - 1, z - 1, 0); + level->setTileNoUpdate(x, y - 1, z + 1, 0); + } + + shared_ptr villagerGolem = shared_ptr(new VillagerGolem(level)); + villagerGolem->setPlayerCreated(true); + villagerGolem->moveTo(x + 0.5, y - 1.95, z + 0.5, 0, 0); + level->addEntity(villagerGolem); + + for (int i = 0; i < 120; i++) + { + level->addParticle(eParticleType_snowballpoof, x + level->random->nextDouble(), y - 2 + level->random->nextDouble() * 3.9, z + level->random->nextDouble(), 0, 0, 0); + } + + level->tileUpdated(x, y, z, 0); + level->tileUpdated(x, y - 1, z, 0); + level->tileUpdated(x, y - 2, z, 0); + if (xArms) + { + level->tileUpdated(x - 1, y - 1, z, 0); + level->tileUpdated(x + 1, y - 1, z, 0); + } + else + { + level->tileUpdated(x, y - 1, z - 1, 0); + level->tileUpdated(x, y - 1, z + 1, 0); + } + } + else + { + // If we can't spawn it, at least give the resources back + Tile::spawnResources(level, x, y, z, level->getData(x, y, z), 0); + Tile::tiles[Tile::ironBlock_Id]->spawnResources(level, x, y - 1, z, level->getData(x, y - 1, z), 0); + Tile::tiles[Tile::ironBlock_Id]->spawnResources(level, x, y - 2, z, level->getData(x, y - 2, z), 0); + level->setTile(x, y, z, 0); + level->setTile(x, y - 1, z, 0); + level->setTile(x, y - 2, z, 0); + + if(xArms) + { + Tile::tiles[Tile::ironBlock_Id]->spawnResources(level, x - 1, y - 1, z, level->getData(x - 1, y - 1, z), 0); + Tile::tiles[Tile::ironBlock_Id]->spawnResources(level, x + 1, y - 1, z, level->getData(x + 1, y - 1, z), 0); + level->setTile(x - 1, y - 1, z, 0); + level->setTile(x + 1, y - 1, z, 0); + } + else + { + Tile::tiles[Tile::ironBlock_Id]->spawnResources(level, x, y - 1, z - 1, level->getData(x, y - 1, z - 1), 0); + Tile::tiles[Tile::ironBlock_Id]->spawnResources(level, x, y - 1, z + 1, level->getData(x, y - 1, z + 1), 0); + level->setTile(x, y - 1, z - 1, 0); + level->setTile(x, y - 1, z + 1, 0); + } + } + } + } + } +} + +bool PumpkinTile::mayPlace(Level *level, int x, int y, int z) +{ + int t = level->getTile(x, y, z); + return (t == 0 || Tile::tiles[t]->material->isReplaceable()) && level->isTopSolidBlocking(x, y - 1, z); + +} + +void PumpkinTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int dir = Mth::floor(by->yRot * 4 / (360) + 2.5) & 3; + level->setData(x, y, z, dir); +} + +void PumpkinTile::registerIcons(IconRegister *iconRegister) +{ + iconFace = iconRegister->registerIcon(lit ? TEXTURE_LANTERN : TEXTURE_FACE); + iconTop = iconRegister->registerIcon(L"pumpkin_top"); + icon = iconRegister->registerIcon(L"pumpkin_side"); +} diff --git a/Minecraft.World/PumpkinTile.h b/Minecraft.World/PumpkinTile.h new file mode 100644 index 00000000..1c808b54 --- /dev/null +++ b/Minecraft.World/PumpkinTile.h @@ -0,0 +1,31 @@ +#pragma once +#include "DirectionalTile.h" + +class Mob; +class ChunkRebuildData; + +class PumpkinTile : public DirectionalTile +{ + friend class Tile; + friend class ChunkRebuildData; +public: + static const int DIR_SOUTH = 0; + static const int DIR_WEST = 1; + static const int DIR_NORTH = 2; + static const int DIR_EAST = 3; + +private: + static const wstring TEXTURE_FACE; + static const wstring TEXTURE_LANTERN; + bool lit; + Icon *iconTop; + Icon *iconFace; +protected: + PumpkinTile(int id, bool lit); +public: + virtual Icon *getTexture(int face, int data); + virtual void onPlace(Level *level, int x, int y, int z); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/QuartzBlockTile.cpp b/Minecraft.World/QuartzBlockTile.cpp new file mode 100644 index 00000000..a7beeb21 --- /dev/null +++ b/Minecraft.World/QuartzBlockTile.cpp @@ -0,0 +1,122 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.h" +#include "QuartzBlockTile.h" + +// 4J Added +#include "Level.h" +#include "PistonBaseTile.h" +#include "Player.h" + +int QuartzBlockTile::BLOCK_NAMES[QUARTZ_BLOCK_NAMES] = { + IDS_TILE_QUARTZ_BLOCK, IDS_TILE_QUARTZ_BLOCK_CHISELED, IDS_TILE_QUARTZ_BLOCK_LINES, IDS_TILE_QUARTZ_BLOCK_LINES, IDS_TILE_QUARTZ_BLOCK_LINES +}; + +const wstring QuartzBlockTile::TEXTURE_TOP = L"quartzblock_top"; +const wstring QuartzBlockTile::TEXTURE_CHISELED_TOP = L"quartzblock_chiseled_top"; +const wstring QuartzBlockTile::TEXTURE_LINES_TOP = L"quartzblock_lines_top"; +const wstring QuartzBlockTile::TEXTURE_BOTTOM = L"quartzblock_bottom"; +const wstring QuartzBlockTile::TEXTURE_NAMES[QUARTZ_BLOCK_TEXTURES] = { L"quartzblock_side", L"quartzblock_chiseled", L"quartzblock_lines", L"", L""}; + +QuartzBlockTile::QuartzBlockTile(int id) : Tile(id, Material::stone) +{ +} + +Icon *QuartzBlockTile::getTexture(int face, int data) +{ + if (data == TYPE_LINES_Y || data == TYPE_LINES_X || data == TYPE_LINES_Z) + { + if (data == TYPE_LINES_Y && (face == Facing::UP || face == Facing::DOWN)) + { + return iconLinesTop; + } + else if (data == TYPE_LINES_X && (face == Facing::EAST || face == Facing::WEST)) + { + return iconLinesTop; + } + else if (data == TYPE_LINES_Z && (face == Facing::NORTH || face == Facing::SOUTH)) + { + return iconLinesTop; + } + + return icons[data]; + } + + if (face == Facing::UP || (face == Facing::DOWN && data == TYPE_CHISELED)) + { + if (data == TYPE_CHISELED) + { + return iconChiseledTop; + } + return iconTop; + } + if (face == Facing::DOWN) + { + return iconBottom; + } + if (data < 0 || data >= QUARTZ_BLOCK_TEXTURES) data = 0; + return icons[data]; +} + +int QuartzBlockTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) +{ + if (itemValue == TYPE_LINES_Y) + { + switch (face) + { + case Facing::NORTH: + case Facing::SOUTH: + itemValue = TYPE_LINES_Z; + break; + case Facing::EAST: + case Facing::WEST: + itemValue = TYPE_LINES_X; + break; + case Facing::UP: + case Facing::DOWN: + itemValue = TYPE_LINES_Y; + break; + } + } + + return itemValue; +} + +int QuartzBlockTile::getSpawnResourcesAuxValue(int data) +{ + if (data == TYPE_LINES_X || data == TYPE_LINES_Z) return TYPE_LINES_Y; + + return data; +} + +shared_ptr QuartzBlockTile::getSilkTouchItemInstance(int data) +{ + if (data == TYPE_LINES_X || data == TYPE_LINES_Z) return shared_ptr(new ItemInstance(id, 1, TYPE_LINES_Y)); + return Tile::getSilkTouchItemInstance(data); +} + +int QuartzBlockTile::getRenderShape() +{ + return Tile::SHAPE_QUARTZ; +} + + +void QuartzBlockTile::registerIcons(IconRegister *iconRegister) +{ + for (int i = 0; i < QUARTZ_BLOCK_TEXTURES; i++) + { + if (TEXTURE_NAMES[i].empty()) + { + icons[i] = icons[i - 1]; + } + else + { + icons[i] = iconRegister->registerIcon(TEXTURE_NAMES[i]); + } + } + + iconTop = iconRegister->registerIcon(TEXTURE_TOP); + iconChiseledTop = iconRegister->registerIcon(TEXTURE_CHISELED_TOP); + iconLinesTop = iconRegister->registerIcon(TEXTURE_LINES_TOP); + iconBottom = iconRegister->registerIcon(TEXTURE_BOTTOM); +} \ No newline at end of file diff --git a/Minecraft.World/QuartzBlockTile.h b/Minecraft.World/QuartzBlockTile.h new file mode 100644 index 00000000..bdb5af8b --- /dev/null +++ b/Minecraft.World/QuartzBlockTile.h @@ -0,0 +1,47 @@ +#pragma once + +#include "Tile.h" + +class QuartzBlockTile : public Tile +{ + friend class ChunkRebuildData; +public: + static const int TYPE_DEFAULT = 0; + static const int TYPE_CHISELED = 1; + static const int TYPE_LINES_Y = 2; + static const int TYPE_LINES_X = 3; + static const int TYPE_LINES_Z = 4; + + static const int QUARTZ_BLOCK_NAMES = 5; + + static int BLOCK_NAMES[QUARTZ_BLOCK_NAMES]; + +private: + static const int QUARTZ_BLOCK_TEXTURES = 5; + + static const wstring TEXTURE_TOP; + static const wstring TEXTURE_CHISELED_TOP; + static const wstring TEXTURE_LINES_TOP; + static const wstring TEXTURE_BOTTOM; + static const wstring TEXTURE_NAMES[QUARTZ_BLOCK_TEXTURES]; + + Icon *icons[QUARTZ_BLOCK_TEXTURES]; + Icon *iconChiseledTop; + Icon *iconLinesTop; + Icon *iconTop; + Icon *iconBottom; + +public: + QuartzBlockTile(int id); + + Icon *getTexture(int face, int data); + int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + int getSpawnResourcesAuxValue(int data); + +protected: + shared_ptr getSilkTouchItemInstance(int data); + +public: + int getRenderShape(); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/RailTile.cpp b/Minecraft.World/RailTile.cpp new file mode 100644 index 00000000..4ece48ca --- /dev/null +++ b/Minecraft.World/RailTile.cpp @@ -0,0 +1,676 @@ +#include "stdafx.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" +#include "RailTile.h" + +RailTile::Rail::Rail(Level *level, int x, int y, int z) +{ + this->level = level; + this->x = x; + this->y = y; + this->z = z; + + int id = level->getTile(x, y, z); + + // 4J Stu - We saw a random crash near the end of development on XboxOne orignal version where the id here isn't a tile any more + // Adding this check in to avoid that crash + m_bValidRail = isRail(id); + if(m_bValidRail) + { + int direction = level->getData(x, y, z); + if (((RailTile *) Tile::tiles[id])->usesDataBit) + { + usesDataBit = true; + direction = direction & ~RAIL_DATA_BIT; + } + else + { + usesDataBit = false; + } + updateConnections(direction); + } +} + +RailTile::Rail::~Rail() +{ + for( int i = 0; i < connections.size(); i++ ) + { + delete connections[i]; + } +} + +void RailTile::Rail::updateConnections(int direction) +{ + if(m_bValidRail) + { + for( int i = 0; i < connections.size(); i++ ) + { + delete connections[i]; + } + connections.clear(); + MemSect(50); + if (direction == DIR_FLAT_Z) + { + connections.push_back(new TilePos(x, y, z - 1)); + connections.push_back(new TilePos(x, y, z + 1)); + } else if (direction == DIR_FLAT_X) + { + connections.push_back(new TilePos(x - 1, y, z)); + connections.push_back(new TilePos(x + 1, y, z)); + } else if (direction == 2) + { + connections.push_back(new TilePos(x - 1, y, z)); + connections.push_back(new TilePos(x + 1, y + 1, z)); + } else if (direction == 3) + { + connections.push_back(new TilePos(x - 1, y + 1, z)); + connections.push_back(new TilePos(x + 1, y, z)); + } else if (direction == 4) + { + connections.push_back(new TilePos(x, y + 1, z - 1)); + connections.push_back(new TilePos(x, y, z + 1)); + } else if (direction == 5) + { + connections.push_back(new TilePos(x, y, z - 1)); + connections.push_back(new TilePos(x, y + 1, z + 1)); + } else if (direction == 6) + { + connections.push_back(new TilePos(x + 1, y, z)); + connections.push_back(new TilePos(x, y, z + 1)); + } else if (direction == 7) + { + connections.push_back(new TilePos(x - 1, y, z)); + connections.push_back(new TilePos(x, y, z + 1)); + } else if (direction == 8) + { + connections.push_back(new TilePos(x - 1, y, z)); + connections.push_back(new TilePos(x, y, z - 1)); + } else if (direction == 9) + { + connections.push_back(new TilePos(x + 1, y, z)); + connections.push_back(new TilePos(x, y, z - 1)); + } + MemSect(0); + } +} + +void RailTile::Rail::removeSoftConnections() +{ + if(m_bValidRail) + { + for (unsigned int i = 0; i < connections.size(); i++) + { + Rail *rail = getRail(connections[i]); + if (rail == NULL || !rail->connectsTo(this)) + { + delete connections[i]; + connections.erase(connections.begin()+i); + i--; + } else + { + delete connections[i]; + MemSect(50); + connections[i] =new TilePos(rail->x, rail->y, rail->z); + MemSect(0); + } + delete rail; + } + } +} + +bool RailTile::Rail::hasRail(int x, int y, int z) +{ + if(!m_bValidRail) return false; + if (isRail(level, x, y, z)) return true; + if (isRail(level, x, y + 1, z)) return true; + if (isRail(level, x, y - 1, z)) return true; + return false; +} + +RailTile::Rail *RailTile::Rail::getRail(TilePos *p) +{ + if(!m_bValidRail) return NULL; + if (isRail(level, p->x, p->y, p->z)) return new Rail(level, p->x, p->y, p->z); + if (isRail(level, p->x, p->y + 1, p->z)) return new Rail(level, p->x, p->y + 1, p->z); + if (isRail(level, p->x, p->y - 1, p->z)) return new Rail(level, p->x, p->y - 1, p->z); + return NULL; +} + + +bool RailTile::Rail::connectsTo(Rail *rail) +{ + if(m_bValidRail) + { + AUTO_VAR(itEnd, connections.end()); + for (AUTO_VAR(it, connections.begin()); it != itEnd; it++) + { + TilePos *p = *it; //connections[i]; + if (p->x == rail->x && p->z == rail->z) + { + return true; + } + } + } + return false; +} + +bool RailTile::Rail::hasConnection(int x, int y, int z) +{ + if(m_bValidRail) + { + AUTO_VAR(itEnd, connections.end()); + for (AUTO_VAR(it, connections.begin()); it != itEnd; it++) + { + TilePos *p = *it; //connections[i]; + if (p->x == x && p->z == z) + { + return true; + } + } + } + return false; +} + + +int RailTile::Rail::countPotentialConnections() +{ + int count = 0; + + if(m_bValidRail) + { + if (hasRail(x, y, z - 1)) count++; + if (hasRail(x, y, z + 1)) count++; + if (hasRail(x - 1, y, z)) count++; + if (hasRail(x + 1, y, z)) count++; + } + + return count; +} + +bool RailTile::Rail::canConnectTo(Rail *rail) +{ + if(!m_bValidRail) return false; + if (connectsTo(rail)) return true; + if (connections.size() == 2) + { + return false; + } + if (connections.empty()) + { + return true; + } + + TilePos *c = connections[0]; + + return true; +} + +void RailTile::Rail::connectTo(Rail *rail) +{ + if(m_bValidRail) + { + MemSect(50); + connections.push_back(new TilePos(rail->x, rail->y, rail->z)); + MemSect(0); + + bool n = hasConnection(x, y, z - 1); + bool s = hasConnection(x, y, z + 1); + bool w = hasConnection(x - 1, y, z); + bool e = hasConnection(x + 1, y, z); + + int dir = -1; + + if (n || s) dir = DIR_FLAT_Z; + if (w || e) dir = DIR_FLAT_X; + + if (!usesDataBit) + { + if (s && e && !n && !w) dir = 6; + if (s && w && !n && !e) dir = 7; + if (n && w && !s && !e) dir = 8; + if (n && e && !s && !w) dir = 9; + } + if (dir == DIR_FLAT_Z) + { + if (isRail(level, x, y + 1, z - 1)) dir = 4; + if (isRail(level, x, y + 1, z + 1)) dir = 5; + } + if (dir == DIR_FLAT_X) + { + if (isRail(level, x + 1, y + 1, z)) dir = 2; + if (isRail(level, x - 1, y + 1, z)) dir = 3; + } + + if (dir < 0) dir = DIR_FLAT_Z; + + int data = dir; + if (usesDataBit) + { + data = (level->getData(x, y, z) & RAIL_DATA_BIT) | dir; + } + + level->setData(x, y, z, data); + } +} + +bool RailTile::Rail::hasNeighborRail(int x, int y, int z) +{ + if(!m_bValidRail) return false; + TilePos tp(x,y,z); + Rail *neighbor = getRail( &tp ); + if (neighbor == NULL) return false; + neighbor->removeSoftConnections(); + bool retval = neighbor->canConnectTo(this); + delete neighbor; + return retval; +} + +void RailTile::Rail::place(bool hasSignal, bool first) +{ + if(m_bValidRail) + { + bool n = hasNeighborRail(x, y, z - 1); + bool s = hasNeighborRail(x, y, z + 1); + bool w = hasNeighborRail(x - 1, y, z); + bool e = hasNeighborRail(x + 1, y, z); + + int dir = -1; + + if ((n || s) && !w && !e) dir = DIR_FLAT_Z; + if ((w || e) && !n && !s) dir = DIR_FLAT_X; + + if (!usesDataBit) + { + if (s && e && !n && !w) dir = 6; + if (s && w && !n && !e) dir = 7; + if (n && w && !s && !e) dir = 8; + if (n && e && !s && !w) dir = 9; + } + if (dir == -1) + { + if (n || s) dir = DIR_FLAT_Z; + if (w || e) dir = DIR_FLAT_X; + + if (!usesDataBit) + { + if (hasSignal) + { + if (s && e) dir = 6; + if (w && s) dir = 7; + if (e && n) dir = 9; + if (n && w) dir = 8; + } else { + if (n && w) dir = 8; + if (e && n) dir = 9; + if (w && s) dir = 7; + if (s && e) dir = 6; + } + } + } + + if (dir == DIR_FLAT_Z) + { + if (isRail(level, x, y + 1, z - 1)) dir = 4; + if (isRail(level, x, y + 1, z + 1)) dir = 5; + } + if (dir == DIR_FLAT_X) + { + if (isRail(level, x + 1, y + 1, z)) dir = 2; + if (isRail(level, x - 1, y + 1, z)) dir = 3; + } + + if (dir < 0) dir = DIR_FLAT_Z; + + updateConnections(dir); + + int data = dir; + if (usesDataBit) + { + data = (level->getData(x, y, z) & RAIL_DATA_BIT) | dir; + } + + if (first || level->getData(x, y, z) != data) + { + level->setData(x, y, z, data); + + AUTO_VAR(itEnd, connections.end()); + for (AUTO_VAR(it, connections.begin()); it != itEnd; it++) + { + Rail *neighbor = getRail(*it); + if (neighbor == NULL) continue; + neighbor->removeSoftConnections(); + + if (neighbor->canConnectTo(this)) + { + neighbor->connectTo(this); + } + delete neighbor; + } + } + } +} + +bool RailTile::isRail(Level *level, int x, int y, int z) +{ + int tile = level->getTile(x, y, z); + return tile == Tile::rail_Id || tile == Tile::goldenRail_Id || tile == Tile::detectorRail_Id; + +} + +bool RailTile::isRail(int id) +{ + return id == Tile::rail_Id || id == Tile::goldenRail_Id || id == Tile::detectorRail_Id; +} + +RailTile::RailTile(int id, bool usesDataBit) : Tile(id, Material::decoration, isSolidRender()) +{ + this->usesDataBit = usesDataBit; + this->setShape(0, 0, 0, 1, 2 / 16.0f, 1); + + iconTurn = NULL; +} + +bool RailTile::isUsesDataBit() +{ + return usesDataBit; +} + +AABB *RailTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +bool RailTile::blocksLight() +{ + return false; +} + +bool RailTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +HitResult *RailTile::clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b) +{ + updateShape(level, xt, yt, zt); + return Tile::clip(level, xt, yt, zt, a, b); +} + +void RailTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + int data = level->getData(x, y, z); + if (data >= 2 && data <= 5) + { + setShape(0, 0, 0, 1, 2 / 16.0f + 0.5f, 1); + } else + { + setShape(0, 0, 0, 1, 2 / 16.0f, 1); + } +} + +Icon *RailTile::getTexture(int face, int data) +{ + if (usesDataBit) + { + if (id == Tile::goldenRail_Id) + { + if ((data & RAIL_DATA_BIT) == 0) + { + return icon; + } + else + { + return iconTurn; // Actually the powered rail on version + } + } + } else if (data >= 6) return iconTurn; + return icon; +} + +bool RailTile::isCubeShaped() +{ + return false; +} + +int RailTile::getRenderShape() +{ + return Tile::SHAPE_RAIL; +} + +int RailTile::getResourceCount(Random random) +{ + return 1; +} + +bool RailTile::mayPlace(Level *level, int x, int y, int z) +{ + if (level->isTopSolidBlocking(x, y - 1, z)) + { + return true; + } + return false; +} + +void RailTile::onPlace(Level *level, int x, int y, int z) +{ + if (!level->isClientSide) + { + updateDir(level, x, y, z, true); + + if (id == Tile::goldenRail_Id) + { + neighborChanged(level, x, y, z, id); + } + } +} + +void RailTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (level->isClientSide) return; + + int data = level->getData(x, y, z); + int dir = data; + if (usesDataBit) { + dir = dir & RAIL_DIRECTION_MASK; + } + bool remove = false; + + if (!level->isTopSolidBlocking(x, y - 1, z)) remove = true; + if (dir == 2 && !level->isTopSolidBlocking(x + 1, y, z)) remove = true; + if (dir == 3 && !level->isTopSolidBlocking(x - 1, y, z)) remove = true; + if (dir == 4 && !level->isTopSolidBlocking(x, y, z - 1)) remove = true; + if (dir == 5 && !level->isTopSolidBlocking(x, y, z + 1)) remove = true; + + if (remove) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } + else + { + if (id == Tile::goldenRail_Id) + { + bool signal = level->hasNeighborSignal(x, y, z); + signal = signal || findGoldenRailSignal(level, x, y, z, data, true, 0) || findGoldenRailSignal(level, x, y, z, data, false, 0); + + bool changed = false; + if (signal && (data & RAIL_DATA_BIT) == 0) + { + level->setData(x, y, z, dir | RAIL_DATA_BIT); + changed = true; + } else if (!signal && (data & RAIL_DATA_BIT) != 0) + { + level->setData(x, y, z, dir); + changed = true; + } + + // usually the level only updates neighbors that are in the same + // y plane as the current tile, but sloped rails may need to + // update tiles above or below it as well + if (changed) { + level->updateNeighborsAt(x, y - 1, z, id); + if (dir == 2 || dir == 3 || dir == 4 || dir == 5) + { + level->updateNeighborsAt(x, y + 1, z, id); + } + } + } + else if (type > 0 && Tile::tiles[type]->isSignalSource() && !usesDataBit) + { + Rail *rail = new Rail(level, x, y, z); + if (rail->countPotentialConnections() == 3) + { + updateDir(level, x, y, z, false); + } + delete rail; + } + } + +} + +void RailTile::updateDir(Level *level, int x, int y, int z, bool first) +{ + if (level->isClientSide) return; + Rail *rail = new Rail(level, x, y, z); + rail->place(level->hasNeighborSignal(x, y, z), first); + delete rail; +} + +bool RailTile::findGoldenRailSignal(Level *level, int x, int y, int z, int data, bool forward, int searchDepth) +{ + if (searchDepth >= 8) + { + return false; + } + + int dir = data & RAIL_DIRECTION_MASK; + + bool checkBelow = true; + switch (dir) + { + case DIR_FLAT_Z: + if (forward) + { + z++; + } else { + z--; + } + break; + case DIR_FLAT_X: + if (forward) + { + x--; + } else { + x++; + } + break; + case 2: + if (forward) + { + x--; + } else + { + x++; + y++; + checkBelow = false; + } + dir = DIR_FLAT_X; + break; + case 3: + if (forward) + { + x--; + y++; + checkBelow = false; + } else { + x++; + } + dir = DIR_FLAT_X; + break; + case 4: + if (forward) + { + z++; + } else { + z--; + y++; + checkBelow = false; + } + dir = DIR_FLAT_Z; + break; + case 5: + if (forward) + { + z++; + y++; + checkBelow = false; + } else + { + z--; + } + dir = DIR_FLAT_Z; + break; + } + + if (isGoldenRailWithPower(level, x, y, z, forward, searchDepth, dir)) + { + return true; + } + if (checkBelow && isGoldenRailWithPower(level, x, y - 1, z, forward, searchDepth, dir)) + { + return true; + } + return false; + +} + +bool RailTile::isGoldenRailWithPower(Level *level, int x, int y, int z, bool forward, int searchDepth, int dir) +{ + int tile = level->getTile(x, y, z); + if (tile == Tile::goldenRail_Id) + { + int tileData = level->getData(x, y, z); + int myDir = tileData & RAIL_DIRECTION_MASK; + + if (dir == DIR_FLAT_X && (myDir == DIR_FLAT_Z || myDir == 4 || myDir == 5)) + { + return false; + } + if (dir == DIR_FLAT_Z && (myDir == DIR_FLAT_X || myDir == 2 || myDir == 3)) + { + return false; + } + + if ((tileData & RAIL_DATA_BIT) != 0) + { + if (level->hasNeighborSignal(x, y, z)) + { + return true; + } + else + { + return findGoldenRailSignal(level, x, y, z, tileData, forward, searchDepth + 1); + } + } + } + return false; +} + +int RailTile::getPistonPushReaction() +{ + return Material::PUSH_NORMAL; +} + +void RailTile::registerIcons(IconRegister *iconRegister) +{ + Tile::registerIcons(iconRegister); + if(id == Tile::goldenRail_Id) + { + iconTurn = iconRegister->registerIcon(L"goldenRail_powered"); + } + else + { + iconTurn = iconRegister->registerIcon(L"rail_turn"); + } +} \ No newline at end of file diff --git a/Minecraft.World/RailTile.h b/Minecraft.World/RailTile.h new file mode 100644 index 00000000..776d8454 --- /dev/null +++ b/Minecraft.World/RailTile.h @@ -0,0 +1,84 @@ +#pragma once +#include "Tile.h" +#include "TilePos.h" +#include "Definitions.h" + +class Random; +class HitResult; +class ChunkRebuildData; + +using namespace std; + +class RailTile : public Tile +{ + friend class Tile; + friend class ChunkRebuildData; +public: + static const int DIR_FLAT_Z = 0; + static const int DIR_FLAT_X = 1; + // the data bit is used by boosters and detectors, so they can't turn + static const int RAIL_DATA_BIT = 8; + static const int RAIL_DIRECTION_MASK = 7; + +private: + Icon *iconTurn; + + bool usesDataBit; + + class Rail + { + friend class RailTile; + private: + Level *level; + int x, y, z; + bool usesDataBit; + vector connections; + bool m_bValidRail; // 4J added + + public: + Rail(Level *level, int x, int y, int z); + ~Rail(); + private: + void updateConnections(int direction); + void removeSoftConnections(); + bool hasRail(int x, int y, int z); + Rail *getRail(TilePos *p); + bool connectsTo(Rail *rail); + bool hasConnection(int x, int y, int z); + int countPotentialConnections(); + bool canConnectTo(Rail *rail); + private: + void connectTo(Rail *rail); + bool hasNeighborRail(int x, int y, int z); + public: + void place(bool hasSignal, bool first); + }; +public: + static bool isRail(Level *level, int x, int y, int z); + static bool isRail(int id); +protected: + RailTile(int id, bool usesDataBit); +public: + using Tile::getResourceCount; + + bool isUsesDataBit(); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual bool blocksLight(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual HitResult *clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual Icon *getTexture(int face, int data); + virtual bool isCubeShaped(); + virtual int getRenderShape(); + virtual int getResourceCount(Random random); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual void onPlace(Level *level, int x, int y, int z); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); +private: + void updateDir(Level *level, int x, int y, int z, bool first); + bool findGoldenRailSignal(Level *level, int x, int y, int z, int data, bool forward, int searchDepth); + bool isGoldenRailWithPower(Level *level, int x, int y, int z, bool forward, int searchDepth, int dir); +public: + virtual int getPistonPushReaction(); + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/RainforestBiome.cpp b/Minecraft.World/RainforestBiome.cpp new file mode 100644 index 00000000..6e39038d --- /dev/null +++ b/Minecraft.World/RainforestBiome.cpp @@ -0,0 +1,16 @@ +#include "stdafx.h" +#include "RainforestBiome.h" +#include "net.minecraft.world.level.levelgen.feature.h" + +RainforestBiome::RainforestBiome(int id) : Biome(id) +{ +} + +Feature *RainforestBiome::getTreeFeature(Random *random) +{ + if (random->nextInt(3) == 0) + { + return new BasicTree(false); + } + return new TreeFeature(false); +} \ No newline at end of file diff --git a/Minecraft.World/RainforestBiome.h b/Minecraft.World/RainforestBiome.h new file mode 100644 index 00000000..791ffcda --- /dev/null +++ b/Minecraft.World/RainforestBiome.h @@ -0,0 +1,9 @@ +#pragma once +#include "Biome.h" + +class RainforestBiome : public Biome +{ +public: + RainforestBiome(int id); + virtual Feature *getTreeFeature(Random *random); +}; \ No newline at end of file diff --git a/Minecraft.World/Random.cpp b/Minecraft.World/Random.cpp new file mode 100644 index 00000000..9176bf38 --- /dev/null +++ b/Minecraft.World/Random.cpp @@ -0,0 +1,106 @@ +#include "stdafx.h" +#include "Random.h" +#include "System.h" + +Random::Random() +{ + // 4J - jave now uses the system nanosecond counter added to a "seedUniquifier" to get an initial seed. Our nanosecond timer is actually only millisecond accuate, so + // use QueryPerformanceCounter here instead + __int64 seed; + QueryPerformanceCounter((LARGE_INTEGER *)&seed); + seed += 8682522807148012LL; + + setSeed(seed); +} + +Random::Random(__int64 seed) +{ + setSeed(seed); +} + +void Random::setSeed(__int64 s) +{ + this->seed = (s ^ 0x5DEECE66DLL) & ((1LL << 48) - 1); + haveNextNextGaussian = false; +} + +int Random::next(int bits) +{ + seed = (seed * 0x5DEECE66DLL + 0xBLL) & ((1LL << 48) - 1); + return (int)(seed >> (48 - bits)); +} + +void Random::nextBytes(byte *bytes, unsigned int count) +{ + for(unsigned int i = 0; i < count; i++ ) + { + bytes[i] = (byte)next(8); + } +} + +double Random::nextDouble() +{ + + return (((__int64)next(26) << 27) + next(27)) + / (double)(1LL << 53); +} + +double Random::nextGaussian() +{ + if (haveNextNextGaussian) + { + haveNextNextGaussian = false; + return nextNextGaussian; + } + else + { + double v1, v2, s; + do + { + v1 = 2 * nextDouble() - 1; // between -1.0 and 1.0 + v2 = 2 * nextDouble() - 1; // between -1.0 and 1.0 + s = v1 * v1 + v2 * v2; + } while (s >= 1 || s == 0); + double multiplier = sqrt(-2 * log(s)/s); + nextNextGaussian = v2 * multiplier; + haveNextNextGaussian = true; + return v1 * multiplier; + } +} + +int Random::nextInt() +{ + return next(32); +} + +int Random::nextInt(int n) +{ + assert (n>0); + + + if ((n & -n) == n) // i.e., n is a power of 2 + return (int)(((__int64)next(31) * n) >> 31); // 4J Stu - Made __int64 instead of long + + int bits, val; + do + { + bits = next(31); + val = bits % n; + } while(bits - val + (n-1) < 0); + return val; +} + +float Random::nextFloat() +{ + return next(24) / ((float)(1 << 24)); +} + +__int64 Random::nextLong() +{ + return ((__int64)next(32) << 32) + next(32); +} + +bool Random::nextBoolean() +{ + return next(1) != 0; +} \ No newline at end of file diff --git a/Minecraft.World/Random.h b/Minecraft.World/Random.h new file mode 100644 index 00000000..9af3a3c2 --- /dev/null +++ b/Minecraft.World/Random.h @@ -0,0 +1,23 @@ +#pragma once + +class Random +{ +private: + __int64 seed; + bool haveNextNextGaussian; + double nextNextGaussian; +protected: + int next(int bits); +public: + Random(); + Random(__int64 seed); + void setSeed(__int64 s); + void nextBytes(byte *bytes, unsigned int count); + double nextDouble(); + double nextGaussian(); + int nextInt(); + int nextInt(int to); + float nextFloat(); + __int64 nextLong(); + bool nextBoolean(); +}; \ No newline at end of file diff --git a/Minecraft.World/RandomLevelSource.cpp b/Minecraft.World/RandomLevelSource.cpp new file mode 100644 index 00000000..4e26134a --- /dev/null +++ b/Minecraft.World/RandomLevelSource.cpp @@ -0,0 +1,780 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.levelgen.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.level.levelgen.structure.h" +#include "net.minecraft.world.level.levelgen.synth.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.storage.h" +#include "RandomLevelSource.h" + +#ifdef __PS3__ +#include "..\Minecraft.Client\PS3\SPU_Tasks\PerlinNoise\PerlinNoiseJob.h" +#include "C4JSpursJob.h" +static PerlinNoise_DataIn g_lperlinNoise1_SPU __attribute__((__aligned__(16))); +static PerlinNoise_DataIn g_lperlinNoise2_SPU __attribute__((__aligned__(16))); +static PerlinNoise_DataIn g_perlinNoise1_SPU __attribute__((__aligned__(16))); +static PerlinNoise_DataIn g_scaleNoise_SPU __attribute__((__aligned__(16))); +static PerlinNoise_DataIn g_depthNoise_SPU __attribute__((__aligned__(16))); +#endif + + +const double RandomLevelSource::SNOW_SCALE = 0.3; +const double RandomLevelSource::SNOW_CUTOFF = 0.5; + +RandomLevelSource::RandomLevelSource(Level *level, __int64 seed, bool generateStructures) : generateStructures( generateStructures ) +{ + m_XZSize = level->getLevelData()->getXZSize(); + + caveFeature = new LargeCaveFeature(); + strongholdFeature = new StrongholdFeature(); + villageFeature = new VillageFeature(0,m_XZSize); + mineShaftFeature = new MineShaftFeature(); + scatteredFeature = new RandomScatteredLargeFeature(); + canyonFeature = new CanyonFeature(); + + this->level = level; + + random = new Random(seed); + pprandom = new Random(seed); // 4J - added, so that we can have a separate random for doing post-processing in parallel with creation + lperlinNoise1 = new PerlinNoise(random, 16); + lperlinNoise2 = new PerlinNoise(random, 16); + perlinNoise1 = new PerlinNoise(random, 8); + perlinNoise3 = new PerlinNoise(random, 4); + + scaleNoise = new PerlinNoise(random, 10); + depthNoise = new PerlinNoise(random, 16); + + if (FLOATING_ISLANDS) + { + floatingIslandScale = new PerlinNoise(random, 10); + floatingIslandNoise = new PerlinNoise(random, 16); + } + else + { + floatingIslandScale = NULL; + floatingIslandNoise = NULL; + } + + forestNoise = new PerlinNoise(random, 8); +} + +RandomLevelSource::~RandomLevelSource() +{ + delete caveFeature; + delete strongholdFeature; + delete villageFeature; + delete mineShaftFeature; + delete scatteredFeature; + delete canyonFeature; + + this->level = level; + + delete random;; + delete lperlinNoise1; + delete lperlinNoise2; + delete perlinNoise1; + delete perlinNoise3; + + delete scaleNoise; + delete depthNoise; + + if (FLOATING_ISLANDS) + { + delete floatingIslandScale; + delete floatingIslandNoise; + } + + delete forestNoise; + + if( pows.data != NULL ) delete [] pows.data; +} + + +int g_numPrepareHeightCalls = 0; +LARGE_INTEGER g_totalPrepareHeightsTime = {0,0}; +LARGE_INTEGER g_averagePrepareHeightsTime = {0, 0}; + + +void RandomLevelSource::prepareHeights(int xOffs, int zOffs, byteArray blocks) +{ + LARGE_INTEGER startTime; + int xChunks = 16 / CHUNK_WIDTH; + int yChunks = Level::genDepth / CHUNK_HEIGHT; + int waterHeight = level->seaLevel; + + int xSize = xChunks + 1; + int ySize = Level::genDepth / CHUNK_HEIGHT + 1; + int zSize = xChunks + 1; + + BiomeArray biomes; // 4J created locally here for thread safety, java has this as a class member + + level->getBiomeSource()->getRawBiomeBlock(biomes, xOffs * CHUNK_WIDTH - 2, zOffs * CHUNK_WIDTH - 2, xSize + 5, zSize + 5); + + doubleArray buffer; // 4J - used to be declared with class level scope but tidying up for thread safety reasons + buffer = getHeights(buffer, xOffs * xChunks, 0, zOffs * xChunks, xSize, ySize, zSize, biomes); + + QueryPerformanceCounter(&startTime); + for (int xc = 0; xc < xChunks; xc++) + { + for (int zc = 0; zc < xChunks; zc++) + { + for (int yc = 0; yc < yChunks; yc++) + { + double yStep = 1 / (double) CHUNK_HEIGHT; + double s0 = buffer[((xc + 0) * zSize + (zc + 0)) * ySize + (yc + 0)]; + double s1 = buffer[((xc + 0) * zSize + (zc + 1)) * ySize + (yc + 0)]; + double s2 = buffer[((xc + 1) * zSize + (zc + 0)) * ySize + (yc + 0)]; + double s3 = buffer[((xc + 1) * zSize + (zc + 1)) * ySize + (yc + 0)]; + + double s0a = (buffer[((xc + 0) * zSize + (zc + 0)) * ySize + (yc + 1)] - s0) * yStep; + double s1a = (buffer[((xc + 0) * zSize + (zc + 1)) * ySize + (yc + 1)] - s1) * yStep; + double s2a = (buffer[((xc + 1) * zSize + (zc + 0)) * ySize + (yc + 1)] - s2) * yStep; + double s3a = (buffer[((xc + 1) * zSize + (zc + 1)) * ySize + (yc + 1)] - s3) * yStep; + + for (int y = 0; y < CHUNK_HEIGHT; y++) + { + double xStep = 1 / (double) CHUNK_WIDTH; + + double _s0 = s0; + double _s1 = s1; + double _s0a = (s2 - s0) * xStep; + double _s1a = (s3 - s1) * xStep; + + for (int x = 0; x < CHUNK_WIDTH; x++) + { + int offs = (x + xc * CHUNK_WIDTH) << Level::genDepthBitsPlusFour | (0 + zc * CHUNK_WIDTH) << Level::genDepthBits | (yc * CHUNK_HEIGHT + y); + int step = 1 << Level::genDepthBits; + offs -= step; + double zStep = 1 / (double) CHUNK_WIDTH; + + double val = _s0; + double vala = (_s1 - _s0) * zStep; + val -= vala; + for (int z = 0; z < CHUNK_WIDTH; z++) + { + /////////////////////////////////////////////////////////////////// + // 4J - add this chunk of code to make land "fall-off" at the edges of + // a finite world - size of that world is currently hard-coded in here + const int worldSize = m_XZSize * 16; + const int falloffStart = 32; // chunks away from edge were we start doing fall-off + const float falloffMax = 128.0f; // max value we need to get to falloff by the edge of the map + + int xxx = ( ( xOffs * 16 ) + x + ( xc * CHUNK_WIDTH ) ); + int zzz = ( ( zOffs * 16 ) + z + ( zc * CHUNK_WIDTH ) ); + + // Get distance to edges of world in x + int xxx0 = xxx + ( worldSize / 2 ); + if( xxx0 < 0 ) xxx0 = 0; + int xxx1 = ( ( worldSize / 2 ) - 1 ) - xxx; + if( xxx1 < 0 ) xxx1 = 0; + + // Get distance to edges of world in z + int zzz0 = zzz + ( worldSize / 2 ); + if( zzz0 < 0 ) zzz0 = 0; + int zzz1 = ( ( worldSize / 2 ) - 1 ) - zzz; + if( zzz1 < 0 ) zzz1 = 0; + + // Get min distance to any edge + int emin = xxx0; + if (xxx1 < emin ) emin = xxx1; + if (zzz0 < emin ) emin = zzz0; + if (zzz1 < emin ) emin = zzz1; + + float comp = 0.0f; + + // Calculate how much we want the world to fall away, if we're in the defined region to do so + if( emin < falloffStart ) + { + int falloff = falloffStart - emin; + comp = ((float)falloff / (float)falloffStart ) * falloffMax; + } + // 4J - end of extra code + /////////////////////////////////////////////////////////////////// + + // 4J - slightly rearranged this code (as of java 1.0.1 merge) to better fit with + // changes we've made edge-of-world things - original sets blocks[offs += step] directly + // here rather than setting a tileId + int tileId = 0; + // 4J - this comparison used to just be with 0.0f but is now varied by block above + if ((val += vala) > comp) + { + tileId = (byte) Tile::rock_Id; + } + else if (yc * CHUNK_HEIGHT + y < waterHeight) + { + tileId = (byte) Tile::calmWater_Id; + } + + // 4J - more extra code to make sure that the column at the edge of the world is just water & rock, to match the infinite sea that + // continues on after the edge of the world. + + if( emin == 0 ) + { + // This matches code in MultiPlayerChunkCache that makes the geometry which continues at the edge of the world + if( yc * CHUNK_HEIGHT + y <= ( level->getSeaLevel() - 10 ) ) tileId = Tile::rock_Id; + else if( yc * CHUNK_HEIGHT + y < level->getSeaLevel() ) tileId = Tile::calmWater_Id; + } + + blocks[offs += step] = tileId; + } + _s0 += _s0a; + _s1 += _s1a; + } + + s0 += s0a; + s1 += s1a; + s2 += s2a; + s3 += s3a; + } + } + } + } + LARGE_INTEGER endTime; + QueryPerformanceCounter(&endTime); + LARGE_INTEGER timeInFunc; + timeInFunc.QuadPart = endTime.QuadPart - startTime.QuadPart; + g_numPrepareHeightCalls++; + g_totalPrepareHeightsTime.QuadPart += timeInFunc.QuadPart; + g_averagePrepareHeightsTime.QuadPart = g_totalPrepareHeightsTime.QuadPart / g_numPrepareHeightCalls; + + delete [] buffer.data; + delete [] biomes.data; + + +} + + +void RandomLevelSource::buildSurfaces(int xOffs, int zOffs, byteArray blocks, BiomeArray biomes) +{ + int waterHeight = level->seaLevel; + + double s = 1 / 32.0; + + doubleArray depthBuffer(16*16); // 4J - used to be declared with class level scope but moved here for thread safety + + depthBuffer = perlinNoise3->getRegion(depthBuffer, xOffs * 16, zOffs * 16, 0, 16, 16, 1, s * 2, s * 2, s * 2); + + for (int x = 0; x < 16; x++) + { + for (int z = 0; z < 16; z++) + { + Biome *b = biomes[z + x * 16]; + float temp = b->getTemperature(); + int runDepth = (int) (depthBuffer[x + z * 16] / 3 + 3 + random->nextDouble() * 0.25); + + int run = -1; + + byte top = b->topMaterial; + byte material = b->material; + + LevelGenerationOptions *lgo = app.getLevelGenerationOptions(); + if(lgo != NULL) + { + lgo->getBiomeOverride(b->id,material,top); + } + + for (int y = Level::genDepthMinusOne; y >= 0; y--) + { + int offs = (z * 16 + x) * Level::genDepth + y; + + if (y <= 1 + random->nextInt(2)) // 4J - changed to make the bedrock not have bits you can get stuck in +// if (y <= 0 + random->nextInt(5)) + { + blocks[offs] = (byte) Tile::unbreakable_Id; + } + else + { + int old = blocks[offs]; + + if (old == 0) + { + run = -1; + } + else if (old == Tile::rock_Id) + { + if (run == -1) + { + if (runDepth <= 0) + { + top = 0; + material = (byte) Tile::rock_Id; + } + else if (y >= waterHeight - 4 && y <= waterHeight + 1) + { + top = b->topMaterial; + material = b->material; + if(lgo != NULL) + { + lgo->getBiomeOverride(b->id,material,top); + } + } + + if (y < waterHeight && top == 0) + { + if (temp < 0.15f) top = (byte) Tile::ice_Id; + else top = (byte) Tile::calmWater_Id; + } + + run = runDepth; + if (y >= waterHeight - 1) blocks[offs] = top; + else blocks[offs] = material; + } + else if (run > 0) + { + run--; + blocks[offs] = material; + + // place a few sandstone blocks beneath sand + // runs + if (run == 0 && material == Tile::sand_Id) + { + run = random->nextInt(4); + material = (byte) Tile::sandStone_Id; + } + } + } + } + } + } + } + + delete [] depthBuffer.data; + +} + +LevelChunk *RandomLevelSource::create(int x, int z) +{ + return getChunk(x,z); +} + +LevelChunk *RandomLevelSource::getChunk(int xOffs, int zOffs) +{ + random->setSeed(xOffs * 341873128712l + zOffs * 132897987541l); + + // 4J - now allocating this with a physical alloc & bypassing general memory management so that it will get cleanly freed + int blocksSize = Level::genDepth * 16 * 16; + byte *tileData = (byte *)XPhysicalAlloc(blocksSize, MAXULONG_PTR, 4096, PAGE_READWRITE); + XMemSet128(tileData,0,blocksSize); + byteArray blocks = byteArray(tileData,blocksSize); +// byteArray blocks = byteArray(16 * level->depth * 16); + + // LevelChunk *levelChunk = new LevelChunk(level, blocks, xOffs, zOffs); // 4J - moved to below + + prepareHeights(xOffs, zOffs, blocks); + + // 4J - Some changes made here to how biomes, temperatures and downfalls are passed around for thread safety + BiomeArray biomes; + level->getBiomeSource()->getBiomeBlock(biomes, xOffs * 16, zOffs * 16, 16, 16, true); + + buildSurfaces(xOffs, zOffs, blocks, biomes); + + delete [] biomes.data; + + caveFeature->apply(this, level, xOffs, zOffs, blocks); + // 4J Stu Design Change - 1.8 gen goes stronghold, mineshaft, village, canyon + // this changed in 1.2 to canyon, mineshaft, village, stronghold + // This change makes sense as it stops canyons running through other structures + canyonFeature->apply(this, level, xOffs, zOffs, blocks); + if (generateStructures) + { + mineShaftFeature->apply(this, level, xOffs, zOffs, blocks); + villageFeature->apply(this, level, xOffs, zOffs, blocks); + strongholdFeature->apply(this, level, xOffs, zOffs, blocks); + scatteredFeature->apply(this, level, xOffs, zOffs, blocks); + } +// canyonFeature.apply(this, level, xOffs, zOffs, blocks); + // townFeature.apply(this, level, xOffs, zOffs, blocks); + // addCaves(xOffs, zOffs, blocks); + // addTowns(xOffs, zOffs, blocks); + +// levelChunk->recalcHeightmap(); // 4J - removed & moved into its own method + + // 4J - this now creates compressed block data from the blocks array passed in, so moved it until after the blocks are actually finalised. We also + // now need to free the passed in blocks as the LevelChunk doesn't use the passed in allocation anymore. + LevelChunk *levelChunk = new LevelChunk(level, blocks, xOffs, zOffs); + XPhysicalFree(tileData); + + return levelChunk; +} + +// 4J - removed & moved into its own method from getChunk, so we can call recalcHeightmap after the chunk is added into the cache. Without +// doing this, then loads of the lightgaps() calls will fail to add any lights, because adding a light checks if the cache has this chunk in. +// lightgaps also does light 1 block into the neighbouring chunks, and maybe that is somehow enough to get lighting to propagate round the world, +// but this just doesn't seem right - this isn't a new fault in the 360 version, have checked that java does the same. +void RandomLevelSource::lightChunk(LevelChunk *lc) +{ + lc->recalcHeightmap(); +} + + +doubleArray RandomLevelSource::getHeights(doubleArray buffer, int x, int y, int z, int xSize, int ySize, int zSize, BiomeArray& biomes) +{ + if (buffer.data == NULL) + { + buffer = doubleArray(xSize * ySize * zSize); + } + if (pows.data == NULL) + { + pows = floatArray(5 * 5); + for (int xb = -2; xb <= 2; xb++) + { + for (int zb = -2; zb <= 2; zb++) + { + float ppp = 10.0f / Mth::sqrt(xb * xb + zb * zb + 0.2f); + pows[xb + 2 + (zb + 2) * 5] = ppp; + } + } + } + + double s = 1 * 684.412; + double hs = 1 * 684.412; + + doubleArray pnr, ar, br, sr, dr, fi, fis; // 4J - used to be declared with class level scope but moved here for thread safety + + if (FLOATING_ISLANDS) + { + fis = floatingIslandScale->getRegion(fis, x, y, z, xSize, 1, zSize, 1.0, 0, 1.0); + fi = floatingIslandNoise->getRegion(fi, x, y, z, xSize, 1, zSize, 500.0, 0, 500.0); + } + +#if defined __PS3__ && !defined DISABLE_SPU_CODE + C4JSpursJobQueue::Port port("C4JSpursJob_PerlinNoise"); + C4JSpursJob_PerlinNoise perlinJob1(&g_scaleNoise_SPU); + C4JSpursJob_PerlinNoise perlinJob2(&g_depthNoise_SPU); + C4JSpursJob_PerlinNoise perlinJob3(&g_perlinNoise1_SPU); + C4JSpursJob_PerlinNoise perlinJob4(&g_lperlinNoise1_SPU); + C4JSpursJob_PerlinNoise perlinJob5(&g_lperlinNoise2_SPU); + + g_scaleNoise_SPU.set(scaleNoise, sr, x, z, xSize, zSize, 1.121, 1.121, 0.5); + g_depthNoise_SPU.set(depthNoise, dr, x, z, xSize, zSize, 200.0, 200.0, 0.5); + g_perlinNoise1_SPU.set(perlinNoise1, pnr, x, y, z, xSize, ySize, zSize, s / 80.0, hs / 160.0, s / 80.0); + g_lperlinNoise1_SPU.set(lperlinNoise1, ar, x, y, z, xSize, ySize, zSize, s, hs, s); + g_lperlinNoise2_SPU.set(lperlinNoise2, br, x, y, z, xSize, ySize, zSize, s, hs, s); + + port.submitJob(&perlinJob1); + port.submitJob(&perlinJob2); + port.submitJob(&perlinJob3); + port.submitJob(&perlinJob4); + port.submitJob(&perlinJob5); + port.waitForCompletion(); + #else + sr = scaleNoise->getRegion(sr, x, z, xSize, zSize, 1.121, 1.121, 0.5); + dr = depthNoise->getRegion(dr, x, z, xSize, zSize, 200.0, 200.0, 0.5); + pnr = perlinNoise1->getRegion(pnr, x, y, z, xSize, ySize, zSize, s / 80.0, hs / 160.0, s / 80.0); + ar = lperlinNoise1->getRegion(ar, x, y, z, xSize, ySize, zSize, s, hs, s); + br = lperlinNoise2->getRegion(br, x, y, z, xSize, ySize, zSize, s, hs, s); + +#endif + + x = z = 0; + + int p = 0; + int pp = 0; + + for (int xx = 0; xx < xSize; xx++) + { + for (int zz = 0; zz < zSize; zz++) + { + float sss = 0; + float ddd = 0; + float pow = 0; + + int rr = 2; + + Biome *mb = biomes[(xx + 2) + (zz + 2) * (xSize + 5)]; + for (int xb = -rr; xb <= rr; xb++) + { + for (int zb = -rr; zb <= rr; zb++) + { + Biome *b = biomes[(xx + xb + 2) + (zz + zb + 2) * (xSize + 5)]; + float ppp = pows[xb + 2 + (zb + 2) * 5] / (b->depth + 2); + if (b->depth > mb->depth) + { + ppp /= 2; + } + sss += b->scale * ppp; + ddd += b->depth * ppp; + pow += ppp; + } + } + sss /= pow; + ddd /= pow; + + sss = sss * 0.9f + 0.1f; + ddd = (ddd * 4 - 1) / 8.0f; + + double rdepth = (dr[pp] / 8000.0); + if (rdepth < 0) rdepth = -rdepth * 0.3; + rdepth = rdepth * 3.0 - 2.0; + + if (rdepth < 0) + { + rdepth = rdepth / 2; + if (rdepth < -1) rdepth = -1; + rdepth = rdepth / 1.4; + rdepth /= 2; + } + else + { + if (rdepth > 1) rdepth = 1; + rdepth = rdepth / 8; + } + + pp++; + + for (int yy = 0; yy < ySize; yy++) + { + double depth = ddd; + double scale = sss; + + depth += rdepth * 0.2; + depth = depth * ySize / 16.0; + + double yCenter = ySize / 2.0 + depth * 4; + + double val = 0; + + double yOffs = (yy - (yCenter)) * 12 * 128 / Level::genDepth / scale; + + if (yOffs < 0) yOffs *= 4; + + double bb = ar[p] / 512; + double cc = br[p] / 512; + + double v = (pnr[p] / 10 + 1) / 2; + if (v < 0) val = bb; + else if (v > 1) val = cc; + else val = bb + (cc - bb) * v; + val -= yOffs; + + if (yy > ySize - 4) + { + double slide = (yy - (ySize - 4)) / (4 - 1.0f); + val = val * (1 - slide) + -10 * slide; + } + + buffer[p] = val; + p++; + } + } + } + + delete [] pnr.data; + delete [] ar.data; + delete [] br.data; + delete [] sr.data; + delete [] dr.data; + delete [] fi.data; + delete [] fis.data; + + return buffer; + +} + +bool RandomLevelSource::hasChunk(int x, int y) +{ + return true; +} + +void RandomLevelSource::calcWaterDepths(ChunkSource *parent, int xt, int zt) +{ + int xo = xt * 16; + int zo = zt * 16; + for (int x = 0; x < 16; x++) + { + int y = level->getSeaLevel(); + for (int z = 0; z < 16; z++) + { + int xp = xo + x + 7; + int zp = zo + z + 7; + int h = level->getHeightmap(xp, zp); + if (h <= 0) + { + if (level->getHeightmap(xp - 1, zp) > 0 || level->getHeightmap(xp + 1, zp) > 0 || level->getHeightmap(xp, zp - 1) > 0 || level->getHeightmap(xp, zp + 1) > 0) + { + bool hadWater = false; + if (hadWater || (level->getTile(xp - 1, y, zp) == Tile::calmWater_Id && level->getData(xp - 1, y, zp) < 7)) hadWater = true; + if (hadWater || (level->getTile(xp + 1, y, zp) == Tile::calmWater_Id && level->getData(xp + 1, y, zp) < 7)) hadWater = true; + if (hadWater || (level->getTile(xp, y, zp - 1) == Tile::calmWater_Id && level->getData(xp, y, zp - 1) < 7)) hadWater = true; + if (hadWater || (level->getTile(xp, y, zp + 1) == Tile::calmWater_Id && level->getData(xp, y, zp + 1) < 7)) hadWater = true; + if (hadWater) + { + for (int x2 = -5; x2 <= 5; x2++) + { + for (int z2 = -5; z2 <= 5; z2++) + { + int d = (x2 > 0 ? x2 : -x2) + (z2 > 0 ? z2 : -z2); + + if (d <= 5) + { + d = 6 - d; + if (level->getTile(xp + x2, y, zp + z2) == Tile::calmWater_Id) + { + int od = level->getData(xp + x2, y, zp + z2); + if (od < 7 && od < d) + { + level->setData(xp + x2, y, zp + z2, d); + } + } + } + } + } + if (hadWater) + { + level->setTileAndDataNoUpdate(xp, y, zp, Tile::calmWater_Id, 7); + for (int y2 = 0; y2 < y; y2++) + { + level->setTileAndDataNoUpdate(xp, y2, zp, Tile::calmWater_Id, 8); + } + } + } + } + } + } + } + +} + +// 4J - changed this to used pprandom rather than random, so that we can run it concurrently with getChunk +void RandomLevelSource::postProcess(ChunkSource *parent, int xt, int zt) +{ + HeavyTile::instaFall = true; + int xo = xt * 16; + int zo = zt * 16; + + Biome *biome = level->getBiome(xo + 16, zo + 16); + + if (RandomLevelSource::FLOATING_ISLANDS) + { + calcWaterDepths(parent, xt, zt); + } + + pprandom->setSeed(level->getSeed()); + __int64 xScale = pprandom->nextLong() / 2 * 2 + 1; + __int64 zScale = pprandom->nextLong() / 2 * 2 + 1; + pprandom->setSeed(((xt * xScale) + (zt * zScale)) ^ level->getSeed()); + + bool hasVillage = false; + + PIXBeginNamedEvent(0,"Structure postprocessing"); + if (generateStructures) + { + mineShaftFeature->postProcess(level, pprandom, xt, zt); + hasVillage = villageFeature->postProcess(level, pprandom, xt, zt); + strongholdFeature->postProcess(level, pprandom, xt, zt); + scatteredFeature->postProcess(level, random, xt, zt); + } + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Lakes"); + if (!hasVillage && pprandom->nextInt(4) == 0) + { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(Level::genDepth); + int z = zo + pprandom->nextInt(16) + 8; + + LakeFeature *calmWater = new LakeFeature(Tile::calmWater_Id); + calmWater->place(level, pprandom, x, y, z); + delete calmWater; + } + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Lava"); + if (!hasVillage && pprandom->nextInt(8) == 0) + { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(pprandom->nextInt(Level::genDepth - 8) + 8); + int z = zo + pprandom->nextInt(16) + 8; + if (y < level->seaLevel || pprandom->nextInt(10) == 0) + { + LakeFeature *calmLava = new LakeFeature(Tile::calmLava_Id); + calmLava->place(level, pprandom, x, y, z); + delete calmLava; + } + } + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Monster rooms"); + for (int i = 0; i < 8; i++) { + int x = xo + pprandom->nextInt(16) + 8; + int y = pprandom->nextInt(Level::genDepth); + int z = zo + pprandom->nextInt(16) + 8; + MonsterRoomFeature *mrf = new MonsterRoomFeature(); + if (mrf->place(level, pprandom, x, y, z)) + { + } + delete mrf; + } + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Biome decorate"); + biome->decorate(level, pprandom, xo, zo); + PIXEndNamedEvent(); + + app.processSchematics(parent->getChunk(xt,zt)); + + MobSpawner::postProcessSpawnMobs(level, biome, xo + 8, zo + 8, 16, 16, pprandom); + + // 4J - brought forward from 1.2.3 to get snow back in taiga biomes + xo += 8; + zo += 8; + for (int x = 0; x < 16; x++) + { + for (int z = 0; z < 16; z++) + { + int y = level->getTopRainBlock(xo + x, zo + z); + + if (level->shouldFreezeIgnoreNeighbors(x + xo, y - 1, z + zo)) + { + level->setTileNoUpdate(x + xo, y - 1, z + zo, Tile::ice_Id); // 4J - changed from setTile, otherwise we end up creating a *lot* of dynamic water tiles as these ice tiles are set + } + if (level->shouldSnow(x + xo, y, z + zo)) + { + level->setTile(x + xo, y, z + zo, Tile::topSnow_Id); + } + } + } + + HeavyTile::instaFall = false; +} + +bool RandomLevelSource::save(bool force, ProgressListener *progressListener) +{ + return true; +} + +bool RandomLevelSource::tick() +{ + return false; +} + +bool RandomLevelSource::shouldSave() +{ + return true; +} + +wstring RandomLevelSource::gatherStats() +{ + return L"RandomLevelSource"; +} + +vector *RandomLevelSource::getMobsAt(MobCategory *mobCategory, int x, int y, int z) +{ + Biome *biome = level->getBiome(x, z); + if (biome == NULL) + { + return NULL; + } + return biome->getMobs(mobCategory); +} + +TilePos *RandomLevelSource::findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z) +{ + if (LargeFeature::STRONGHOLD == featureName && strongholdFeature != NULL) + { + return strongholdFeature->getNearestGeneratedFeature(level, x, y, z); + } + return NULL; +} diff --git a/Minecraft.World/RandomLevelSource.h b/Minecraft.World/RandomLevelSource.h new file mode 100644 index 00000000..d71028b1 --- /dev/null +++ b/Minecraft.World/RandomLevelSource.h @@ -0,0 +1,92 @@ +#pragma once +using namespace std; + +#include "ChunkSource.h" + +class ProgressListener; +class LargeFeature; +class StrongholdFeature; +class VillageFeature; +class MineShaftFeature; +class PerlinNoise; +class RandomScatteredLargeFeature; + +class RandomLevelSource : public ChunkSource +{ +public: + static const double SNOW_CUTOFF; + static const double SNOW_SCALE; + static const bool FLOATING_ISLANDS = false; + static const int CHUNK_HEIGHT = 8; + static const int CHUNK_WIDTH = 4; + +private: + Random *random; + Random *pprandom; // 4J - added + + PerlinNoise *lperlinNoise1; + PerlinNoise *lperlinNoise2; + PerlinNoise *perlinNoise1; + PerlinNoise *perlinNoise3; + +public: + PerlinNoise *scaleNoise; + PerlinNoise *depthNoise; + +private: + PerlinNoise *floatingIslandScale; + PerlinNoise *floatingIslandNoise; + +public: + PerlinNoise *forestNoise; + +private: + Level *level; + const bool generateStructures; + + floatArray pows; + +public: + RandomLevelSource(Level *level, __int64 seed, bool generateStructures); + ~RandomLevelSource(); + +public: + void prepareHeights(int xOffs, int zOffs, byteArray blocks); + +public: + void buildSurfaces(int xOffs, int zOffs, byteArray blocks, BiomeArray biomes); + +private: + LargeFeature *caveFeature; + StrongholdFeature *strongholdFeature; + VillageFeature *villageFeature; + MineShaftFeature *mineShaftFeature; + RandomScatteredLargeFeature *scatteredFeature; + LargeFeature *canyonFeature; +private: + virtual LevelChunk *create(int x, int z); + +public: + virtual LevelChunk *getChunk(int xOffs, int zOffs); + virtual void lightChunk(LevelChunk *lc); // 4J added + +private: + doubleArray getHeights(doubleArray buffer, int x, int y, int z, int xSize, int ySize, int zSize, BiomeArray& biomes); + +public: + virtual bool hasChunk(int x, int y); + +private: + void calcWaterDepths(ChunkSource *parent, int xt, int zt); + +public: + virtual void postProcess(ChunkSource *parent, int xt, int zt); + virtual bool save(bool force, ProgressListener *progressListener); + virtual bool tick(); + virtual bool shouldSave(); + virtual wstring gatherStats(); + +public: + virtual vector *getMobsAt(MobCategory *mobCategory, int x, int y, int z); + virtual TilePos *findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z); +}; diff --git a/Minecraft.World/RandomLookAroundGoal.cpp b/Minecraft.World/RandomLookAroundGoal.cpp new file mode 100644 index 00000000..679386fe --- /dev/null +++ b/Minecraft.World/RandomLookAroundGoal.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "RandomLookAroundGoal.h" + +RandomLookAroundGoal::RandomLookAroundGoal(Mob *mob) +{ + relX = relZ = 0.0; + lookTime = 0; + + this->mob = mob; + setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag); +} + +bool RandomLookAroundGoal::canUse() +{ + return mob->getRandom()->nextFloat() < 0.02f; +} + +bool RandomLookAroundGoal::canContinueToUse() +{ + return lookTime >= 0; +} + +void RandomLookAroundGoal::start() +{ + double rnd = 2 * PI * mob->getRandom()->nextDouble(); + relX = cos(rnd); + relZ = sin(rnd); + lookTime = 20 + mob->getRandom()->nextInt(20); +} + +void RandomLookAroundGoal::tick() +{ + --lookTime; + mob->getLookControl()->setLookAt(mob->x + relX, mob->y + mob->getHeadHeight(), mob->z + relZ, 10, mob->getMaxHeadXRot()); +} \ No newline at end of file diff --git a/Minecraft.World/RandomLookAroundGoal.h b/Minecraft.World/RandomLookAroundGoal.h new file mode 100644 index 00000000..76d4c7ad --- /dev/null +++ b/Minecraft.World/RandomLookAroundGoal.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Goal.h" + +class RandomLookAroundGoal : public Goal +{ +private: + Mob *mob; + double relX, relZ; + int lookTime; + +public: + RandomLookAroundGoal(Mob *mob); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/RandomPos.cpp b/Minecraft.World/RandomPos.cpp new file mode 100644 index 00000000..c2cfd908 --- /dev/null +++ b/Minecraft.World/RandomPos.cpp @@ -0,0 +1,88 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.phys.h" +#include "RandomPos.h" + +Vec3 *RandomPos::tempDir = Vec3::newPermanent(0, 0, 0); + +Vec3 *RandomPos::getPos(shared_ptr mob, int xzDist, int yDist, int quadrant/*=-1*/) // 4J - added quadrant +{ + return generateRandomPos(mob, xzDist, yDist, NULL, quadrant); +} + +Vec3 *RandomPos::getPosTowards(shared_ptr mob, int xzDist, int yDist, Vec3 *towardsPos) +{ + tempDir->x = towardsPos->x - mob->x; + tempDir->y = towardsPos->y - mob->y; + tempDir->z = towardsPos->z - mob->z; + return generateRandomPos(mob, xzDist, yDist, tempDir); +} + +Vec3 *RandomPos::getPosAvoid(shared_ptr mob, int xzDist, int yDist, Vec3 *avoidPos) +{ + tempDir->x = mob->x - avoidPos->x; + tempDir->y = mob->y - avoidPos->y; + tempDir->z = mob->z - avoidPos->z; + return generateRandomPos(mob, xzDist, yDist, tempDir); +} + +Vec3 *RandomPos::generateRandomPos(shared_ptr mob, int xzDist, int yDist, Vec3 *dir, int quadrant/*=-1*/) // 4J - added quadrant +{ + Random *random = mob->getRandom(); + bool hasBest = false; + int xBest = 0, yBest = 0, zBest = 0; + float best = -99999; + + // 4J Stu - restrict is a reserved keyword + bool bRestrict; + if (mob->hasRestriction()) + { + double restDist = mob->getRestrictCenter()->distSqr(Mth::floor(mob->x), Mth::floor(mob->y), Mth::floor(mob->z)) + 4; + double radius = mob->getRestrictRadius() + xzDist; + bRestrict = restDist < radius * radius; + } + else bRestrict = false; + + for (int i = 0; i < 10; i++) + { + int xt, yt, zt; + // 4J - added quadrant here so that we can choose to select positions only within the one quadrant. Passing a parameter of -1 will + // lead to normal java behaviour + if( quadrant == -1 ) + { + xt = random->nextInt(2 * xzDist) - xzDist; + zt = random->nextInt(2 * xzDist) - xzDist; + } + else + { + int sx = ( ( quadrant & 1 ) ? -1 : 1 ); + int sz = ( ( quadrant & 2 ) ? -1 : 1 ); + xt = random->nextInt(xzDist) * sx; + zt = random->nextInt(xzDist) * sz; + } + yt = random->nextInt(2 * yDist) - yDist; + + if (dir != NULL && xt * dir->x + zt * dir->z < 0) continue; + + xt += Mth::floor(mob->x); + yt += Mth::floor(mob->y); + zt += Mth::floor(mob->z); + + if (bRestrict && !mob->isWithinRestriction(xt, yt, zt)) continue; + float value = mob->getWalkTargetValue(xt, yt, zt); + if (value > best) + { + best = value; + xBest = xt; + yBest = yt; + zBest = zt; + hasBest = true; + } + } + if (hasBest) + { + return Vec3::newTemp(xBest, yBest, zBest); + } + + return NULL; +} \ No newline at end of file diff --git a/Minecraft.World/RandomPos.h b/Minecraft.World/RandomPos.h new file mode 100644 index 00000000..eceed17e --- /dev/null +++ b/Minecraft.World/RandomPos.h @@ -0,0 +1,17 @@ +#pragma once + +class PathfinderMob; + +class RandomPos +{ +private: + static Vec3 *tempDir; + +public: + static Vec3 *getPos(shared_ptr mob, int xzDist, int yDist, int quadrant = -1); // 4J added quadrant + static Vec3 *getPosTowards(shared_ptr mob, int xzDist, int yDist, Vec3 *towardsPos); + static Vec3 *getPosAvoid(shared_ptr mob, int xzDist, int yDist, Vec3 *avoidPos); + +private: + static Vec3 *generateRandomPos(shared_ptr mob, int xzDist, int yDist, Vec3 *dir, int quadrant = -1); // 4J added quadrant +}; \ No newline at end of file diff --git a/Minecraft.World/RandomScatteredLargeFeature.cpp b/Minecraft.World/RandomScatteredLargeFeature.cpp new file mode 100644 index 00000000..d460dc14 --- /dev/null +++ b/Minecraft.World/RandomScatteredLargeFeature.cpp @@ -0,0 +1,84 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "ScatteredFeaturePieces.h" +#include "RandomScatteredLargeFeature.h" + +vector RandomScatteredLargeFeature::allowedBiomes; + +void RandomScatteredLargeFeature::staticCtor() +{ + allowedBiomes.push_back( Biome::desert ); + allowedBiomes.push_back( Biome::desertHills ); + allowedBiomes.push_back( Biome::jungle ); +} + +RandomScatteredLargeFeature::RandomScatteredLargeFeature() +{ +} + +bool RandomScatteredLargeFeature::isFeatureChunk(int x, int z, bool bIsSuperflat) +{ + int featureSpacing = 32; + int minFeatureSeparation = 8; + + int xx = x; + int zz = z; + if (x < 0) x -= featureSpacing - 1; + if (z < 0) z -= featureSpacing - 1; + + int xCenterFeatureChunk = x / featureSpacing; + int zCenterFeatureChunk = z / featureSpacing; + Random *r = level->getRandomFor(xCenterFeatureChunk, zCenterFeatureChunk, 14357617); + xCenterFeatureChunk *= featureSpacing; + zCenterFeatureChunk *= featureSpacing; + xCenterFeatureChunk += r->nextInt(featureSpacing - minFeatureSeparation); + zCenterFeatureChunk += r->nextInt(featureSpacing - minFeatureSeparation); + x = xx; + z = zz; + + bool forcePlacement = false; + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + if( levelGenOptions != NULL ) + { + forcePlacement = levelGenOptions->isFeatureChunk(x,z,eFeature_Temples); + } + + if (forcePlacement || (x == xCenterFeatureChunk && z == zCenterFeatureChunk)) + { + bool biomeOk = level->getBiomeSource()->containsOnly(x * 16 + 8, z * 16 + 8, 0, allowedBiomes); + if (biomeOk) + { + // System.out.println("feature at " + (x * 16) + " " + (z * 16)); + return true; + } + } + + return false; + +} + +StructureStart *RandomScatteredLargeFeature::createStructureStart(int x, int z) +{ + // System.out.println("feature at " + (x * 16) + " " + (z * 16)); + return new ScatteredFeatureStart(level, random, x, z); +} + + +RandomScatteredLargeFeature::ScatteredFeatureStart::ScatteredFeatureStart(Level *level, Random *random, int chunkX, int chunkZ) +{ + if (level->getBiome(chunkX * 16 + 8, chunkZ * 16 + 8) == Biome::jungle) + { + ScatteredFeaturePieces::JunglePyramidPiece *startRoom = new ScatteredFeaturePieces::JunglePyramidPiece(random, chunkX * 16, chunkZ * 16); + pieces.push_back(startRoom); + // System.out.println("jungle feature at " + (chunkX * 16) + " " + (chunkZ * 16)); + } + else + { + ScatteredFeaturePieces::DesertPyramidPiece *startRoom = new ScatteredFeaturePieces::DesertPyramidPiece(random, chunkX * 16, chunkZ * 16); + pieces.push_back(startRoom); + // System.out.println("desert feature at " + (chunkX * 16) + " " + (chunkZ * 16)); + } + + calculateBoundingBox(); +} \ No newline at end of file diff --git a/Minecraft.World/RandomScatteredLargeFeature.h b/Minecraft.World/RandomScatteredLargeFeature.h new file mode 100644 index 00000000..b7d9fc06 --- /dev/null +++ b/Minecraft.World/RandomScatteredLargeFeature.h @@ -0,0 +1,22 @@ +#pragma once + +#include "StructureFeature.h" +#include "StructureStart.h" + +class RandomScatteredLargeFeature : public StructureFeature +{ +public: + static void staticCtor(); + static vector allowedBiomes; + RandomScatteredLargeFeature(); + +protected: + virtual bool isFeatureChunk(int x, int z, bool bIsSuperflat=false); + StructureStart *createStructureStart(int x, int z); + + class ScatteredFeatureStart : public StructureStart + { + public: + ScatteredFeatureStart(Level *level, Random *random, int chunkX, int chunkZ); + }; +}; \ No newline at end of file diff --git a/Minecraft.World/RandomStrollGoal.cpp b/Minecraft.World/RandomStrollGoal.cpp new file mode 100644 index 00000000..21f3adb1 --- /dev/null +++ b/Minecraft.World/RandomStrollGoal.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.util.h" +#include "net.minecraft.world.phys.h" +#include "SharedConstants.h" +#include "RandomStrollGoal.h" + +RandomStrollGoal::RandomStrollGoal(PathfinderMob *mob, float speed) +{ + this->mob = mob; + this->speed = speed; + setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag); +} + +bool RandomStrollGoal::canUse() +{ + // 4J - altered a little so we can do some more random strolling when appropriate, to try and move any animals that aren't confined to a fenced-off region far enough to determine we can despawn them + if (mob->getNoActionTime() < SharedConstants::TICKS_PER_SECOND * 5) + { + if (mob->getRandom()->nextInt(120) == 0) + { + Vec3 *pos = RandomPos::getPos(dynamic_pointer_cast(mob->shared_from_this()), 10, 7); + if (pos == NULL) return false; + wantedX = pos->x; + wantedY = pos->y; + wantedZ = pos->z; + return true; + } + } + else + { + // This entity wouldn't normally be randomly strolling. However, if our management system says that it should do, then do. Don't + // bother waiting for random conditions to be met before picking a direction though as the point here is to see if it is possible to + // stroll out of a given area and so waiting around is just wasting time + + if( mob->isExtraWanderingEnabled() ) + { + Vec3 *pos = RandomPos::getPos(dynamic_pointer_cast(mob->shared_from_this()), 10, 7,mob->getWanderingQuadrant()); + if (pos == NULL) return false; + wantedX = pos->x; + wantedY = pos->y; + wantedZ = pos->z; + return true; + } + + } + return false; +} + +bool RandomStrollGoal::canContinueToUse() +{ + return !mob->getNavigation()->isDone(); +} + +void RandomStrollGoal::start() +{ + mob->getNavigation()->moveTo(wantedX, wantedY, wantedZ, speed); +} \ No newline at end of file diff --git a/Minecraft.World/RandomStrollGoal.h b/Minecraft.World/RandomStrollGoal.h new file mode 100644 index 00000000..e46a9c95 --- /dev/null +++ b/Minecraft.World/RandomStrollGoal.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Goal.h" + +class PathfinderMob; + +class RandomStrollGoal : public Goal +{ +private: + PathfinderMob *mob; + double wantedX, wantedY, wantedZ; + float speed; + +public: + RandomStrollGoal(PathfinderMob *mob, float speed); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); +}; \ No newline at end of file diff --git a/Minecraft.World/Rarity.cpp b/Minecraft.World/Rarity.cpp new file mode 100644 index 00000000..fde81dd7 --- /dev/null +++ b/Minecraft.World/Rarity.cpp @@ -0,0 +1,12 @@ +#include "stdafx.h" + +#include "Rarity.h" + +const Rarity *Rarity::common = new Rarity(eHTMLColor_f, L"Common"); +const Rarity *Rarity::uncommon = new Rarity(eHTMLColor_e, L"Uncommon"); +const Rarity *Rarity::rare = new Rarity(eHTMLColor_b, L"Rare"); +const Rarity *Rarity::epic = new Rarity(eHTMLColor_d, L"Epic"); + +Rarity::Rarity(eMinecraftColour color, const wstring &name) : color(color), name(name) +{ +} \ No newline at end of file diff --git a/Minecraft.World/Rarity.h b/Minecraft.World/Rarity.h new file mode 100644 index 00000000..8b0164ac --- /dev/null +++ b/Minecraft.World/Rarity.h @@ -0,0 +1,15 @@ +#pragma once + +class Rarity +{ +public: + static const Rarity *common; + static const Rarity *uncommon; + static const Rarity *rare; + static const Rarity *epic; + + const eMinecraftColour color; + const wstring name; + + Rarity(eMinecraftColour color, const wstring &name); +}; \ No newline at end of file diff --git a/Minecraft.World/ReadMe.txt b/Minecraft.World/ReadMe.txt new file mode 100644 index 00000000..ba99773a --- /dev/null +++ b/Minecraft.World/ReadMe.txt @@ -0,0 +1,30 @@ +======================================================================== + STATIC LIBRARY : Minecraft.World Project Overview +======================================================================== + +AppWizard has created this Minecraft.World library project for you. + +This file contains a summary of what you will find in each of the files that +make up your Minecraft.World application. + +Minecraft.World.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + + + +///////////////////////////////////////////////////////////////////////////// + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named Minecraft.World.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/Minecraft.World/ReadOnlyChunkCache.cpp b/Minecraft.World/ReadOnlyChunkCache.cpp new file mode 100644 index 00000000..fdab13dc --- /dev/null +++ b/Minecraft.World/ReadOnlyChunkCache.cpp @@ -0,0 +1,100 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "ReadOnlyChunkCache.h" +#include "Biome.h" + +ReadOnlyChunkCache::ReadOnlyChunkCache(Level *level, ChunkStorage *storage) +{ + chunks = LevelChunkArray(LEN * LEN); + emptyPixels = byteArray(Level::CHUNK_TILE_COUNT); + + this->level = level; + this->storage = storage; +} + +ReadOnlyChunkCache::~ReadOnlyChunkCache() +{ + for(unsigned int i = 0; i < chunks.length; ++i) + delete chunks[i]; + + delete[] chunks.data; + + delete[] emptyPixels.data; +} + +bool ReadOnlyChunkCache::hasChunk(int x, int z) +{ + int slot = (x & LEN_MASK) | ((z & LEN_MASK) * LEN); + return chunks[slot] != NULL && (chunks[slot]->isAt(x, z)); +} + +LevelChunk *ReadOnlyChunkCache::create(int x, int z) +{ + return getChunk(x, z); +} + +LevelChunk *ReadOnlyChunkCache::getChunk(int x, int z) +{ + int slot = (x & LEN_MASK) | ((z & LEN_MASK) * LEN); + // 4J - removed try/catch +// try { + if (!hasChunk(x, z)) + { + LevelChunk *newChunk = load(x, z); + if (newChunk == NULL) + { + newChunk = new EmptyLevelChunk(level, emptyPixels, x, z); + } + chunks[slot] = newChunk; + } + return chunks[slot]; +// } catch (Exception e) { +// e.printStackTrace(); +// return null; +// } +} + +LevelChunk *ReadOnlyChunkCache::load(int x, int z) +{ + // 4J - remove try/catch +// try { + return storage->load(level, x, z); +// } catch (IOException e) { +// e.printStackTrace(); +// return null; +// } +} + // 4J - TODO - was synchronized +void ReadOnlyChunkCache::postProcess(ChunkSource *parent, int x, int z) +{ +} + +bool ReadOnlyChunkCache::save(bool force, ProgressListener *progressListener) +{ + return true; +} + +bool ReadOnlyChunkCache::tick() +{ + return false; +} + +bool ReadOnlyChunkCache::shouldSave() +{ + return false; +} + +wstring ReadOnlyChunkCache::gatherStats() +{ + return L"ReadOnlyChunkCache"; +} + +vector *ReadOnlyChunkCache::getMobsAt(MobCategory *mobCategory, int x, int y, int z) +{ + return NULL; +} + +TilePos *ReadOnlyChunkCache::findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z) +{ + return NULL; +} diff --git a/Minecraft.World/ReadOnlyChunkCache.h b/Minecraft.World/ReadOnlyChunkCache.h new file mode 100644 index 00000000..04e4a09d --- /dev/null +++ b/Minecraft.World/ReadOnlyChunkCache.h @@ -0,0 +1,39 @@ +#pragma once +#include "ChunkSource.h" +#include "LevelChunk.h" +#include "EmptyLevelChunk.h" +#include "ChunkStorage.h" + +class ProgressListener; + +class ReadOnlyChunkCache : public ChunkSource +{ +private: + static const int LEN = 16; + static const int LEN_MASK = LEN - 1; + LevelChunkArray chunks; + Level *level; + ChunkStorage *storage; + +public: + ReadOnlyChunkCache(Level *level, ChunkStorage *storage); + virtual ~ReadOnlyChunkCache(); + + virtual bool hasChunk(int x, int z); + byteArray emptyPixels; + virtual LevelChunk *create(int x, int z); + virtual LevelChunk *getChunk(int x, int z); + +private: + LevelChunk *load(int x, int z); // 4J - TODO - was synchronized + +public: + virtual void postProcess(ChunkSource *parent, int x, int z); + virtual bool save(bool force, ProgressListener *progressListener); + virtual bool tick(); + virtual bool shouldSave(); + virtual wstring gatherStats(); + + virtual vector *getMobsAt(MobCategory *mobCategory, int x, int y, int z); + virtual TilePos *findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z); +}; diff --git a/Minecraft.World/Reader.h b/Minecraft.World/Reader.h new file mode 100644 index 00000000..864eba2b --- /dev/null +++ b/Minecraft.World/Reader.h @@ -0,0 +1,11 @@ +#pragma once + +class Reader +{ +public: + virtual ~Reader() {} + + virtual void close() = 0; //Closes the stream and releases any system resources associated with it. + virtual int read() = 0; //Reads a single character. + virtual int read(wchar_t cbuf[], unsigned int off, unsigned int len) = 0; //Reads characters into a portion of an array. +}; \ No newline at end of file diff --git a/Minecraft.World/Recipes.cpp b/Minecraft.World/Recipes.cpp new file mode 100644 index 00000000..93be1dfb --- /dev/null +++ b/Minecraft.World/Recipes.cpp @@ -0,0 +1,1220 @@ +/*package net.minecraft.world.Item::crafting; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.world.inventory.CraftingContainer; +import net.minecraft.world.Item::CoalItem; +import net.minecraft.world.Item::Item; +import net.minecraft.world.Item::ItemInstance; +import net.minecraft.world.level.Tile::StoneSlabTile; +import net.minecraft.world.level.Tile::Tile;*/ + +#include "stdafx.h" +#include "Container.h" +#include "AbstractContainerMenu.h" +#include "CraftingContainer.h" +#include "CoalItem.h" +#include "Item.h" +#include "ItemInstance.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.crafting.h" + +Recipes *Recipes::instance = NULL; +ArmorRecipes *Recipes::pArmorRecipes=NULL; +ClothDyeRecipes *Recipes::pClothDyeRecipes=NULL; +FoodRecipies *Recipes::pFoodRecipies=NULL; +OreRecipies *Recipes::pOreRecipies=NULL; +StructureRecipies *Recipes::pStructureRecipies=NULL; +ToolRecipies *Recipes::pToolRecipies=NULL; +WeaponRecipies *Recipes::pWeaponRecipies=NULL; + +void Recipes::staticCtor() +{ + Recipes::instance = new Recipes(); + +} + +void Recipes::_init() +{ + // 4J Jev: instance = new Recipes(); + recipies = new RecipyList(); +} + +Recipes::Recipes() +{ + int iCount=0; + _init(); + + pArmorRecipes = new ArmorRecipes; + pClothDyeRecipes = new ClothDyeRecipes; + pFoodRecipies = new FoodRecipies; + pOreRecipies = new OreRecipies; + pStructureRecipies = new StructureRecipies; + pToolRecipies = new ToolRecipies; + pWeaponRecipies = new WeaponRecipies; + + // 4J Stu - These just don't work with our crafting menu + //recipies->push_back(new ArmorDyeRecipe()); + + addShapedRecipy(new ItemInstance(Tile::wood, 4, 0), // + L"sczg", + L"#", // + + L'#', new ItemInstance(Tile::treeTrunk, 1, 0), + L'S'); + + // TU9 - adding coloured wood + addShapedRecipy(new ItemInstance(Tile::wood, 4, TreeTile::BIRCH_TRUNK), // + L"sczg", + L"#", // + + L'#', new ItemInstance(Tile::treeTrunk, 1, TreeTile::BIRCH_TRUNK), + L'S'); + + addShapedRecipy(new ItemInstance(Tile::wood, 4, TreeTile::DARK_TRUNK), // + L"sczg", + L"#", // + + L'#', new ItemInstance(Tile::treeTrunk, 1, TreeTile::DARK_TRUNK), + L'S'); + + addShapedRecipy(new ItemInstance(Tile::wood, 4, TreeTile::JUNGLE_TRUNK), // + L"sczg", + L"#", // + + L'#', new ItemInstance(Tile::treeTrunk, 1, TreeTile::JUNGLE_TRUNK), + L'S'); + + addShapedRecipy(new ItemInstance(Item::stick, 4), // + L"ssctg", + L"#", // + L"#", // + + L'#', Tile::wood, + L'S'); + + pToolRecipies->addRecipes(this); + pFoodRecipies->addRecipes(this); + pStructureRecipies->addRecipes(this); + + + // 4J-PB - changing the order to the way we want to have things in the crafting menu + // bed + addShapedRecipy(new ItemInstance(Item::bed, 1), // + L"ssctctg", + L"###", // + L"XXX", // + L'#', Tile::cloth, L'X', Tile::wood, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::enchantTable, 1), // + L"sssctcicig", + L" B ", // + L"D#D", // + L"###", // + + L'#', Tile::obsidian, L'B', Item::book, L'D', Item::diamond, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::anvil, 1), // + L"sssctcig", + L"III", // + L" i ", // + L"iii", // + + L'I', Tile::ironBlock, L'i', Item::ironIngot, + L'S'); + + // 4J Stu - Reordered for crafting menu + addShapedRecipy(new ItemInstance(Tile::ladder, 3), // + L"ssscig", + L"# #", // + L"###", // + L"# #", // + + L'#', Item::stick, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::fenceGate, 1), // + L"sscictg", + L"#W#", // + L"#W#", // + + L'#', Item::stick, L'W', Tile::wood, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::fence, 2), // + L"sscig", + L"###", // + L"###", // + + L'#', Item::stick, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::netherFence, 6), // + L"ssctg", + L"###", // + L"###", // + + L'#', Tile::netherBrick, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::ironFence, 16), // + L"sscig", + L"###", // + L"###", // + + L'#', Item::ironIngot, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::cobbleWall, 6, WallTile::TYPE_NORMAL), // + L"ssctg", + L"###", // + L"###", // + + L'#', Tile::stoneBrick, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::cobbleWall, 6, WallTile::TYPE_MOSSY), // + L"ssctg", + L"###", // + L"###", // + + L'#', Tile::mossStone, + L'S'); + + addShapedRecipy(new ItemInstance(Item::door_wood, 1), // + L"sssctg", + L"##", // + L"##", // + L"##", // + + L'#', Tile::wood, + L'S'); + + addShapedRecipy(new ItemInstance(Item::door_iron, 1), // + L"ssscig", + L"##", // + L"##", // + L"##", // + + L'#', Item::ironIngot, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::stairs_wood, 4), // + L"sssczg", + L"# ", // + L"## ", // + L"###", // + + L'#', new ItemInstance(Tile::wood, 1, 0), + L'S'); + + addShapedRecipy(new ItemInstance(Tile::trapdoor, 2), // + L"ssctg", + L"###", // + L"###", // + + L'#', Tile::wood, + L'S'); + addShapedRecipy(new ItemInstance(Tile::stairs_stone, 4), // + L"sssctg", + L"# ", // + L"## ", // + L"###", // + + L'#', Tile::stoneBrick, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::stairs_bricks, 4), // + L"sssctg", + L"# ", // + L"## ", // + L"###", // + + L'#', Tile::redBrick, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::stairs_stoneBrickSmooth, 4), // + L"sssctg", + L"# ", // + L"## ", // + L"###", // + + L'#', Tile::stoneBrickSmooth, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::stairs_netherBricks, 4), // + L"sssctg", + L"# ", // + L"## ", // + L"###", // + + L'#', Tile::netherBrick, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::stairs_sandstone, 4), // + L"sssctg", + L"# ", // + L"## ", // + L"###", // + + L'#', Tile::sandStone, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::woodStairsBirch, 4), // + L"sssczg", + L"# ", // + L"## ", // + L"###", // + + L'#', new ItemInstance(Tile::wood, 1, TreeTile::BIRCH_TRUNK), + L'S'); + + addShapedRecipy(new ItemInstance(Tile::woodStairsDark, 4), // + L"sssczg", + L"# ", // + L"## ", // + L"###", // + + L'#', new ItemInstance(Tile::wood, 1, TreeTile::DARK_TRUNK), + L'S'); + + addShapedRecipy(new ItemInstance(Tile::woodStairsJungle, 4), // + L"sssczg", + L"# ", // + L"## ", // + L"###", // + + L'#', new ItemInstance(Tile::wood, 1, TreeTile::JUNGLE_TRUNK), + L'S'); + + addShapedRecipy(new ItemInstance(Tile::stairs_quartz, 4), // + L"sssctg", + L"# ", // + L"## ", // + L"###", // + + L'#', Tile::quartzBlock, + L'S'); + + pArmorRecipes->addRecipes(this); + //iCount=getRecipies()->size(); + + pClothDyeRecipes->addRecipes(this); + + + addShapedRecipy(new ItemInstance(Tile::snow, 1), // + L"sscig", + L"##", // + L"##", // + + L'#', Item::snowBall, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::clay, 1), // + L"sscig", + L"##", // + L"##", // + + L'#', Item::clay, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::redBrick, 1), // + L"sscig", + L"##", // + L"##", // + + L'#', Item::brick, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::cloth, 1), // + L"sscig", + L"##", // + L"##", // + + L'#', Item::string, + L'D'); + + addShapedRecipy(new ItemInstance(Tile::tnt, 1), // + L"ssscictg", + L"X#X", // + L"#X#", // + L"X#X", // + + L'X', Item::sulphur,// + L'#', Tile::sand, + L'T'); + + addShapedRecipy(new ItemInstance(Tile::stoneSlabHalf, 6, StoneSlabTile::SAND_SLAB), // + L"sctg", + L"###", // + + L'#', Tile::sandStone, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::stoneSlabHalf, 6, StoneSlabTile::STONE_SLAB), // + L"sctg", + L"###", // + + L'#', Tile::rock, + L'S'); + addShapedRecipy(new ItemInstance(Tile::stoneSlabHalf, 6, StoneSlabTile::COBBLESTONE_SLAB), // + L"sctg", + L"###", // + + L'#', Tile::stoneBrick, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::stoneSlabHalf, 6, StoneSlabTile::BRICK_SLAB), // + L"sctg", + L"###", // + + L'#', Tile::redBrick, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::stoneSlabHalf, 6, StoneSlabTile::SMOOTHBRICK_SLAB), // + L"sctg", + L"###", // + + L'#', Tile::stoneBrickSmooth, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::stoneSlabHalf, 6, StoneSlabTile::NETHERBRICK_SLAB), // + L"sctg", + L"###", // + + L'#', Tile::netherBrick, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::stoneSlabHalf, 6, StoneSlabTile::QUARTZ_SLAB), // + L"sctg", + L"###", // + + L'#', Tile::quartzBlock, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::woodSlabHalf, 6, 0), // + L"sczg", + L"###", // + + L'#', new ItemInstance(Tile::wood, 1, 0), + L'S'); + // TU9 - adding wood slabs + + addShapedRecipy(new ItemInstance(Tile::woodSlabHalf, 6, TreeTile::BIRCH_TRUNK), // + L"sczg", + L"###", // + + L'#', new ItemInstance(Tile::wood, 1, TreeTile::BIRCH_TRUNK), + L'S'); + + + addShapedRecipy(new ItemInstance(Tile::woodSlabHalf, 6, TreeTile::DARK_TRUNK), // + L"sczg", + L"###", // + + L'#', new ItemInstance(Tile::wood, 1, TreeTile::DARK_TRUNK), + L'S'); + + addShapedRecipy(new ItemInstance(Tile::woodSlabHalf, 6, TreeTile::JUNGLE_TRUNK), // + L"sczg", + L"###", // + + L'#', new ItemInstance(Tile::wood, 1, TreeTile::JUNGLE_TRUNK), + L'S'); + + + + + + //iCount=getRecipies()->size(); + + addShapedRecipy(new ItemInstance(Item::cake, 1), // + L"ssscicicicig", + L"AAA", // + L"BEB", // + L"CCC", // + + L'A', Item::milk,// + L'B', Item::sugar,// + L'C', Item::wheat, L'E', Item::egg, + L'F'); + + addShapedRecipy(new ItemInstance(Item::sugar, 1), // + L"scig", + L"#", // + + L'#', Item::reeds, + L'F'); + + addShapedRecipy(new ItemInstance(Tile::rail, 16), // + L"ssscicig", + L"X X", // + L"X#X", // + L"X X", // + + L'X', Item::ironIngot,// + L'#', Item::stick, + L'V'); + + addShapedRecipy(new ItemInstance(Tile::goldenRail, 6), // + L"ssscicicig", + L"X X", // + L"X#X", // + L"XRX", // + + L'X', Item::goldIngot,// + L'R', Item::redStone,// + L'#', Item::stick, + L'V'); + + addShapedRecipy(new ItemInstance(Tile::detectorRail, 6), // + L"ssscicictg", + L"X X", // + L"X#X", // + L"XRX", // + + L'X', Item::ironIngot,// + L'R', Item::redStone,// + L'#', Tile::pressurePlate_stone, + L'V'); + + addShapedRecipy(new ItemInstance(Item::minecart, 1), // + L"sscig", + L"# #", // + L"###", // + + L'#', Item::ironIngot, + L'V'); + + addShapedRecipy(new ItemInstance(Item::minecart_chest, 1), // + L"ssctcig", + L"A", // + L"B", // + + L'A', Tile::chest, L'B', Item::minecart, + L'V'); + + addShapedRecipy(new ItemInstance(Item::minecart_furnace, 1), // + L"ssctcig", + L"A", // + L"B", // + + L'A', Tile::furnace, L'B', Item::minecart, + L'V'); + + addShapedRecipy(new ItemInstance(Item::boat, 1), // + L"ssctg", + L"# #", // + L"###", // + + L'#', Tile::wood, + L'V'); + + addShapedRecipy(new ItemInstance((Item *)Item::fishingRod, 1), // + L"ssscicig", + L" #", // + L" #X", // + L"# X", // + + L'#', Item::stick, L'X', Item::string, + L'T'); + + addShapedRecipy(new ItemInstance(Item::carrotOnAStick, 1), // + L"sscicig", + L"# ", // + L" X", // + + L'#', Item::fishingRod, L'X', Item::carrots, + L'T')->keepTag(); + + addShapedRecipy(new ItemInstance(Item::flintAndSteel, 1), // + L"sscicig", + L"A ", // + L" B", // + + L'A', Item::ironIngot, L'B', Item::flint, + L'T'); + + addShapedRecipy(new ItemInstance(Item::bread, 1), // + L"scig", + L"###", // + + L'#', Item::wheat, + L'F'); + + // Moved bow and arrow in from weapons to avoid stacking on the group name display + addShapedRecipy(new ItemInstance((Item *)Item::bow, 1), // + L"ssscicig", + L" #X", // + L"# X", // + L" #X", // + + L'X', Item::string,// + L'#', Item::stick, + L'T'); + + addShapedRecipy(new ItemInstance(Item::arrow, 4), // + L"ssscicicig", + L"X", // + L"#", // + L"Y", // + + L'Y', Item::feather,// + L'X', Item::flint,// + L'#', Item::stick, + L'T'); + + pWeaponRecipies->addRecipes(this); + + addShapedRecipy(new ItemInstance(Item::bucket_empty, 1), // + L"sscig", + L"# #", // + L" # ", // + + L'#', Item::ironIngot, + L'T'); + + addShapedRecipy(new ItemInstance(Item::bowl, 4), // + L"ssctg", + L"# #", // + L" # ", // + + L'#', Tile::wood, + L'T'); + + addShapedRecipy(new ItemInstance(Item::glassBottle, 3), // + L"ssctg", + L"# #", // + L" # ", // + + L'#', Tile::glass, + L'T'); + + addShapedRecipy(new ItemInstance(Item::flowerPot, 1), // + L"sscig", + L"# #", // + L" # ", // + + L'#', Item::brick, + L'D'); + + // torch made of charcoal - moved to be the default due to the tutorial using it + addShapedRecipy(new ItemInstance(Tile::torch, 4), // + L"ssczcig", + L"X", // + L"#", // + + L'X', new ItemInstance(Item::coal, 1, CoalItem::CHAR_COAL),// + L'#', Item::stick, + L'T'); + + addShapedRecipy(new ItemInstance(Tile::torch, 4), // + L"sscicig", + L"X", // + L"#", // + L'X', Item::coal,// + L'#', Item::stick, + L'T'); + + addShapedRecipy(new ItemInstance(Tile::lightGem, 1), // + L"sscig", + L"##", // + L"##", // + + L'#', Item::yellowDust, + L'T'); + + addShapedRecipy(new ItemInstance(Tile::quartzBlock, 1), // + L"sscig", + L"##", // + L"##", // + + L'#', Item::netherQuartz, + L'S'); + + addShapedRecipy(new ItemInstance(Tile::lever, 1), // + L"ssctcig", + L"X", // + L"#", // + + L'#', Tile::stoneBrick, L'X', Item::stick, + L'M'); + + addShapedRecipy(new ItemInstance(Tile::tripWireSource, 2), // + L"sssctcicig", + L"I", // + L"S", // + L"#", // + + L'#', Tile::wood, L'S', Item::stick, L'I', Item::ironIngot, + L'M'); + + addShapedRecipy(new ItemInstance(Tile::notGate_on, 1), // + L"sscicig", + L"X", // + L"#", // + + L'#', Item::stick, L'X', Item::redStone, + L'M'); + + addShapedRecipy(new ItemInstance(Item::diode, 1), // + L"ssctcictg", + L"#X#", // + L"III", // + + L'#', Tile::notGate_on, L'X', Item::redStone, L'I', Tile::rock, + L'M'); + + + addShapedRecipy(new ItemInstance(Item::clock, 1), // + L"ssscicig", + L" # ", // + L"#X#", // + L" # ", // + L'#', Item::goldIngot, L'X', Item::redStone, + L'T'); + + addShapelessRecipy(new ItemInstance(Item::eyeOfEnder, 1), // + L"iig", + Item::enderPearl, Item::blazePowder, + L'T'); + + addShapelessRecipy(new ItemInstance(Item::fireball, 3), // + L"iiig", + Item::sulphur, Item::blazePowder,Item::coal, + L'T'); + + addShapelessRecipy(new ItemInstance(Item::fireball, 3), // + L"iizg", + Item::sulphur, Item::blazePowder,new ItemInstance(Item::coal, 1, CoalItem::CHAR_COAL), + L'T'); + + + addShapedRecipy(new ItemInstance(Item::compass, 1), // + L"ssscicig", + L" # ", // + L"#X#", // + L" # ", // + + L'#', Item::ironIngot, L'X', Item::redStone, + L'T'); + + // 4J-PB Added a MapItem init + addShapedRecipy(new ItemInstance(Item::map, 1), // + L"ssscicig", + L"###", // + L"#X#", // + L"###", // + + L'#', Item::paper, L'X', Item::compass, + L'T'); + + addShapedRecipy(new ItemInstance(Tile::button, 1), // + L"sctg", + L"#", // + //L"#", // + L'#', Tile::rock, + L'M'); + + addShapedRecipy(new ItemInstance(Tile::button_wood, 1), // + L"sctg", + L"#", // + //L"#", // + L'#', Tile::wood, + L'M'); + + addShapedRecipy(new ItemInstance(Tile::pressurePlate_wood, 1), // + L"sctg", + L"##", // + L'#', Tile::wood, + L'M'); + + addShapedRecipy(new ItemInstance(Tile::pressurePlate_stone, 1), // + L"sctg", + L"##", // + L'#', Tile::rock, + L'M'); + + + addShapedRecipy(new ItemInstance(Tile::dispenser, 1), // + L"sssctcicig", + L"###", // + L"#X#", // + L"#R#", // + L'#', Tile::stoneBrick, L'X', Item::bow, L'R', Item::redStone, + L'M'); + + addShapedRecipy(new ItemInstance(Item::cauldron, 1), // + L"ssscig", + L"# #", // + L"# #", // + L"###", // + + L'#', Item::ironIngot, + L'T'); + + addShapedRecipy(new ItemInstance(Item::brewingStand, 1), // + L"ssctcig", + L" B ", // + L"###", // + + L'#', Tile::stoneBrick, L'B', Item::blazeRod, + L'S'); + + + addShapedRecipy(new ItemInstance(Tile::litPumpkin, 1), // + L"ssctctg", + L"A", // + L"B", // + + L'A', Tile::pumpkin, L'B', Tile::torch, + L'T'); + + + addShapedRecipy(new ItemInstance(Tile::recordPlayer, 1), // + L"sssctcig", + L"###", // + L"#X#", // + L"###", // + + L'#', Tile::wood, L'X', Item::diamond, + 'D'); + + + + addShapedRecipy(new ItemInstance(Item::paper, 3), // + L"scig", + L"###", // + + L'#', Item::reeds, + L'D'); + + addShapelessRecipy(new ItemInstance(Item::book, 1), + L"iiiig", + Item::paper, + Item::paper, + Item::paper, + Item::leather, + L'D'); + + //addShapelessRecipy(new ItemInstance(Item.writingBook, 1), // + // Item.book, new ItemInstance(Item.dye_powder, 1, DyePowderItem.BLACK), Item.feather); + + addShapedRecipy(new ItemInstance(Tile::musicBlock, 1), // + L"sssctcig", + L"###", // + L"#X#", // + L"###", // + + L'#', Tile::wood, L'X', Item::redStone, + L'M'); + + addShapedRecipy(new ItemInstance(Tile::bookshelf, 1), // + L"sssctcig", + L"###", // + L"XXX", // + L"###", // + + L'#', Tile::wood, L'X', Item::book, + L'D'); + + addShapedRecipy(new ItemInstance(Item::painting, 1), // + L"ssscictg", + L"###", // + L"#X#", // + L"###", // + + L'#', Item::stick, L'X', Tile::cloth, + L'D'); + + + addShapedRecipy(new ItemInstance(Item::frame, 1), // + L"ssscicig", + L"###", // + L"#X#", // + L"###", // + + L'#', Item::stick, L'X', Item::leather, + L'D'); + + pOreRecipies->addRecipes(this); + + addShapedRecipy(new ItemInstance(Item::goldIngot), // + L"ssscig", + L"###", // + L"###", // + L"###", // + + L'#', Item::goldNugget, + L'D'); + + addShapedRecipy(new ItemInstance(Item::goldNugget, 9), // + L"scig", + L"#", // + L'#', Item::goldIngot, + L'D'); + + // 4J-PB - moving into decorations to make the structures list smaller + addShapedRecipy(new ItemInstance(Item::sign, 3), // + L"sssctcig", + L"###", // + L"###", // + L" X ", // + + L'#', Tile::wood, L'X', Item::stick, + L'D'); + + // 4J - TODO - put these new 1.7.3 items in required place within recipes + addShapedRecipy(new ItemInstance((Tile *)Tile::pistonBase, 1), // + L"sssctcicictg", + L"TTT", // + L"#X#", // + L"#R#", // + + L'#', Tile::stoneBrick, L'X', Item::ironIngot, L'R', Item::redStone, L'T', Tile::wood, + L'M'); + + addShapedRecipy(new ItemInstance((Tile *)Tile::pistonStickyBase, 1), // + L"sscictg", + L"S", // + L"P", // + + L'S', Item::slimeBall, L'P', Tile::pistonBase, + L'M'); + + + + + // Sort so the largest recipes get checked first! + /* 4J-PB - TODO + Collections.sort(recipies, new Comparator() + { + public: int compare(Recipy r0, Recipy r1) + { + + // shapeless recipes are put in the back of the list + if (r0 instanceof ShapelessRecipy && r1 instanceof ShapedRecipy) + { + return 1; + } + if (r1 instanceof ShapelessRecipy && r0 instanceof ShapedRecipy) + { + return -1; + } + + if (r1.size() < r0.size()) return -1; + if (r1.size() > r0.size()) return 1; + return 0; + } + }); + */ + + // 4J-PB removed System.out.println(recipies->size() + L" recipes"); + + // 4J-PB - build the array of ingredients required per recipe + buildRecipeIngredientsArray(); +} + +// 4J-PB - this function has been substantially changed due to the differences with a va_list of classes in C++ and Java +ShapedRecipy *Recipes::addShapedRecipy(ItemInstance *result, ...) +{ + wstring map = L""; + int p = 0; + int width = 0; + int height = 0; + int group = ShapedRecipy::eGroupType_Decoration; + va_list vl; + wchar_t *wchTypes; + wchar_t *pwchString; + wstring wString; + wstring *wStringA; + ItemInstance *pItemInstance; + Tile *pTile; + Item *pItem; + wchar_t wchFrom; + int iCount; + ItemInstance **ids = NULL; + + myMap *mappings = new unordered_map(); + + va_start(vl,result); + // 4J-PB - second argument is a list of the types + // s - string + // w - string array + // a - char * + // c - char + // z - ItemInstance * + // i - Item * + // t - Tile * + // g - group [wt] - which group does the item created by the recipe belong in. Set a default until all recipes have a group + + wchTypes = va_arg(vl,wchar_t *); + + for(int i = 0; wchTypes[i] != L'\0'; ++i ) + { + if(wchTypes[i+1]==L'\0' && wchTypes[i]!=L'g') + { + app.DebugPrintf("Missing group type\n"); + } + + switch(wchTypes[i]) + { + case L'a': + pwchString=va_arg(vl,wchar_t *); + wString=pwchString; + height++; + width = (int)wString.length(); + map += wString; + break; + case L's': + pwchString=va_arg(vl,wchar_t *); + wString=pwchString; + height++; + width = (int)wString.length(); + map += wString; + break; + case L'w': + wStringA=va_arg(vl,wstring *); + iCount=0; + do + { + wString=wStringA[iCount++]; + if(!wString.empty()) + { + height++; + width = (int)wString.length(); + map += wString; + } + } + while(!wString.empty()); + + break; + case L'c': + wchFrom=va_arg(vl,wchar_t); + break; + case L'z': + pItemInstance=va_arg(vl,ItemInstance *); + mappings->insert(myMap::value_type(wchFrom,pItemInstance)); + break; + case L'i': + pItem=va_arg(vl,Item *); + pItemInstance= new ItemInstance(pItem); + mappings->insert(myMap::value_type(wchFrom,pItemInstance)); + break; + case L't': + pTile=va_arg(vl,Tile *); + pItemInstance= new ItemInstance(pTile,1,ANY_AUX_VALUE); + mappings->insert(myMap::value_type(wchFrom,pItemInstance)); + break; + case L'g': + wchFrom=va_arg(vl,wchar_t); + switch(wchFrom) + { +// case L'W': +// group=ShapedRecipy::eGroupType_Weapon; +// break; + case L'T': + group=ShapedRecipy::eGroupType_Tool; + break; + case L'A': + group=ShapedRecipy::eGroupType_Armour; + break; + case L'S': + group=ShapedRecipy::eGroupType_Structure; + break; + case L'V': + group=ShapedRecipy::eGroupType_Transport; + break; + case L'M': + group=ShapedRecipy::eGroupType_Mechanism; + break; + case L'F': + group=ShapedRecipy::eGroupType_Food; + break; + case L'D': + default: + group=ShapedRecipy::eGroupType_Decoration; + break; + + } + break; + + } + + + ids = new ItemInstance *[width * height]; + + for (int j = 0; j < width * height; j++) + { + wchar_t ch = map[j]; + myMap::iterator it=mappings->find(ch); + if (it != mappings->end()) + { + ids[j] =it->second; + } + else + { + ids[j] = NULL; + } + } + } + + va_end(vl); + + ShapedRecipy *recipe = new ShapedRecipy(width, height, ids, result, group); + recipies->push_back(recipe); + return recipe; +} + +void Recipes::addShapelessRecipy(ItemInstance *result,... ) +{ + va_list vl; + wchar_t *szTypes; + wstring String; + ItemInstance *pItemInstance; + Tile *pTile; + Item *pItem; + Recipy::_eGroupType group = Recipy::eGroupType_Decoration; + wchar_t wchFrom; + vector *ingredients = new vector(); + + va_start(vl,result); + // 4J-PB - second argument is a list of the types + // z - ItemInstance * + // i - Item * + // t - Tile * + szTypes = va_arg(vl,wchar_t *); + + for(int i = 0; szTypes[i] != L'\0'; ++i ) + { + switch(szTypes[i]) + { + case L'z': + pItemInstance=va_arg(vl,ItemInstance *); + // 4J-PB - original code copies the item instance, copy the pointer isnt the same... + // TODO + ingredients->push_back(pItemInstance->copy_not_shared()); + break; + case L'i': + pItem=va_arg(vl,Item *); + pItemInstance= new ItemInstance(pItem); + ingredients->push_back(pItemInstance); + break; + case L't': + pTile=va_arg(vl,Tile *); + ingredients->push_back(new ItemInstance(pTile)); + break; + case L'g': + wchFrom=va_arg(vl,wchar_t); + switch(wchFrom) + { + + case L'T': + group=Recipy::eGroupType_Tool; + break; + case L'A': + group=Recipy::eGroupType_Armour; + break; + case L'S': + group=Recipy::eGroupType_Structure; + break; + case L'V': + group=Recipy::eGroupType_Transport; + break; + case L'M': + group=Recipy::eGroupType_Mechanism; + break; + case L'F': + group=Recipy::eGroupType_Food; + break; + case L'D': + default: + group=Recipy::eGroupType_Decoration; + break; + + } + break; } + } + + recipies->push_back(new ShapelessRecipy(result, ingredients, group)); +} + +shared_ptr Recipes::getItemFor(shared_ptr craftSlots, Level *level) +{ + int count = 0; + shared_ptr first = nullptr; + shared_ptr second = nullptr; + for (int i = 0; i < craftSlots->getContainerSize(); i++) + { + shared_ptr item = craftSlots->getItem(i); + if (item != NULL) + { + if (count == 0) first = item; + if (count == 1) second = item; + count++; + } + } + + if (count == 2 && first->id == second->id && first->count == 1 && second->count == 1 && Item::items[first->id]->canBeDepleted()) + { + Item *item = Item::items[first->id]; + int remaining1 = item->getMaxDamage() - first->getDamageValue(); + int remaining2 = item->getMaxDamage() - second->getDamageValue(); + int remaining = (remaining1 + remaining2) + item->getMaxDamage() * 5 / 100; + int resultDamage = item->getMaxDamage() - remaining; + if (resultDamage < 0) resultDamage = 0; + return shared_ptr( new ItemInstance(first->id, 1, resultDamage) ); + } + + AUTO_VAR(itEnd, recipies->end()); + for (AUTO_VAR(it, recipies->begin()); it != itEnd; it++) + { + Recipy *r = *it; //recipies->at(i); + if (r->matches(craftSlots, level)) return r->assemble(craftSlots); + } + return nullptr; +} + +vector *Recipes::getRecipies() +{ + return recipies; +} + +// 4J-PB - added to deal with Xb0x 'crafting' +shared_ptr Recipes::getItemForRecipe(Recipy *r) +{ + return r->assemble(nullptr); +} + +// 4J-PB - build the required ingredients for recipes +void Recipes::buildRecipeIngredientsArray(void) +{ + //RecipyList *recipes = ((Recipes *)Recipes::getInstance())->getRecipies(); + + int iRecipeC=(int)recipies->size(); + + m_pRecipeIngredientsRequired= new Recipy::INGREDIENTS_REQUIRED [iRecipeC]; + + int iCount=0; + AUTO_VAR(itEndRec, recipies->end()); + for (AUTO_VAR(it, recipies->begin()); it != itEndRec; it++) + { + Recipy *recipe = *it; + //wprintf(L"RECIPE - [%d] is %w\n",iCount,recipe->getResultItem()->getItem()->getName()); + recipe->requires(&m_pRecipeIngredientsRequired[iCount++]); + } + + //printf("Total recipes in buildRecipeIngredientsArray - %d",iCount); +} + +Recipy::INGREDIENTS_REQUIRED *Recipes::getRecipeIngredientsArray(void) +{ + return m_pRecipeIngredientsRequired; +} \ No newline at end of file diff --git a/Minecraft.World/Recipes.h b/Minecraft.World/Recipes.h new file mode 100644 index 00000000..c5d091e1 --- /dev/null +++ b/Minecraft.World/Recipes.h @@ -0,0 +1,111 @@ +/*package net.minecraft.world.item.crafting; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.world.inventory.CraftingContainer; +import net.minecraft.world.item.CoalItem; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemInstance; +import net.minecraft.world.level.tile.StoneSlabTile; +import net.minecraft.world.level.tile.Tile; +*/ + +#include "Recipy.h" + +#pragma once +using namespace std; + +class CraftingContainer; +class FireTile; + +class ArmorRecipes; +class ClothDyeRecipes; +class FoodRecipies; +class OreRecipies; +class StructureRecipies; +class ToolRecipies; +class WeaponRecipies; +class ShapedRecipy; + +typedef unordered_map myMap; + +#define ADD_OBJECT(a,b) a.push_back(new Object(b)) + +class Object +{ +public: + union + { + Tile *tile; + FireTile *firetile; + Item *item; + MapItem *mapitem; + ItemInstance *iteminstance; + }; + + Object() { eType=eTYPE_NOTSET;} + Object(Tile *t) { eType=eType_TILE;tile=t;} + Object(FireTile *t) { eType=eType_FIRETILE;firetile=t;} + Object(Item *i) { eType=eType_ITEM; item=i;} + Object(MapItem *i) { eType=eType_MAPITEM;mapitem=i;} + Object(ItemInstance *i) { eType=eType_ITEMINSTANCE;iteminstance=i;} + + eINSTANCEOF instanceof() { return eType;} + eINSTANCEOF GetType() { return eType; }; + +private: + eINSTANCEOF eType; +}; + +class Recipes +{ +public: + static const int ANY_AUX_VALUE = -1; + +private: + static Recipes *instance; + + vector *recipies; + +public: + static void staticCtor(); + +public: + static Recipes *getInstance() + { + return instance; + } + +private: + void _init(); // 4J add + Recipes(); + +public: + ShapedRecipy *addShapedRecipy(ItemInstance *, ... ); + void addShapelessRecipy(ItemInstance *result,... ); + + shared_ptr getItemFor(shared_ptr craftSlots, Level *level); + vector *getRecipies(); + + // 4J-PB - Added all below for new Xbox 'crafting' + shared_ptr getItemForRecipe(Recipy *r); + Recipy::INGREDIENTS_REQUIRED *getRecipeIngredientsArray(); + +private: + void buildRecipeIngredientsArray(); + Recipy::INGREDIENTS_REQUIRED *m_pRecipeIngredientsRequired; + + + static ToolRecipies *pToolRecipies; + static WeaponRecipies *pWeaponRecipies; + static StructureRecipies *pStructureRecipies; + static OreRecipies *pOreRecipies; + static FoodRecipies *pFoodRecipies; + static ClothDyeRecipes *pClothDyeRecipes; + static ArmorRecipes *pArmorRecipes; +}; diff --git a/Minecraft.World/Recipy.h b/Minecraft.World/Recipy.h new file mode 100644 index 00000000..88d9640a --- /dev/null +++ b/Minecraft.World/Recipy.h @@ -0,0 +1,55 @@ +// package net.minecraft.world.item.crafting; +// +// import net.minecraft.world.inventory.CraftingContainer; +// import net.minecraft.world.item.ItemInstance; + +#pragma once + +#include "CraftingContainer.h" + +#define RECIPE_TYPE_2x2 0 +#define RECIPE_TYPE_3x3 1 + +class Recipy +{ +public: + enum _eGroupType + { + eGroupType_First=0, + eGroupType_Structure=0, + eGroupType_Tool, + eGroupType_Food, + eGroupType_Armour, + eGroupType_Mechanism, + eGroupType_Transport, + eGroupType_Decoration, + eGroupType_Max + } + eGroupType; // to class the item produced by the recipe + + // 4J-PB - we'll classing an ingredient ID with a different aux value as a different IngID AuxVal pair + typedef struct + { + int iIngC; + int iType; // Can be a 2x2 or a 3x3. Inventory crafting can only make a 2x2. + int *iIngIDA; + int *iIngValA; + int *iIngAuxValA; + Recipy *pRecipy; + bool bCanMake[XUSER_MAX_COUNT]; + unsigned int *uiGridA; // hold the layout of the recipe (id | auxval<<24) + unsigned short usBitmaskMissingGridIngredients[XUSER_MAX_COUNT]; // each bit set means we don't have that grid ingredient + } + INGREDIENTS_REQUIRED; + ~Recipy() {} + virtual bool matches(shared_ptr craftSlots, Level *level) = 0; + virtual shared_ptr assemble(shared_ptr craftSlots) = 0; + virtual int size() = 0; + virtual const ItemInstance *getResultItem() = 0; + virtual const int getGroup() = 0; + + // 4J-PB + virtual bool requires(int iRecipe) = 0; + virtual void requires(INGREDIENTS_REQUIRED *pIngReq) = 0; +}; + diff --git a/Minecraft.World/RecordPlayerTile.cpp b/Minecraft.World/RecordPlayerTile.cpp new file mode 100644 index 00000000..de5f7386 --- /dev/null +++ b/Minecraft.World/RecordPlayerTile.cpp @@ -0,0 +1,100 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.h" +#include "net.minecraft.world.h" +#include "RecordPlayerTile.h" +#include "LevelEvent.h" + + +RecordPlayerTile::RecordPlayerTile(int id) : EntityTile(id, Material::wood) +{ + iconTop = NULL; +} + +Icon *RecordPlayerTile::getTexture(int face, int data) +{ + if (face == Facing::UP) + { + return iconTop; + } + return icon; +} + +// 4J-PB - Adding a TestUse for tooltip display +bool RecordPlayerTile::TestUse(Level *level, int x, int y, int z, shared_ptr player) +{ + // if the jukebox is empty, return true + if (level->getData(x, y, z) == 0) return false; + return true; +} + +bool RecordPlayerTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if (soundOnly) return false; + if (level->getData(x, y, z) == 0) return false; + dropRecording(level, x, y, z); + return true; +} + +void RecordPlayerTile::setRecord(Level *level, int x, int y, int z, int record) +{ + if (level->isClientSide) return; + + shared_ptr rte = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + rte->record = record; + rte->setChanged(); + + level->setData(x, y, z, 1); +} + +void RecordPlayerTile::dropRecording(Level *level, int x, int y, int z) +{ + if (level->isClientSide) return; + + shared_ptr rte = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + if( rte == NULL ) return; + + int oldRecord = rte->record; + if (oldRecord == 0) return; + + + level->levelEvent(LevelEvent::SOUND_PLAY_RECORDING, x, y, z, 0); + // 4J-PB- the level event will play the music + //level->playStreamingMusic(L"", x, y, z); + rte->record = 0; + rte->setChanged(); + level->setData(x, y, z, 0); + + float s = 0.7f; + double xo = level->random->nextFloat() * s + (1 - s) * 0.5; + double yo = level->random->nextFloat() * s + (1 - s) * 0.2 + 0.6; + double zo = level->random->nextFloat() * s + (1 - s) * 0.5; + shared_ptr item = shared_ptr( new ItemEntity(level, x + xo, y + yo, z + zo, shared_ptr( new ItemInstance(oldRecord, 1, 0) ) ) ); + item->throwTime = 10; + level->addEntity(item); +} + +void RecordPlayerTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + dropRecording(level, x, y, z); + Tile::onRemove(level, x, y, z, id, data); +} + +void RecordPlayerTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus) +{ + if (level->isClientSide) return; + Tile::spawnResources(level, x, y, z, data, odds, 0); +} + +shared_ptr RecordPlayerTile::newTileEntity(Level *level) +{ + return shared_ptr( new RecordPlayerTile::Entity() ); +} + +void RecordPlayerTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"musicBlock"); + iconTop = iconRegister->registerIcon(L"jukebox_top"); +} \ No newline at end of file diff --git a/Minecraft.World/RecordPlayerTile.h b/Minecraft.World/RecordPlayerTile.h new file mode 100644 index 00000000..b6a2d901 --- /dev/null +++ b/Minecraft.World/RecordPlayerTile.h @@ -0,0 +1,68 @@ +#pragma once + +#include "EntityTile.h" +#include "CompoundTag.h" +#include "TileEntity.h" + +class CompoundTag; +class ChunkRebuildData; + +class RecordPlayerTile : public EntityTile +{ + friend class Tile; + friend class ChunkRebuildData; +public: + class Entity : public TileEntity + { +public: + eINSTANCEOF GetType() { return eTYPE_RECORDPLAYERTILE; } + static TileEntity *create() { return new RecordPlayerTile::Entity(); } + + public: + int record; + Entity() : TileEntity(), record( 0 ) {} + + virtual void load(CompoundTag *tag) + { + TileEntity::load(tag); + record = tag->getInt(L"Record"); + } + + virtual void save(CompoundTag *tag) + { + TileEntity::save(tag); + if (record > 0) tag->putInt(L"Record", record); + // return true; // 4J - TODO, in java there is no return type here but we can't change our derived class member function to not have one - investigate + // 4J Jev, took out the return statement, TileEntity::save is now virtual. Think this fixes above. + } + + // 4J Added + shared_ptr clone() + { + shared_ptr result = shared_ptr( new RecordPlayerTile::Entity() ); + TileEntity::clone(result); + + result->record = record; + + return result; + } + }; + +private: + Icon *iconTop; + +protected: + RecordPlayerTile(int id); + +public: + virtual Icon *getTexture(int face, int data); + virtual bool TestUse(Level *level, int x, int y, int z, shared_ptr player); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + void setRecord(Level *level, int x, int y, int z, int record); + void dropRecording(Level *level, int x, int y, int z); + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); + virtual void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus); + + virtual shared_ptr newTileEntity(Level *level); + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/RecordingItem.cpp b/Minecraft.World/RecordingItem.cpp new file mode 100644 index 00000000..24be1190 --- /dev/null +++ b/Minecraft.World/RecordingItem.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.h" +#include "ItemInstance.h" +#include "RecordingItem.h" +#include "GenericStats.h" + +RecordingItem::RecordingItem(int id, const wstring& recording) : Item(id), recording( recording ) +{ + this->maxStackSize = 1; +} + +Icon *RecordingItem::getIcon(int auxValue) +{ + return icon; +} + +bool RecordingItem::useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + // 4J-PB - Adding a test only version to allow tooltips to be displayed + if (level->getTile(x, y, z) == Tile::recordPlayer_Id && level->getData(x, y, z) == 0) + { + if(!bTestUseOnOnly) + { + if (level->isClientSide) return true; + + ((RecordPlayerTile *) Tile::recordPlayer)->setRecord(level, x, y, z, id); + level->levelEvent(nullptr, LevelEvent::SOUND_PLAY_RECORDING, x, y, z, id); + itemInstance->count--; + + player->awardStat( + GenericStats::musicToMyEars(), + GenericStats::param_musicToMyEars(id) + ); + } + return true; + } + return false; +} + +void RecordingItem::appendHoverText(shared_ptr itemInstance, shared_ptr player, vector *lines, bool advanced, vector &unformattedStrings) +{ + eMinecraftColour rarityColour = getRarity(shared_ptr())->color; + int colour = app.GetHTMLColour(rarityColour); + wchar_t formatted[256]; + + swprintf(formatted, 256, L"%ls",colour,L"C418 - ", recording.c_str()); + + lines->push_back(formatted); + + unformattedStrings.push_back(recording); +} + +const Rarity *RecordingItem::getRarity(shared_ptr itemInstance) +{ + return (Rarity *)Rarity::rare; +} + +void RecordingItem::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"record_" + recording); +} diff --git a/Minecraft.World/RecordingItem.h b/Minecraft.World/RecordingItem.h new file mode 100644 index 00000000..9b184c6a --- /dev/null +++ b/Minecraft.World/RecordingItem.h @@ -0,0 +1,23 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class RecordingItem : public Item +{ +public: + const std::wstring recording; + +public: // 4J Stu - Was protected in Java, but the can't access it where we need + RecordingItem(int id, const wstring& recording); + + //@Override + Icon *getIcon(int auxValue); + virtual bool useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + + virtual void appendHoverText(shared_ptr itemInstance, shared_ptr player, vector *lines, bool advanced, vector &unformattedStrings); + virtual const Rarity *getRarity(shared_ptr itemInstance); + + //@Override + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/RedStoneDustTile.cpp b/Minecraft.World/RedStoneDustTile.cpp new file mode 100644 index 00000000..699cad95 --- /dev/null +++ b/Minecraft.World/RedStoneDustTile.cpp @@ -0,0 +1,431 @@ +#include "stdafx.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "RedStoneDustTile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" +#include "Direction.h" +#include "DiodeTile.h" + +// AP - added for Vita to set Alpha Cut out +#include "IntBuffer.h" +#include "..\Minecraft.Client\Tesselator.h" + +const wstring RedStoneDustTile::TEXTURE_CROSS = L"redstoneDust_cross"; +const wstring RedStoneDustTile::TEXTURE_LINE = L"redstoneDust_line"; +const wstring RedStoneDustTile::TEXTURE_CROSS_OVERLAY = L"redstoneDust_cross_overlay"; +const wstring RedStoneDustTile::TEXTURE_LINE_OVERLAY = L"redstoneDust_line_overlay"; + +RedStoneDustTile::RedStoneDustTile(int id) : Tile(id, Material::decoration,isSolidRender()) +{ + shouldSignal = true; + + updateDefaultShape(); + + iconCross = NULL; + iconLine = NULL; + iconCrossOver = NULL; + iconLineOver = NULL; +} + +// 4J Added override +void RedStoneDustTile::updateDefaultShape() +{ + setShape(0, 0, 0, 1, 1 / 16.0f, 1); +} + +AABB *RedStoneDustTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +bool RedStoneDustTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool RedStoneDustTile::isCubeShaped() +{ + return false; +} + +int RedStoneDustTile::getRenderShape() +{ + return Tile::SHAPE_RED_DUST; +} + +int RedStoneDustTile::getColor() const +{ + return Minecraft::GetInstance()->getColourTable()->getColor(eMinecraftColour_Tile_RedstoneDust); // 0x800000; +} + +int RedStoneDustTile::getColor(LevelSource *level, int x, int y, int z) +{ + return Minecraft::GetInstance()->getColourTable()->getColor(eMinecraftColour_Tile_RedstoneDust); // 0x800000; +} + +int RedStoneDustTile::getColor(LevelSource *level, int x, int y, int z, int data) +{ + return Minecraft::GetInstance()->getColourTable()->getColor(eMinecraftColour_Tile_RedstoneDust); // 0x800000; +} + +bool RedStoneDustTile::mayPlace(Level *level, int x, int y, int z) +{ + return level->isTopSolidBlocking(x, y - 1, z) || level->getTile(x, y - 1, z) == Tile::lightGem_Id; +} + +void RedStoneDustTile::updatePowerStrength(Level *level, int x, int y, int z) +{ + updatePowerStrength(level, x, y, z, x, y, z); + + vector updates = vector(toUpdate.begin(), toUpdate.end()); + toUpdate.clear(); + + AUTO_VAR(itEnd, updates.end()); + for(AUTO_VAR(it, updates.begin()); it != itEnd; it++) + { + TilePos tp = *it; + level->updateNeighborsAt(tp.x, tp.y, tp.z, id); + } +} + +void RedStoneDustTile::updatePowerStrength(Level *level, int x, int y, int z, int xFrom, int yFrom, int zFrom) +{ + int old = level->getData(x, y, z); + int target = 0; + + this->shouldSignal = false; + bool neighborSignal = level->hasNeighborSignal(x, y, z); + this->shouldSignal = true; + + if (neighborSignal) + { + target = 15; + } + else + { + for (int i = 0; i < 4; i++) + { + int xt = x; + int zt = z; + if (i == 0) xt--; + if (i == 1) xt++; + if (i == 2) zt--; + if (i == 3) zt++; + + if (xt != xFrom || y != yFrom || zt != zFrom) target = checkTarget(level, xt, y, zt, target); + if (level->isSolidBlockingTile(xt, y, zt) && !level->isSolidBlockingTile(x, y + 1, z)) + { + if (xt != xFrom || y + 1 != yFrom || zt != zFrom) target = checkTarget(level, xt, y + 1, zt, target); + } + else if (!level->isSolidBlockingTile(xt, y, zt)) + { + if (xt != xFrom || y - 1 != yFrom || zt != zFrom) target = checkTarget(level, xt, y - 1, zt, target); + } + } + if (target > 0) target--; + else target = 0; + } + + if (old != target) + { + level->noNeighborUpdate = true; + level->setData(x, y, z, target); + level->setTilesDirty(x, y, z, x, y, z); + level->noNeighborUpdate = false; + + for (int i = 0; i < 4; i++) + { + int xt = x; + int zt = z; + int yt = y - 1; + if (i == 0) xt--; + if (i == 1) xt++; + if (i == 2) zt--; + if (i == 3) zt++; + + if (level->isSolidBlockingTile(xt, y, zt)) yt += 2; + + int current = 0; + current = checkTarget(level, xt, y, zt, -1); + target = level->getData(x, y, z); + if (target > 0) target--; + if (current >= 0 && current != target) + { + updatePowerStrength(level, xt, y, zt, x, y, z); + } + current = checkTarget(level, xt, yt, zt, -1); + target = level->getData(x, y, z); + if (target > 0) target--; + if (current >= 0 && current != target) + { + updatePowerStrength(level, xt, yt, zt, x, y, z); + } + } + + if (old < target || target == 0) + { + toUpdate.insert(TilePos(x, y, z)); + toUpdate.insert(TilePos(x - 1, y, z)); + toUpdate.insert(TilePos(x + 1, y, z)); + toUpdate.insert(TilePos(x, y - 1, z)); + toUpdate.insert(TilePos(x, y + 1, z)); + toUpdate.insert(TilePos(x, y, z - 1)); + toUpdate.insert(TilePos(x, y, z + 1)); + } + } +} + +void RedStoneDustTile::checkCornerChangeAt(Level *level, int x, int y, int z) +{ + if (level->getTile(x, y, z) != id) return; + + level->updateNeighborsAt(x, y, z, id); + level->updateNeighborsAt(x - 1, y, z, id); + level->updateNeighborsAt(x + 1, y, z, id); + level->updateNeighborsAt(x, y, z - 1, id); + level->updateNeighborsAt(x, y, z + 1, id); + + level->updateNeighborsAt(x, y - 1, z, id); + level->updateNeighborsAt(x, y + 1, z, id); +} + +void RedStoneDustTile::onPlace(Level *level, int x, int y, int z) +{ + Tile::onPlace(level, x, y, z); + if (level->isClientSide) return; + + updatePowerStrength(level, x, y, z); + level->updateNeighborsAt(x, y + 1, z, id); + level->updateNeighborsAt(x, y - 1, z, id); + + checkCornerChangeAt(level, x - 1, y, z); + checkCornerChangeAt(level, x + 1, y, z); + checkCornerChangeAt(level, x, y, z - 1); + checkCornerChangeAt(level, x, y, z + 1); + + if (level->isSolidBlockingTile(x - 1, y, z)) checkCornerChangeAt(level, x - 1, y + 1, z); + else checkCornerChangeAt(level, x - 1, y - 1, z); + if (level->isSolidBlockingTile(x + 1, y, z)) checkCornerChangeAt(level, x + 1, y + 1, z); + else checkCornerChangeAt(level, x + 1, y - 1, z); + if (level->isSolidBlockingTile(x, y, z - 1)) checkCornerChangeAt(level, x, y + 1, z - 1); + else checkCornerChangeAt(level, x, y - 1, z - 1); + if (level->isSolidBlockingTile(x, y, z + 1)) checkCornerChangeAt(level, x, y + 1, z + 1); + else checkCornerChangeAt(level, x, y - 1, z + 1); + +} + +void RedStoneDustTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + Tile::onRemove(level, x, y, z, id, data); + if (level->isClientSide) return; + + level->updateNeighborsAt(x, y + 1, z, this->id); + level->updateNeighborsAt(x, y - 1, z, this->id); + level->updateNeighborsAt(x + 1, y, z, this->id); + level->updateNeighborsAt(x - 1, y, z, this->id); + level->updateNeighborsAt(x, y, z + 1, this->id); + level->updateNeighborsAt(x, y, z - 1, this->id); + updatePowerStrength(level, x, y, z); + + checkCornerChangeAt(level, x - 1, y, z); + checkCornerChangeAt(level, x + 1, y, z); + checkCornerChangeAt(level, x, y, z - 1); + checkCornerChangeAt(level, x, y, z + 1); + + if (level->isSolidBlockingTile(x - 1, y, z)) checkCornerChangeAt(level, x - 1, y + 1, z); + else checkCornerChangeAt(level, x - 1, y - 1, z); + if (level->isSolidBlockingTile(x + 1, y, z)) checkCornerChangeAt(level, x + 1, y + 1, z); + else checkCornerChangeAt(level, x + 1, y - 1, z); + if (level->isSolidBlockingTile(x, y, z - 1)) checkCornerChangeAt(level, x, y + 1, z - 1); + else checkCornerChangeAt(level, x, y - 1, z - 1); + if (level->isSolidBlockingTile(x, y, z + 1)) checkCornerChangeAt(level, x, y + 1, z + 1); + else checkCornerChangeAt(level, x, y - 1, z + 1); +} + +int RedStoneDustTile::checkTarget(Level *level, int x, int y, int z, int target) +{ + if (level->getTile(x, y, z) != id) return target; + int d = level->getData(x, y, z); + if (d > target) return d; + return target; +} + +void RedStoneDustTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (level->isClientSide) return; + int face = level->getData(x, y, z); + + bool ok = mayPlace(level, x, y, z); + + if (ok) + { + updatePowerStrength(level, x, y, z); + } + else + { + spawnResources(level, x, y, z, face, 0); + level->setTile(x, y, z, 0); + } + + Tile::neighborChanged(level, x, y, z, type); +} + +int RedStoneDustTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::redStone->id; +} + +bool RedStoneDustTile::getDirectSignal(Level *level, int x, int y, int z, int dir) +{ + if (!shouldSignal) return false; + return getSignal(level, x, y, z, dir); +} + +bool RedStoneDustTile::getSignal(LevelSource *level, int x, int y, int z, int dir) +{ + if (!shouldSignal) return false; + if (level->getData(x, y, z) == 0) return false; + + if (dir == 1) return true; + + bool w = RedStoneDustTile::shouldReceivePowerFrom(level, x - 1, y, z, Direction::WEST) + || (!level->isSolidBlockingTile(x - 1, y, z) && RedStoneDustTile::shouldReceivePowerFrom(level, x - 1, y - 1, z, Direction::UNDEFINED)); + bool e = RedStoneDustTile::shouldReceivePowerFrom(level, x + 1, y, z, Direction::EAST) + || (!level->isSolidBlockingTile(x + 1, y, z) && RedStoneDustTile::shouldReceivePowerFrom(level, x + 1, y - 1, z, Direction::UNDEFINED)); + bool n = RedStoneDustTile::shouldReceivePowerFrom(level, x, y, z - 1, Direction::NORTH) + || (!level->isSolidBlockingTile(x, y, z - 1) && RedStoneDustTile::shouldReceivePowerFrom(level, x, y - 1, z - 1, Direction::UNDEFINED)); + bool s = RedStoneDustTile::shouldReceivePowerFrom(level, x, y, z + 1, Direction::SOUTH) + || (!level->isSolidBlockingTile(x, y, z + 1) && RedStoneDustTile::shouldReceivePowerFrom(level, x, y - 1, z + 1, Direction::UNDEFINED)); + + if (!level->isSolidBlockingTile(x, y + 1, z)) + { + if (level->isSolidBlockingTile(x - 1, y, z) && RedStoneDustTile::shouldReceivePowerFrom(level, x - 1, y + 1, z, Direction::UNDEFINED)) w = true; + if (level->isSolidBlockingTile(x + 1, y, z) && RedStoneDustTile::shouldReceivePowerFrom(level, x + 1, y + 1, z, Direction::UNDEFINED)) e = true; + if (level->isSolidBlockingTile(x, y, z - 1) && RedStoneDustTile::shouldReceivePowerFrom(level, x, y + 1, z - 1, Direction::UNDEFINED)) n = true; + if (level->isSolidBlockingTile(x, y, z + 1) && RedStoneDustTile::shouldReceivePowerFrom(level, x, y + 1, z + 1, Direction::UNDEFINED)) s = true; + } + + if (!n && !e && !w && !s && (dir >= 2 && dir <= 5)) return true; + + if (dir == 2 && n && (!w && !e)) return true; + if (dir == 3 && s && (!w && !e)) return true; + if (dir == 4 && w && (!n && !s)) return true; + if (dir == 5 && e && (!n && !s)) return true; + + return false; + +} + + +bool RedStoneDustTile::isSignalSource() +{ + return shouldSignal; +} + +void RedStoneDustTile::animateTick(Level *level, int x, int y, int z, Random *random) +{ + int data = level->getData(x, y, z); + if (data > 0) + { + double xx = x + 0.5 + (random->nextFloat() - 0.5) * 0.2; + double yy = y + 1 / 16.0f; + double zz = z + 0.5 + (random->nextFloat() - 0.5) * 0.2; + // use the x movement variable to determine particle color + + // 4J Stu - Unused + //float pow = (data / 15.0f); + //float red = pow * 0.6f + 0.4f; + //if (data == 0) red = 0; + + //float green = pow * pow * 0.7f - 0.5f; + //float blue = pow * pow * 0.6f - 0.7f; + //if (green < 0) green = 0; + //if (blue < 0) blue = 0; + + unsigned int colour = 0; + if(data == 0) + { + colour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Tile_RedstoneDustUnlit ); + } + else + { + unsigned int minColour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Tile_RedstoneDustLitMin ); + unsigned int maxColour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Tile_RedstoneDustLitMax ); + + byte redComponent = ((minColour>>16)&0xFF) + (( (maxColour>>16)&0xFF - (minColour>>16)&0xFF)*( (data-1)/14.0f)); + byte greenComponent = ((minColour>>8)&0xFF) + (( (maxColour>>8)&0xFF - (minColour>>8)&0xFF)*( (data-1)/14.0f)); + byte blueComponent = ((minColour)&0xFF) + (( (maxColour)&0xFF - (minColour)&0xFF)*( (data-1)/14.0f)); + + colour = redComponent<<16 | greenComponent<<8 | blueComponent; + } + + float red = ((colour>>16)&0xFF)/255.0f; + float green = ((colour>>8)&0xFF)/255.0f; + float blue = (colour&0xFF)/255.0f; + + level->addParticle(eParticleType_reddust, xx, yy, zz, red, green, blue); + } +} + + +bool RedStoneDustTile::shouldConnectTo(LevelSource *level, int x, int y, int z, int direction) +{ + int t = level->getTile(x, y, z); + if (t == Tile::redStoneDust_Id) return true; + if (t == 0) return false; + if (t == Tile::diode_off_Id || t == Tile::diode_on_Id) + { + int data = level->getData(x, y, z); + return direction == (data & DiodeTile::DIRECTION_MASK) || direction == Direction::DIRECTION_OPPOSITE[data & DiodeTile::DIRECTION_MASK]; + } + else if (Tile::tiles[t]->isSignalSource() && direction != Direction::UNDEFINED) return true; + + return false; +} + +bool RedStoneDustTile::shouldReceivePowerFrom(LevelSource *level, int x, int y, int z, int direction) +{ + if (shouldConnectTo(level, x, y, z, direction)) + { + return true; + } + + int t = level->getTile(x, y, z); + if (t == Tile::diode_on_Id) + { + int data = level->getData(x, y, z); + return direction == (data & DiodeTile::DIRECTION_MASK); + } + return false; +} + +int RedStoneDustTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Item::redStone_Id; +} + +void RedStoneDustTile::registerIcons(IconRegister *iconRegister) +{ + iconCross = iconRegister->registerIcon(TEXTURE_CROSS); + iconLine = iconRegister->registerIcon(TEXTURE_LINE); + iconCrossOver = iconRegister->registerIcon(TEXTURE_CROSS_OVERLAY); + iconLineOver = iconRegister->registerIcon(TEXTURE_LINE_OVERLAY); + + icon = iconCross; +} + +Icon *RedStoneDustTile::getTexture(const wstring &name) +{ +#ifdef __PSVITA__ + // AP - alpha cut out is expensive on vita. Set the Alpha Cut out flag + Tesselator* t = Tesselator::getInstance(); + t->setAlphaCutOut( true ); +#endif + + if (name.compare(TEXTURE_CROSS) == 0) return Tile::redStoneDust->iconCross; + if (name.compare(TEXTURE_LINE) == 0) return Tile::redStoneDust->iconLine; + if (name.compare(TEXTURE_CROSS_OVERLAY) == 0) return Tile::redStoneDust->iconCrossOver; + if (name.compare(TEXTURE_LINE_OVERLAY) == 0) return Tile::redStoneDust->iconLineOver; + return NULL; +} diff --git a/Minecraft.World/RedStoneDustTile.h b/Minecraft.World/RedStoneDustTile.h new file mode 100644 index 00000000..edb18e6d --- /dev/null +++ b/Minecraft.World/RedStoneDustTile.h @@ -0,0 +1,61 @@ +#pragma once +#include "Tile.h" +#include "Definitions.h" +using namespace std; + +class Random; +class Level; +class ChunkRebuildData; + +class RedStoneDustTile : public Tile +{ + friend ChunkRebuildData; +public: + static const wstring TEXTURE_CROSS; + static const wstring TEXTURE_LINE; + static const wstring TEXTURE_CROSS_OVERLAY; + static const wstring TEXTURE_LINE_OVERLAY; +private: + bool shouldSignal; + unordered_set toUpdate; + Icon *iconCross; + Icon *iconLine; + Icon *iconCrossOver; + Icon *iconLineOver; + +public: + RedStoneDustTile(int id); + virtual void updateDefaultShape(); // 4J Added override + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual int getRenderShape(); + virtual int getColor() const; // 4J Added + virtual int getColor(LevelSource *level, int x, int y, int z); + virtual int getColor(LevelSource *level, int x, int y, int z, int data); // 4J added + virtual bool mayPlace(Level *level, int x, int y, int z); +private: + void updatePowerStrength(Level *level, int x, int y, int z); + void updatePowerStrength(Level *level, int x, int y, int z, int xFrom, int yFrom, int zFrom); + void checkCornerChangeAt(Level *level, int x, int y, int z); +public: + virtual void onPlace(Level *level, int x, int y, int z); + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); +private: + int checkTarget(Level *level, int x, int y, int z, int target); +public: + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual bool getDirectSignal(Level *level, int x, int y, int z, int dir) ; + virtual bool getSignal(LevelSource *level, int x, int y, int z, int dir); + + virtual bool isSignalSource(); + virtual void animateTick(Level *level, int x, int y, int z, Random *random); + + static bool shouldConnectTo(LevelSource *level, int x, int y, int z, int direction); + static bool shouldReceivePowerFrom(LevelSource *level, int x, int y, int z, int direction); + virtual int cloneTileId(Level *level, int x, int y, int z); + + void registerIcons(IconRegister *iconRegister); + static Icon *getTexture(const wstring &name); +}; diff --git a/Minecraft.World/RedStoneItem.cpp b/Minecraft.World/RedStoneItem.cpp new file mode 100644 index 00000000..64201caa --- /dev/null +++ b/Minecraft.World/RedStoneItem.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "ItemInstance.h" +#include "GenericStats.h" +#include "RedstoneItem.h" + +RedStoneItem::RedStoneItem(int id) : Item(id) +{ +} + +bool RedStoneItem::useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + // 4J-PB - Adding a test only version to allow tooltips to be displayed + if (level->getTile(x, y, z) != Tile::topSnow_Id) + { + if (face == 0) y--; + if (face == 1) y++; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + if (!level->isEmptyTile(x, y, z)) return false; + } + if (!player->mayBuild(x, y, z)) return false; + if (Tile::redStoneDust->mayPlace(level, x, y, z)) + { + if(!bTestUseOnOnly) + { + // 4J-JEV: Hook for durango 'BlockPlaced' event. + player->awardStat(GenericStats::blocksPlaced(Tile::redStoneDust_Id), GenericStats::param_blocksPlaced(Tile::redStoneDust_Id,itemInstance->getAuxValue(),1)); + + itemInstance->count--; + level->setTile(x, y, z, Tile::redStoneDust_Id); + } + } + + return true; +} diff --git a/Minecraft.World/RedStoneItem.h b/Minecraft.World/RedStoneItem.h new file mode 100644 index 00000000..01be92ff --- /dev/null +++ b/Minecraft.World/RedStoneItem.h @@ -0,0 +1,13 @@ +#pragma once +using namespace std; + +#include "Item.h" + + class RedStoneItem : public Item + { + public: + RedStoneItem(int id); + + public: + virtual bool useOn(shared_ptr itemInstance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); +}; \ No newline at end of file diff --git a/Minecraft.World/RedStoneOreTile.cpp b/Minecraft.World/RedStoneOreTile.cpp new file mode 100644 index 00000000..9317ffdd --- /dev/null +++ b/Minecraft.World/RedStoneOreTile.cpp @@ -0,0 +1,128 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "RedStoneOreTile.h" +#include "net.minecraft.world.item.h" + +RedStoneOreTile::RedStoneOreTile(int id, bool lit) : Tile(id, Material::stone) +{ + if (lit) + { + this->setTicking(true); + } + this->lit = lit; +} + +int RedStoneOreTile::getTickDelay() +{ + return 30; +} + +void RedStoneOreTile::attack(Level *level, int x, int y, int z, shared_ptr player) +{ + interact(level, x, y, z); + Tile::attack(level, x, y, z, player); +} + +void RedStoneOreTile::stepOn(Level *level, int x, int y, int z, shared_ptr entity) +{ + interact(level, x, y, z); + Tile::stepOn(level, x, y, z, entity); +} + +// 4J-PB - Adding a TestUse for tooltip display +bool RedStoneOreTile::TestUse() +{ + return true; +} + +bool RedStoneOreTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if (soundOnly) return false; + interact(level, x, y, z); + return Tile::use(level, x, y, z, player, clickedFace, clickX, clickY, clickZ); +} + +void RedStoneOreTile::interact(Level *level, int x, int y, int z) +{ + poofParticles(level, x, y, z); + if (level->isClientSide) return; // 4J added + if (id == Tile::redStoneOre_Id) + { + level->setTile(x, y, z, Tile::redStoneOre_lit_Id); + } +} + +void RedStoneOreTile::tick(Level *level, int x, int y, int z, Random* random) +{ + if (id == Tile::redStoneOre_lit_Id) + { + level->setTile(x, y, z, Tile::redStoneOre_Id); + } +} + +int RedStoneOreTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::redStone->id; +} + +int RedStoneOreTile::getResourceCountForLootBonus(int bonusLevel, Random *random) +{ + return getResourceCount(random) + random->nextInt(bonusLevel + 1); +} + +int RedStoneOreTile::getResourceCount(Random *random) +{ + return 4 + random->nextInt(2); +} + +void RedStoneOreTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel) +{ + Tile::spawnResources(level, x, y, z, data, odds, playerBonusLevel); + + // also spawn experience if the block is broken + if (getResource(data, level->random, playerBonusLevel) != id) + { + int magicCount = 1 + level->random->nextInt(5); + popExperience(level, x, y, z, magicCount); + } +} + +void RedStoneOreTile::animateTick(Level *level, int x, int y, int z, Random *random) +{ + if (lit) + { + poofParticles(level, x, y, z); + } +} + +void RedStoneOreTile::poofParticles(Level *level, int x, int y, int z) +{ + Random *random = level->random; + double r = 1 / 16.0f; + for (int i = 0; i < 6; i++) + { + double xx = x + random->nextFloat(); + double yy = y + random->nextFloat(); + double zz = z + random->nextFloat(); + if (i == 0 && !level->isSolidRenderTile(x, y + 1, z)) yy = y + 1 + r; + if (i == 1 && !level->isSolidRenderTile(x, y - 1, z)) yy = y + 0 - r; + if (i == 2 && !level->isSolidRenderTile(x, y, z + 1)) zz = z + 1 + r; + if (i == 3 && !level->isSolidRenderTile(x, y, z - 1)) zz = z + 0 - r; + if (i == 4 && !level->isSolidRenderTile(x + 1, y, z)) xx = x + 1 + r; + if (i == 5 && !level->isSolidRenderTile(x - 1, y, z)) xx = x + 0 - r; + if (xx < x || xx > x + 1 || yy < 0 || yy > y + 1 || zz < z || zz > z + 1) + { + level->addParticle(eParticleType_reddust, xx, yy, zz, 0, 0, 0); + } + } +} + +bool RedStoneOreTile::shouldTileTick(Level *level, int x,int y,int z) +{ + return id == Tile::redStoneOre_lit_Id; +} + +shared_ptr RedStoneOreTile::getSilkTouchItemInstance(int data) +{ + return shared_ptr(new ItemInstance(Tile::redStoneOre)); +} \ No newline at end of file diff --git a/Minecraft.World/RedStoneOreTile.h b/Minecraft.World/RedStoneOreTile.h new file mode 100644 index 00000000..bc1d48d7 --- /dev/null +++ b/Minecraft.World/RedStoneOreTile.h @@ -0,0 +1,34 @@ +#pragma once +#include "Tile.h" + +class Player; +class Random; + +class RedStoneOreTile : public Tile +{ +private: + bool lit; +public: + RedStoneOreTile(int id, bool lit); + virtual int getTickDelay(); + virtual void attack(Level *level, int x, int y, int z, shared_ptr player); + virtual void stepOn(Level *level, int x, int y, int z, shared_ptr entity); + virtual bool TestUse(); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param +private: + virtual void interact(Level *level, int x, int y, int z); +public: + virtual void tick(Level *level, int x, int y, int z, Random* random); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int getResourceCountForLootBonus(int bonusLevel, Random *random); + virtual int getResourceCount(Random *random); + virtual void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel); + virtual void animateTick(Level *level, int x, int y, int z, Random *random); + + // 4J Added so we can check before we try to add a tile to the tick list if it's actually going to do seomthing + virtual bool shouldTileTick(Level *level, int x,int y,int z); +private: + void poofParticles(Level *level, int x, int y, int z); +protected: + virtual shared_ptr getSilkTouchItemInstance(int data); +}; diff --git a/Minecraft.World/RedlightTile.cpp b/Minecraft.World/RedlightTile.cpp new file mode 100644 index 00000000..2a529c84 --- /dev/null +++ b/Minecraft.World/RedlightTile.cpp @@ -0,0 +1,78 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.h" +#include "RedlightTile.h" + +RedlightTile::RedlightTile(int id, bool isLit) : Tile(id, Material::buildable_glass) +{ + this->isLit = isLit; + + if (isLit) + { + setLightEmission(1.0f); + } +} + +void RedlightTile::registerIcons(IconRegister *iconRegister) +{ + if (isLit) + { + icon = iconRegister->registerIcon(L"redstoneLight_lit"); + } + else + { + icon = iconRegister->registerIcon(L"redstoneLight"); + } +} + +void RedlightTile::onPlace(Level *level, int x, int y, int z) +{ + if (!level->isClientSide) + { + if (isLit && !level->hasNeighborSignal(x, y, z)) + { + level->addToTickNextTick(x, y, z, id, 4); + } + else if (!isLit && level->hasNeighborSignal(x, y, z)) + { + level->setTile(x, y, z, Tile::redstoneLight_lit_Id); + } + } +} + +void RedlightTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (!level->isClientSide) + { + if (isLit && !level->hasNeighborSignal(x, y, z)) + { + level->addToTickNextTick(x, y, z, id, 4); + } + else if (!isLit && level->hasNeighborSignal(x, y, z)) + { + level->setTile(x, y, z, Tile::redstoneLight_lit_Id); + } + } +} + +void RedlightTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (!level->isClientSide) + { + if (isLit && !level->hasNeighborSignal(x, y, z)) + { + level->setTile(x, y, z, Tile::redstoneLight_Id); + } + } +} + +int RedlightTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::redstoneLight_Id; +} + +int RedlightTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Tile::redstoneLight_Id; +} \ No newline at end of file diff --git a/Minecraft.World/RedlightTile.h b/Minecraft.World/RedlightTile.h new file mode 100644 index 00000000..f7b1c461 --- /dev/null +++ b/Minecraft.World/RedlightTile.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Tile.h" + +class RedlightTile : public Tile +{ +private: + bool isLit; + +public: + RedlightTile(int id, bool isLit); + + virtual void registerIcons(IconRegister *iconRegister); + virtual void onPlace(Level *level, int x, int y, int z); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int cloneTileId(Level *level, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/ReedTile.cpp b/Minecraft.World/ReedTile.cpp new file mode 100644 index 00000000..69596e83 --- /dev/null +++ b/Minecraft.World/ReedTile.cpp @@ -0,0 +1,117 @@ +#include "stdafx.h" +#include "GrassTile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.phys.h" +#include "ReedTile.h" + +ReedTile::ReedTile(int id) : Tile( id, Material::plant,isSolidRender() ) +{ + this->updateDefaultShape(); + this->setTicking(true); +} + +// 4J Added override +void ReedTile::updateDefaultShape() +{ + float ss = 6 / 16.0f; + this->setShape(0.5f - ss, 0, 0.5f - ss, 0.5f + ss, 1, 0.5f + ss); +} + +void ReedTile::tick(Level *level, int x, int y, int z, Random* random) +{ + if (level->isEmptyTile(x, y + 1, z)) + { + int height = 1; + while (level->getTile(x, y - height, z) == id) + { + height++; + } + if (height < 3) + { + int age = level->getData(x, y, z); + if (age == 15) + { + level->setTile(x, y + 1, z, id); + level->setData(x, y, z, 0); + } + else + { + level->setData(x, y, z, age + 1); + } + } + } +} + +bool ReedTile::mayPlace(Level *level, int x, int y, int z) +{ + int below = level->getTile(x, y - 1, z); + if (below == id) return true; + if (below != Tile::grass_Id && below != Tile::dirt_Id && below != Tile::sand_Id) return false; + if (level->getMaterial(x - 1, y - 1, z) == Material::water) return true; + if (level->getMaterial(x + 1, y - 1, z) == Material::water) return true; + if (level->getMaterial(x, y - 1, z - 1) == Material::water) return true; + if (level->getMaterial(x, y - 1, z + 1) == Material::water) return true; + //printf("no water\n"); + return false; +} + +void ReedTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + checkAlive(level, x, y, z); +} + +const void ReedTile::checkAlive(Level *level, int x, int y, int z) +{ + if (!canSurvive(level, x, y, z)) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } +} + +bool ReedTile::canSurvive(Level *level, int x, int y, int z) +{ + return mayPlace(level, x, y, z); +} + +AABB *ReedTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +int ReedTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::reeds->id; +} + +bool ReedTile::blocksLight() +{ + return false; +} + +bool ReedTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool ReedTile::isCubeShaped() +{ + return false; +} + +int ReedTile::getRenderShape() +{ + return Tile::SHAPE_CROSS_TEXTURE; +} + +int ReedTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Item::reeds_Id; +} + +bool ReedTile::shouldTileTick(Level *level, int x,int y,int z) +{ + return level->isEmptyTile(x, y + 1, z); +} diff --git a/Minecraft.World/ReedTile.h b/Minecraft.World/ReedTile.h new file mode 100644 index 00000000..bc2a0366 --- /dev/null +++ b/Minecraft.World/ReedTile.h @@ -0,0 +1,52 @@ +#pragma once +using namespace std; + +#include "Tile.h" +#include "Definitions.h" + +class Random; + +class ReedTile : public Tile +{ + friend class Tile; +protected: + ReedTile(int id); + +public: + virtual void updateDefaultShape(); // 4J Added override + void tick(Level *level, int x, int y, int z, Random* random); + +public: + bool mayPlace(Level *level, int x, int y, int z); + +public: + void neighborChanged(Level *level, int x, int y, int z, int type); + +protected: + const void checkAlive(Level *level, int x, int y, int z); + +public: + bool canSurvive(Level *level, int x, int y, int z); + +public: + AABB *getAABB(Level *level, int x, int y, int z); + +public: + int getResource(int data, Random *random, int playerBonusLevel); + +public: + bool blocksLight(); + +public: + bool isSolidRender(bool isServerLevel = false); + +public: + bool isCubeShaped(); + +public: + int getRenderShape(); + virtual int cloneTileId(Level *level, int x, int y, int z); + + // 4J Added so we can check before we try to add a tile to the tick list if it's actually going to do seomthing + virtual bool shouldTileTick(Level *level, int x,int y,int z); +}; diff --git a/Minecraft.World/ReedsFeature.cpp b/Minecraft.World/ReedsFeature.cpp new file mode 100644 index 00000000..6d4f3e97 --- /dev/null +++ b/Minecraft.World/ReedsFeature.cpp @@ -0,0 +1,46 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "ReedsFeature.h" + +bool ReedsFeature::place(Level *level, Random *random, int x, int y, int z) +{ + for (int i = 0; i < 20; i++) + { + int x2 = x + random->nextInt(4) - random->nextInt(4); + int y2 = y; + int z2 = z + random->nextInt(4) - random->nextInt(4); + + // 4J Stu Added to stop reed features generating areas previously place by game rule generation + if(app.getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + bool intersects = levelGenOptions->checkIntersects(x2, y2, z2, x2, y2, z2); + if(intersects) + { + //app.DebugPrintf("Skipping reeds feature generation as it overlaps a game rule structure\n"); + continue; + } + } + if (level->isEmptyTile(x2, y2, z2)) + { + if (level->getMaterial(x2-1, y2-1, z2) == Material::water || + level->getMaterial(x2+1, y2-1, z2) == Material::water || + level->getMaterial(x2, y2-1, z2-1) == Material::water || + level->getMaterial(x2, y2-1, z2+1) == Material::water) + { + + int h = 2 + random->nextInt(random->nextInt(3) + 1); + for (int yy = 0; yy < h; yy++) + { + if ( Tile::reeds->canSurvive(level, x2, y2 + yy, z2) ) + { + level->setTileNoUpdate(x2, y2 + yy, z2, Tile::reeds_Id); + } + } + } + } + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/ReedsFeature.h b/Minecraft.World/ReedsFeature.h new file mode 100644 index 00000000..b07b7b96 --- /dev/null +++ b/Minecraft.World/ReedsFeature.h @@ -0,0 +1,9 @@ +#pragma once +#include "Feature.h" +#include "Material.h" + +class ReedsFeature : public Feature +{ +public: + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/Reference.h b/Minecraft.World/Reference.h new file mode 100644 index 00000000..2d33f17e --- /dev/null +++ b/Minecraft.World/Reference.h @@ -0,0 +1,10 @@ +#pragma once + +template class Reference +{ +private: + T *obj; +public: + T *get() { return obj; } + Reference(T *i) { obj = i; } +}; \ No newline at end of file diff --git a/Minecraft.World/Region.cpp b/Minecraft.World/Region.cpp new file mode 100644 index 00000000..4c3a454d --- /dev/null +++ b/Minecraft.World/Region.cpp @@ -0,0 +1,364 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.chunk.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.level.tile.h" +#include "Material.h" +#include "Level.h" + +#include "Region.h" + + +Region::~Region() +{ + for(unsigned int i = 0; i < chunks->length; ++i) + { + LevelChunkArray *lca = (*chunks)[i]; + delete [] lca->data; + delete lca; + } + delete [] chunks->data; + delete chunks; + + // AP - added a caching system for Chunk::rebuild to take advantage of + if( CachedTiles ) + { + free(CachedTiles); + } +} + +Region::Region(Level *level, int x1, int y1, int z1, int x2, int y2, int z2) +{ + this->level = level; + + xc1 = x1 >> 4; + zc1 = z1 >> 4; + int xc2 = x2 >> 4; + int zc2 = z2 >> 4; + + chunks = new LevelChunk2DArray(xc2 - xc1 + 1, zc2 - zc1 + 1); + + allEmpty = true; + for (int xc = xc1; xc <= xc2; xc++) + { + for (int zc = zc1; zc <= zc2; zc++) + { + LevelChunk *chunk = level->getChunk(xc, zc); + if(chunk != NULL) + { + LevelChunkArray *lca = (*chunks)[xc - xc1]; + lca->data[zc - zc1] = chunk; + //(*chunks)[xc - xc1].data[zc - zc1] = level->getChunk(xc, zc); + if (!chunk->isYSpaceEmpty(y1, y2)) + { + allEmpty = false; + } + } + } + } + + // AP - added a caching system for Chunk::rebuild to take advantage of + xcCached = -1; + zcCached = -1; + CachedTiles = NULL; +} + +bool Region::isAllEmpty() +{ + return allEmpty; +} + +int Region::getTile(int x, int y, int z) +{ + if (y < 0) return 0; + if (y >= Level::maxBuildHeight) return 0; + + int xc = (x >> 4); + int zc = (z >> 4); + +#ifdef __PSVITA__ + // AP - added a caching system for Chunk::rebuild to take advantage of + if( CachedTiles && xc == xcCached && zc == zcCached ) + { + unsigned char* Tiles = CachedTiles; + Tiles += y; + if(y >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) + { + Tiles += Level::COMPRESSED_CHUNK_SECTION_TILES - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + } + + return Tiles[ ( (x & 15) << 11 ) | ( (z & 15) << 7 ) ]; + } +#endif + + xc -= xc1; + zc -= zc1; + + if (xc < 0 || xc >= (int)chunks->length || zc < 0 || zc >= (int)(*chunks)[xc]->length) + { + return 0; + } + + LevelChunk *lc = (*chunks)[xc]->data[zc]; + if (lc == NULL) return 0; + + return lc->getTile(x & 15, y, z & 15); +} + +// AP - added a caching system for Chunk::rebuild to take advantage of +void Region::setCachedTiles(unsigned char *tiles, int xc, int zc) +{ + xcCached = xc; + zcCached = zc; + int size = 16 * 16 * Level::maxBuildHeight; + if( CachedTiles == NULL ) + { + CachedTiles = (unsigned char *) malloc(size); + } + memcpy(CachedTiles, tiles, size); +} + +LevelChunk* Region::getLevelChunk(int x, int y, int z) +{ + if (y < 0) return 0; + if (y >= Level::maxBuildHeight) return NULL; + + int xc = (x >> 4) - xc1; + int zc = (z >> 4) - zc1; + + if (xc < 0 || xc >= (int)chunks->length || zc < 0 || zc >= (int)(*chunks)[xc]->length) + { + return NULL; + } + + LevelChunk *lc = (*chunks)[xc]->data[zc]; + return lc; +} + + + +shared_ptr Region::getTileEntity(int x, int y, int z) +{ + int xc = (x >> 4) - xc1; + int zc = (z >> 4) - zc1; + + return (*chunks)[xc]->data[zc]->getTileEntity(x & 15, y, z & 15); +} + +int Region::getLightColor(int x, int y, int z, int emitt, int tileId/*=-1*/) +{ + int s = getBrightnessPropagate(LightLayer::Sky, x, y, z, tileId); + int b = getBrightnessPropagate(LightLayer::Block, x, y, z, tileId); + if (b < emitt) b = emitt; + return s << 20 | b << 4; +} + +float Region::getBrightness(int x, int y, int z, int emitt) +{ + int n = getRawBrightness(x, y, z); + if (n < emitt) n = emitt; + return level->dimension->brightnessRamp[n]; +} + + +float Region::getBrightness(int x, int y, int z) +{ + return level->dimension->brightnessRamp[getRawBrightness(x, y, z)]; +} + + +int Region::getRawBrightness(int x, int y, int z) +{ + return getRawBrightness(x, y, z, true); +} + + +int Region::getRawBrightness(int x, int y, int z, bool propagate) +{ + if (x < -Level::MAX_LEVEL_SIZE || z < -Level::MAX_LEVEL_SIZE || x >= Level::MAX_LEVEL_SIZE || z > Level::MAX_LEVEL_SIZE) + { + return Level::MAX_BRIGHTNESS; + } + + if (propagate) + { + int id = getTile(x, y, z); + switch(id) + { + case Tile::stoneSlabHalf_Id: + case Tile::woodSlabHalf_Id: + case Tile::farmland_Id: + case Tile::stairs_stone_Id: + case Tile::stairs_wood_Id: + { + int br = getRawBrightness(x, y + 1, z, false); + int br1 = getRawBrightness(x + 1, y, z, false); + int br2 = getRawBrightness(x - 1, y, z, false); + int br3 = getRawBrightness(x, y, z + 1, false); + int br4 = getRawBrightness(x, y, z - 1, false); + if (br1 > br) br = br1; + if (br2 > br) br = br2; + if (br3 > br) br = br3; + if (br4 > br) br = br4; + return br; + } + break; + } + } + + if (y < 0) return 0; + if (y >= Level::maxBuildHeight) + { + int br = Level::MAX_BRIGHTNESS - level->skyDarken; + if (br < 0) br = 0; + return br; + } + + int xc = (x >> 4) - xc1; + int zc = (z >> 4) - zc1; + + return (*chunks)[xc]->data[zc]->getRawBrightness(x & 15, y, z & 15, level->skyDarken); +} + + +int Region::getData(int x, int y, int z) +{ + if (y < 0) return 0; + if (y >= Level::maxBuildHeight) return 0; + int xc = (x >> 4) - xc1; + int zc = (z >> 4) - zc1; + + return (*chunks)[xc]->data[zc]->getData(x & 15, y, z & 15); +} + +Material *Region::getMaterial(int x, int y, int z) +{ + int t = getTile(x, y, z); + if (t == 0) return Material::air; + return Tile::tiles[t]->material; +} + + +BiomeSource *Region::getBiomeSource() +{ + return level->getBiomeSource(); +} + +Biome *Region::getBiome(int x, int z) +{ + return level->getBiome(x, z); +} + +bool Region::isSolidRenderTile(int x, int y, int z) +{ + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile == NULL) return false; + + // 4J - addition here to make rendering big blocks of leaves more efficient. Normally leaves never consider themselves as solid, so + // blocks of leaves will have all sides of each block completely visible. Changing to consider as solid if this block is surrounded by + // other leaves (or solid things). This is paired with another change in Tile::getTexture which makes such solid tiles actually visibly solid (these + // textures exist already for non-fancy graphics). Note: this tile-specific code is here rather than making some new virtual method in the tiles, + // for the sake of efficiency - I don't imagine we'll be doing much more of this sort of thing + if( tile->id == Tile::leaves_Id ) + { + int axo[6] = { 1,-1, 0, 0, 0, 0}; + int ayo[6] = { 0, 0, 1,-1, 0, 0}; + int azo[6] = { 0, 0, 0, 0, 1,-1}; + for( int i = 0; i < 6; i++ ) + { + int t = getTile(x + axo[i], y + ayo[i] , z + azo[i]); + if( ( t != Tile::leaves_Id ) && ( ( Tile::tiles[t] == NULL ) || !Tile::tiles[t]->isSolidRender() ) ) + { + return false; + } + } + + return true; + } + + return tile->isSolidRender(); +} + + +bool Region::isSolidBlockingTile(int x, int y, int z) +{ + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile == NULL) return false; + return tile->material->blocksMotion() && tile->isCubeShaped(); +} + +bool Region::isTopSolidBlocking(int x, int y, int z) +{ + // Temporary workaround until tahgs per-face solidity is finished + Tile *tile = Tile::tiles[getTile(x, y, z)]; + if (tile == NULL) return false; + + if (tile->material->isSolidBlocking() && tile->isCubeShaped()) return true; + if (dynamic_cast(tile)) return (getData(x, y, z) & StairTile::UPSIDEDOWN_BIT) == StairTile::UPSIDEDOWN_BIT; + if (dynamic_cast(tile)) return (getData(x, y, z) & HalfSlabTile::TOP_SLOT_BIT) == HalfSlabTile::TOP_SLOT_BIT; + return false; +} + +bool Region::isEmptyTile(int x, int y, int z) +{ + Tile *tile = Tile::tiles[getTile(x, y, z)]; + return (tile == NULL); +} + + +// 4J - brought forward from 1.8.2 +int Region::getBrightnessPropagate(LightLayer::variety layer, int x, int y, int z, int tileId) +{ + if (y < 0) y = 0; + if (y >= Level::maxBuildHeight) y = Level::maxBuildHeight - 1; + if (y < 0 || y >= Level::maxBuildHeight || x < -Level::MAX_LEVEL_SIZE || z < -Level::MAX_LEVEL_SIZE || x >= Level::MAX_LEVEL_SIZE || z > Level::MAX_LEVEL_SIZE) + { + // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we + // were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast + // it to an int + return (int)layer; + } + + int id = tileId > -1 ? tileId : getTile(x, y, z); + if (Tile::propagate[id]) + { + int br = getBrightness(layer, x, y + 1, z); if( br == 15 ) return 15; + int br1 = getBrightness(layer, x + 1, y, z); if( br1 == 15 ) return 15; + int br2 = getBrightness(layer, x - 1, y, z); if( br2 == 15 ) return 15; + int br3 = getBrightness(layer, x, y, z + 1); if( br3 == 15 ) return 15; + int br4 = getBrightness(layer, x, y, z - 1); if( br4 == 15 ) return 15; + if (br1 > br) br = br1; + if (br2 > br) br = br2; + if (br3 > br) br = br3; + if (br4 > br) br = br4; + return br; + } + + int xc = (x >> 4) - xc1; + int zc = (z >> 4) - zc1; + + return (*chunks)[xc]->data[zc]->getBrightness(layer, x & 15, y, z & 15); +} + +// 4J - brought forward from 1.8.2 +int Region::getBrightness(LightLayer::variety layer, int x, int y, int z) +{ + if (y < 0) y = 0; + if (y >= Level::maxBuildHeight) y = Level::maxBuildHeight - 1; + if (y < 0 || y >= Level::maxBuildHeight || x < -Level::MAX_LEVEL_SIZE || z < -Level::MAX_LEVEL_SIZE || x >= Level::MAX_LEVEL_SIZE || z > Level::MAX_LEVEL_SIZE) + { + // 4J Stu - The java LightLayer was an enum class type with a member "surrounding" which is what we + // were returning here. Surrounding has the same value as the enum value in our C++ code, so just cast + // it to an int + return (int)layer; + } + int xc = (x >> 4) - xc1; + int zc = (z >> 4) - zc1; + + return (*chunks)[xc]->data[zc]->getBrightness(layer, x & 15, y, z & 15); +} + +int Region::getMaxBuildHeight() +{ + return Level::maxBuildHeight; +} \ No newline at end of file diff --git a/Minecraft.World/Region.h b/Minecraft.World/Region.h new file mode 100644 index 00000000..0d200a35 --- /dev/null +++ b/Minecraft.World/Region.h @@ -0,0 +1,51 @@ +#pragma once + +#include "LevelSource.h" + +class Material; +class TileEntity; +class BiomeSource; + +class Region : public LevelSource +{ +private: + int xc1, zc1; + LevelChunk2DArray *chunks; + Level *level; + bool allEmpty; + + // AP - added a caching system for Chunk::rebuild to take advantage of + int xcCached, zcCached; + unsigned char *CachedTiles; + +public: + Region(Level *level, int x1, int y1, int z1, int x2, int y2, int z2); + virtual ~Region(); + bool isAllEmpty(); + int getTile(int x, int y, int z); + shared_ptr getTileEntity(int x, int y, int z); + float getBrightness(int x, int y, int z, int emitt); + float getBrightness(int x, int y, int z); + int getLightColor(int x, int y, int z, int emitt, int tileId = -1); // 4J - change brought forward from 1.8.2 + int getRawBrightness(int x, int y, int z); + int getRawBrightness(int x, int y, int z, bool propagate); + int getData(int x, int y, int z); + Material *getMaterial(int x, int y, int z); + BiomeSource *getBiomeSource(); + Biome *getBiome(int x, int z); + bool isSolidRenderTile(int x, int y, int z); + bool isSolidBlockingTile(int x, int y, int z); + bool isTopSolidBlocking(int x, int y, int z); + bool isEmptyTile(int x, int y, int z); + + // 4J - changes brought forward from 1.8.2 + int getBrightnessPropagate(LightLayer::variety layer, int x, int y, int z, int tileId); // 4J added tileId + int getBrightness(LightLayer::variety layer, int x, int y, int z); + + int getMaxBuildHeight(); + + LevelChunk* getLevelChunk(int x, int y, int z); + + // AP - added a caching system for Chunk::rebuild to take advantage of + void setCachedTiles(unsigned char *tiles, int xc, int zc); +}; \ No newline at end of file diff --git a/Minecraft.World/RegionFile.cpp b/Minecraft.World/RegionFile.cpp new file mode 100644 index 00000000..2cfd9078 --- /dev/null +++ b/Minecraft.World/RegionFile.cpp @@ -0,0 +1,497 @@ +#include "stdafx.h" +#include "System.h" +#include "InputOutputStream.h" +#include "File.h" +#include "RegionFile.h" + +#include "ConsoleSaveFile.h" + +byteArray RegionFile::emptySector(SECTOR_BYTES); + +RegionFile::RegionFile(ConsoleSaveFile *saveFile, File *path) +{ + _lastModified = 0; + + m_saveFile = saveFile; + + offsets = new int[SECTOR_INTS]; + memset(offsets,0,SECTOR_BYTES); + chunkTimestamps = new int[SECTOR_INTS]; + memset(chunkTimestamps,0,SECTOR_BYTES); + + /* 4J Jev, using files instead of strings: + strncpy(fileName,path,MAX_PATH_SIZE); */ + + fileName = path; + +// debugln("REGION LOAD " + fileName); + + sizeDelta = 0; + + // 4J - removed try/catch +// try { + + /* 4J - Removed as _lastModifed not used and this is always failing as checking wrong thing + if( path->exists() ) + { + _lastModified = path->lastModified(); + } + */ + + fileEntry = m_saveFile->createFile( fileName->getName() ); + m_saveFile->setFilePointer( fileEntry, 0, NULL, FILE_END ); + + if ( fileEntry->getFileSize() < SECTOR_BYTES) + { + // 4J altered - the original code used to write out 2 empty sectors here, which we don't want to do as we might be at a point where we shouldn't be touching the save file. + // This now happens in insertInitialSectors when we do a first write to the region + m_bIsEmpty = true; + + sizeDelta += SECTOR_BYTES * 2; + } + else + { + m_bIsEmpty = false; + } + + //if ((GetFileSize(file,NULL) & 0xfff) != 0) + if ((fileEntry->getFileSize() & 0xfff) != 0) + { + //byte zero = 0; + DWORD numberOfBytesWritten = 0; + DWORD bytesToWrite = 0x1000 - (fileEntry->getFileSize() & 0xfff); + byte *zeroBytes = new byte[ bytesToWrite ]; + ZeroMemory(zeroBytes, bytesToWrite); + + /* the file size is not a multiple of 4KB, grow it */ + m_saveFile->writeFile(fileEntry,zeroBytes,bytesToWrite,&numberOfBytesWritten); + + delete [] zeroBytes; + } + + /* set up the available sector map */ + + int nSectors; + if( m_bIsEmpty ) // 4J - added this case for our empty files that we now don't create + { + nSectors = 2; + } + else + { + nSectors = (int) fileEntry->getFileSize() / SECTOR_BYTES; + } + sectorFree = new vector; + sectorFree->reserve(nSectors); + + for (int i = 0; i < nSectors; ++i) + { + sectorFree->push_back(true); + } + + sectorFree->at(0) = false; // chunk offset table + sectorFree->at(1) = false; // for the last modified info + + m_saveFile->setFilePointer( fileEntry, 0, NULL, FILE_BEGIN ); + for (int i = 0; i < SECTOR_INTS; ++i) + { + unsigned int offset = 0; + DWORD numberOfBytesRead = 0; + if( !m_bIsEmpty ) // 4J added condition, don't read back if we've just created an empty file as we don't immediately write this anymore + { + m_saveFile->readFile(fileEntry, &offset, 4, &numberOfBytesRead); + + if(saveFile->isSaveEndianDifferent()) System::ReverseULONG(&offset); + + } + offsets[i] = offset; + if (offset != 0 && (offset >> 8) + (offset & 0xFF) <= sectorFree->size()) + { + for (unsigned int sectorNum = 0; sectorNum < (offset & 0xFF); ++sectorNum) + { + sectorFree->at((offset >> 8) + sectorNum) = false; + } + } + } + for (int i = 0; i < SECTOR_INTS; ++i) + { + int lastModValue = 0; + DWORD numberOfBytesRead = 0; + if( !m_bIsEmpty ) // 4J added condition, don't read back if we've just created an empty file as we don't immediately write this anymore + { + m_saveFile->readFile(fileEntry, &lastModValue, 4, &numberOfBytesRead); + + if(saveFile->isSaveEndianDifferent()) System::ReverseINT(&lastModValue); + } + chunkTimestamps[i] = lastModValue; + } + + +// } catch (IOException e) { +// e.printStackTrace(); +// } +} + +void RegionFile::writeAllOffsets() // used for the file ConsoleSaveFile conversion between platforms +{ + if(m_bIsEmpty == false) + { + // save all the offsets and timestamps + m_saveFile->LockSaveAccess(); + + DWORD numberOfBytesWritten = 0; + m_saveFile->setFilePointer( fileEntry, 0, NULL, FILE_BEGIN ); + m_saveFile->writeFile(fileEntry,offsets, SECTOR_BYTES ,&numberOfBytesWritten); + + numberOfBytesWritten = 0; + m_saveFile->setFilePointer( fileEntry, SECTOR_BYTES, NULL, FILE_BEGIN ); + m_saveFile->writeFile(fileEntry, chunkTimestamps, SECTOR_BYTES, &numberOfBytesWritten); + + m_saveFile->ReleaseSaveAccess(); + } + +} +RegionFile::~RegionFile() +{ + delete[] offsets; + delete[] chunkTimestamps; + delete sectorFree; + m_saveFile->closeHandle( fileEntry ); +} + +__int64 RegionFile::lastModified() +{ + return _lastModified; +} + +int RegionFile::getSizeDelta() // TODO - was synchronized +{ + int ret = sizeDelta; + sizeDelta = 0; + return ret; +} + +DataInputStream *RegionFile::getChunkDataInputStream(int x, int z) // TODO - was synchronized +{ + if (outOfBounds(x, z)) + { +// debugln("READ", x, z, "out of bounds"); + return NULL; + } + + // 4J - removed try/catch +// try { + int offset = getOffset(x, z); + if (offset == 0) + { + // debugln("READ", x, z, "miss"); + return NULL; + } + + unsigned int sectorNumber = offset >> 8; + unsigned int numSectors = offset & 0xFF; + + if (sectorNumber + numSectors > sectorFree->size()) + { +// debugln("READ", x, z, "invalid sector"); + return NULL; + } + + m_saveFile->LockSaveAccess(); + + //SetFilePointer(file,sectorNumber * SECTOR_BYTES,0,FILE_BEGIN); + m_saveFile->setFilePointer( fileEntry, sectorNumber * SECTOR_BYTES, NULL, FILE_BEGIN); + + unsigned int length; + unsigned int decompLength; + unsigned int readDecompLength; + + DWORD numberOfBytesRead = 0; + + // 4J - this differs a bit from the java file format. Java has length stored as an int, then a type as a byte, then length-1 bytes of data + // We store length and decompression length as ints, then length bytes of xbox LZX compressed data + m_saveFile->readFile(fileEntry,&length,4,&numberOfBytesRead); + + if(m_saveFile->isSaveEndianDifferent()) System::ReverseULONG(&length); + + // Using to bit of length to signify that this data was compressed with RLE method + bool useRLE = false; + if( length & 0x80000000 ) + { + useRLE = true; + length &= 0x7fffffff; + } + m_saveFile->readFile(fileEntry,&decompLength,4,&numberOfBytesRead); + + if(m_saveFile->isSaveEndianDifferent()) System::ReverseULONG(&decompLength); + + if (length > SECTOR_BYTES * numSectors) + { +// debugln("READ", x, z, "invalid length: " + length + " > 4096 * " + numSectors); + + m_saveFile->ReleaseSaveAccess(); + return NULL; + } + + MemSect(50); + byte *data = new byte[length]; + byte *decomp = new byte[decompLength]; + MemSect(0); + readDecompLength = decompLength; + m_saveFile->readFile(fileEntry,data,length,&numberOfBytesRead); + + m_saveFile->ReleaseSaveAccess(); + + Compression::getCompression()->SetDecompressionType(m_saveFile->getSavePlatform()); // if this save is from another platform, set the correct decompression type + + if( useRLE ) + { + Compression::getCompression()->DecompressLZXRLE(decomp, &readDecompLength, data, length ); + } + else + { + Compression::getCompression()->Decompress(decomp, &readDecompLength, data, length ); + } + + Compression::getCompression()->SetDecompressionType(SAVE_FILE_PLATFORM_LOCAL); // and then set the decompression back to the local machine's standard type + + delete [] data; + + // 4J - was InflaterInputStream in here too, but we've already decompressed + DataInputStream *ret = new DataInputStream(new ByteArrayInputStream( byteArray( decomp, readDecompLength) )); + return ret; + +// } catch (IOException e) { +// debugln("READ", x, z, "exception"); +// return null; +// } +} + +DataOutputStream *RegionFile::getChunkDataOutputStream(int x, int z) +{ + // 4J - was DeflatorOutputStream in here too, but we've already compressed + return new DataOutputStream( new ChunkBuffer(this, x, z)); +} + +/* write a chunk at (x,z) with length bytes of data to disk */ +void RegionFile::write(int x, int z, byte *data, int length) // TODO - was synchronized +{ + // 4J Stu - Do the compression here so that we know how much space we need to store the compressed data + byte *compData = new byte[length + 2048]; // presuming compression is going to make this smaller... UPDATE - for some really small things this isn't the case. Added 2K on here to cover those. + unsigned int compLength = length; + Compression::getCompression()->CompressLZXRLE(compData,&compLength,data,length); + + int sectorsNeeded = (compLength + CHUNK_HEADER_SIZE) / SECTOR_BYTES + 1; + +// app.DebugPrintf(">>>>>>>>>>>>>> writing compressed data for 0x%.8x, %d %d\n",fileEntry->data.regionIndex,x,z); + + // maximum chunk size is 1MB + if (sectorsNeeded >= 256) + { + return; + } + + m_saveFile->LockSaveAccess(); + { + int offset = getOffset(x, z); + int sectorNumber = offset >> 8; + int sectorsAllocated = offset & 0xFF; + +#ifndef _CONTENT_PACKAGE + if(sectorNumber < 0) + { + __debugbreak(); + } +#endif + + if (sectorNumber != 0 && sectorsAllocated == sectorsNeeded) + { + /* we can simply overwrite the old sectors */ + // debug("SAVE", x, z, length, "rewrite"); + #ifndef _CONTENT_PACKAGE + //wprintf(L"Writing chunk (%d,%d) in %ls from current sector %d to %d\n", x,z, fileEntry->data.filename, sectorNumber, sectorNumber + sectorsNeeded - 1); + #endif + write(sectorNumber, compData, length, compLength); + } + else + { + /* we need to allocate new sectors */ + + /* mark the sectors previously used for this chunk as free */ + for (int i = 0; i < sectorsAllocated; ++i) + { + sectorFree->at(sectorNumber + i) = true; + } + // 4J added - zero this now unused region of the file, so it can be better compressed until it is reused + zero(sectorNumber, SECTOR_BYTES * sectorsAllocated); + + PIXBeginNamedEvent(0,"Scanning for free space\n"); + /* scan for a free space large enough to store this chunk */ + int runStart = (int)(find(sectorFree->begin(),sectorFree->end(),true) - sectorFree->begin()); // 4J - was sectorFree.indexOf(true) + int runLength = 0; + if (runStart != -1) + { + for (unsigned int i = runStart; i < sectorFree->size(); ++i) + { + if (runLength != 0) + { + if (sectorFree->at(i)) runLength++; + else runLength = 0; + } else if (sectorFree->at(i)) + { + runStart = i; + runLength = 1; + } + if (runLength >= sectorsNeeded) + { + break; + } + } + } + PIXEndNamedEvent(); + + if (runLength >= sectorsNeeded) + { + /* we found a free space large enough */ + // debug("SAVE", x, z, length, "reuse"); + sectorNumber = runStart; + setOffset(x, z, (sectorNumber << 8) | sectorsNeeded); + #ifndef _CONTENT_PACKAGE + //wprintf(L"Writing chunk (%d,%d) in %ls from old sector %d to %d\n", x,z, fileEntry->data.filename, sectorNumber, sectorNumber + sectorsNeeded - 1); + #endif + for (int i = 0; i < sectorsNeeded; ++i) + { + sectorFree->at(sectorNumber + i) = false; + } + write(sectorNumber, compData, length, compLength); + } + else + { + PIXBeginNamedEvent(0,"Expanding storage for %d sectors\n", sectorsNeeded); + /* + * no free space large enough found -- we need to grow the + * file + */ + // debug("SAVE", x, z, length, "grow"); + //SetFilePointer(file,0,0,FILE_END); + m_saveFile->setFilePointer( fileEntry, 0, NULL, FILE_END ); + + sectorNumber = (int)sectorFree->size(); + #ifndef _CONTENT_PACAKGE + //wprintf(L"Writing chunk (%d,%d) in %ls from new sector %d to %d\n", x,z, fileEntry->data.filename, sectorNumber, sectorNumber + sectorsNeeded - 1); + #endif + DWORD numberOfBytesWritten = 0; + for (int i = 0; i < sectorsNeeded; ++i) + { + //WriteFile(file,emptySector.data,SECTOR_BYTES,&numberOfBytesWritten,NULL); + m_saveFile->writeFile(fileEntry,emptySector.data,SECTOR_BYTES,&numberOfBytesWritten); + sectorFree->push_back(false); + } + sizeDelta += SECTOR_BYTES * sectorsNeeded; + + write(sectorNumber, compData, length, compLength); + setOffset(x, z, (sectorNumber << 8) | sectorsNeeded); + PIXEndNamedEvent(); + } + } + setTimestamp(x, z, (int) (System::currentTimeMillis() / 1000L)); + } + m_saveFile->ReleaseSaveAccess(); + +// } catch (IOException e) { +// e.printStackTrace(); +// } +} + +/* write a chunk data to the region file at specified sector number */ +void RegionFile::write(int sectorNumber, byte *data, int length, unsigned int compLength) +{ + DWORD numberOfBytesWritten = 0; + //SetFilePointer(file,sectorNumber * SECTOR_BYTES,0,FILE_BEGIN); + m_saveFile->setFilePointer( fileEntry, sectorNumber * SECTOR_BYTES, NULL, FILE_BEGIN ); + + // 4J - this differs a bit from the java file format. Java has length stored as an int, then a type as a byte, then length-1 bytes of data + // We store length and decompression length as ints, then length bytes of xbox LZX compressed data + + // 4J Stu - We need to do the compression at a level above this, where it is checking for free space + + compLength |= 0x80000000; // 4J - signify that this has been encoded with RLE method ( see code in getChunkDataInputStream() for matching detection of this) + m_saveFile->writeFile(fileEntry,&compLength,4,&numberOfBytesWritten); + compLength &= 0x7fffffff; + m_saveFile->writeFile(fileEntry,&length,4,&numberOfBytesWritten); + m_saveFile->writeFile(fileEntry,data,compLength,&numberOfBytesWritten); + delete[] data; +} + +void RegionFile::zero(int sectorNumber, int length) +{ + DWORD numberOfBytesWritten = 0; + //SetFilePointer(file,sectorNumber * SECTOR_BYTES,0,FILE_BEGIN); + m_saveFile->setFilePointer( fileEntry, sectorNumber * SECTOR_BYTES, NULL, FILE_BEGIN ); + m_saveFile->zeroFile( fileEntry, length, &numberOfBytesWritten ); +} + +/* is this an invalid chunk coordinate? */ +bool RegionFile::outOfBounds(int x, int z) +{ + return x < 0 || x >= 32 || z < 0 || z >= 32; +} + +int RegionFile::getOffset(int x, int z) +{ + return offsets[x + z * 32]; +} + +bool RegionFile::hasChunk(int x, int z) +{ + return getOffset(x, z) != 0; +} + +// 4J added - write the initial two sectors that used to be written in the ctor when the file was empty +void RegionFile::insertInitialSectors() +{ + m_saveFile->setFilePointer( fileEntry, 0, NULL, FILE_BEGIN ); + DWORD numberOfBytesWritten = 0; + byte zeroBytes[ SECTOR_BYTES ]; + ZeroMemory(zeroBytes, SECTOR_BYTES); + + /* we need to write the chunk offset table */ + m_saveFile->writeFile(fileEntry,zeroBytes,SECTOR_BYTES,&numberOfBytesWritten); + + // write another sector for the timestamp info + m_saveFile->writeFile(fileEntry,zeroBytes,SECTOR_BYTES,&numberOfBytesWritten); + + m_bIsEmpty = false; +} + +void RegionFile::setOffset(int x, int z, int offset) +{ + if( m_bIsEmpty ) + { + insertInitialSectors(); // 4J added + } + + DWORD numberOfBytesWritten = 0; + offsets[x + z * 32] = offset; + m_saveFile->setFilePointer( fileEntry, (x + z * 32) * 4, NULL, FILE_BEGIN ); + + m_saveFile->writeFile(fileEntry,&offset,4,&numberOfBytesWritten); +} + +void RegionFile::setTimestamp(int x, int z, int value) +{ + if( m_bIsEmpty ) + { + insertInitialSectors(); // 4J added + } + + DWORD numberOfBytesWritten = 0; + chunkTimestamps[x + z * 32] = value; + m_saveFile->setFilePointer( fileEntry, SECTOR_BYTES + (x + z * 32) * 4, NULL, FILE_BEGIN ); + + m_saveFile->writeFile(fileEntry,&value,4,&numberOfBytesWritten); +} + +void RegionFile::close() +{ + m_saveFile->closeHandle( fileEntry ); +} diff --git a/Minecraft.World/RegionFile.h b/Minecraft.World/RegionFile.h new file mode 100644 index 00000000..0717c8c2 --- /dev/null +++ b/Minecraft.World/RegionFile.h @@ -0,0 +1,97 @@ +#pragma once +#include "compression.h" +#include "InputOutputStream.h" +using namespace std; + +class FileEntry; +class ConsoleSaveFile; + +class RegionFile +{ +// 4J Stu TEMP FOR TESTING +private: + FileEntry *fileEntry; + +private: + static const int VERSION_GZIP = 1; + static const int VERSION_DEFLATE = 2; + static const int VERSION_XBOX = 3; + + static const int SECTOR_BYTES = 4096; + static const int SECTOR_INTS = SECTOR_BYTES / 4; + + static const int CHUNK_HEADER_SIZE = 8; + static byteArray emptySector; + + File *fileName; + //HANDLE file; + ConsoleSaveFile *m_saveFile; + + int *offsets; + int *chunkTimestamps; + vector *sectorFree; + int sizeDelta; + __int64 _lastModified; + bool m_bIsEmpty; // 4J added + +public: + RegionFile(ConsoleSaveFile *saveFile, File *path); + ~RegionFile(); + + /* the modification date of the region file when it was first opened */ + __int64 lastModified(); + + /* gets how much the region file has grown since it was last checked */ + int getSizeDelta(); + + /* + * gets an (uncompressed) stream representing the chunk data returns null if + * the chunk is not found or an error occurs + */ + DataInputStream *getChunkDataInputStream(int x, int z); + DataOutputStream *getChunkDataOutputStream(int x, int z); + + class ChunkBuffer : public ByteArrayOutputStream + { + private: + RegionFile *rf; + int x, z; + public: + ChunkBuffer( RegionFile *rf, int x, int z ) : ByteArrayOutputStream(8096) + { + this->rf = rf; + this->x = x; + this->z = z; + } + void close() + { + rf->write(x,z,buf.data,count); + } + }; + + /* write a chunk at (x,z) with length bytes of data to disk */ +protected: + void write(int x, int z, byte *data, int length); + + /* write a chunk data to the region file at specified sector number */ +private: + void write(int sectorNumber, byte *data, int length, unsigned int compLength); + void zero(int sectorNumber, int length); // 4J added + + /* is this an invalid chunk coordinate? */ + bool outOfBounds(int x, int z); + + int getOffset(int x, int z); + +public: + bool hasChunk(int x, int z); + +private: + void insertInitialSectors(); // 4J added + void setOffset(int x, int z, int offset); + void setTimestamp(int x, int z, int value); + +public: + void writeAllOffsets(); + void close(); +}; diff --git a/Minecraft.World/RegionFileCache.cpp b/Minecraft.World/RegionFileCache.cpp new file mode 100644 index 00000000..c21a6250 --- /dev/null +++ b/Minecraft.World/RegionFileCache.cpp @@ -0,0 +1,122 @@ +#include "stdafx.h" +#include "File.h" +#include "RegionFileCache.h" +#include "ConsoleSaveFileIO.h" + +RegionFileCache RegionFileCache::s_defaultCache; + +bool RegionFileCache::useSplitSaves(ESavePlatform platform) +{ + switch(platform) + { + case SAVE_FILE_PLATFORM_XBONE: + case SAVE_FILE_PLATFORM_PS4: + return true; + default: + return false; + }; +} + +RegionFile *RegionFileCache::_getRegionFile(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) // 4J - TODO was synchronized +{ + // 4J Jev - changed back to use of the File class. + //char file[MAX_PATH_SIZE]; + //sprintf(file,"%s\\region\\r.%d.%d.mcr",basePath,chunkX >> 5,chunkZ >> 5); + + //File regionDir(basePath, L"region"); + + //File file(regionDir, wstring(L"r.") + _toString(chunkX>>5) + L"." + _toString(chunkZ>>5) + L".mcr" ); + MemSect(31); + File file; + if(useSplitSaves(saveFile->getSavePlatform())) + { + file = File( prefix + wstring(L"r.") + _toString(chunkX>>4) + L"." + _toString(chunkZ>>4) + L".mcr" ); + } + else + { + file = File( prefix + wstring(L"r.") + _toString(chunkX>>5) + L"." + _toString(chunkZ>>5) + L".mcr" ); + } + MemSect(0); + + RegionFile *ref = NULL; + AUTO_VAR(it, cache.find(file)); + if( it != cache.end() ) + ref = it->second; + + // 4J Jev, put back in. + if (ref != NULL) + { + return ref; + } + + // 4J Stu - Remove for new save files + /* + if (!regionDir.exists()) + { + regionDir.mkdirs(); + } + */ + if (cache.size() >= MAX_CACHE_SIZE) + { + _clear(); + } + + RegionFile *reg = new RegionFile(saveFile, &file); + cache[file] = reg; // 4J - this was originally a softReferenc + return reg; + +} + +void RegionFileCache::_clear() // 4J - TODO was synchronized +{ + AUTO_VAR(itEnd, cache.end()); + for( AUTO_VAR(it, cache.begin()); it != itEnd; it++ ) + { + // 4J - removed try/catch +// try { + RegionFile *regionFile = it->second; + if (regionFile != NULL) + { + regionFile->close(); + } + delete regionFile; +// } catch (IOException e) { +// e.printStackTrace(); +// } + } + cache.clear(); +} + +int RegionFileCache::_getSizeDelta(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) +{ + RegionFile *r = _getRegionFile(saveFile, prefix, chunkX, chunkZ); + return r->getSizeDelta(); +} + +DataInputStream *RegionFileCache::_getChunkDataInputStream(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) +{ + RegionFile* r = _getRegionFile(saveFile, prefix, chunkX, chunkZ); + if(useSplitSaves(saveFile->getSavePlatform())) + { + return r->getChunkDataInputStream(chunkX & 15, chunkZ & 15); + } + else + { + + return r->getChunkDataInputStream(chunkX & 31, chunkZ & 31); + } +} + +DataOutputStream *RegionFileCache::_getChunkDataOutputStream(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) +{ + RegionFile* r = _getRegionFile(saveFile, prefix, chunkX, chunkZ); + if(useSplitSaves(saveFile->getSavePlatform())) + { + return r->getChunkDataOutputStream(chunkX & 15, chunkZ & 15); + } + else + { + + return r->getChunkDataOutputStream(chunkX & 31, chunkZ & 31); + } +} diff --git a/Minecraft.World/RegionFileCache.h b/Minecraft.World/RegionFileCache.h new file mode 100644 index 00000000..03e576cc --- /dev/null +++ b/Minecraft.World/RegionFileCache.h @@ -0,0 +1,35 @@ +#pragma once +#include "RegionFile.h" +#include "Reference.h" +#include "File.h" + +class RegionFileCache +{ + friend class ConsoleSaveFileOriginal; +private: + static const int MAX_CACHE_SIZE = 256; + + unordered_map cache; + + static RegionFileCache s_defaultCache; + +public: + // Made public and non-static so we can have a cache for input and output files + RegionFileCache() {} + + RegionFile *_getRegionFile(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ); // 4J - TODO was synchronized + void _clear(); // 4J - TODO was synchronized + int _getSizeDelta(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ); + DataInputStream *_getChunkDataInputStream(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ); + DataOutputStream *_getChunkDataOutputStream(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ); + + // Keep static version for general game usage + static RegionFile *getRegionFile(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) { return s_defaultCache._getRegionFile(saveFile, prefix, chunkX, chunkZ); } + static void clear() { s_defaultCache._clear(); } + static int getSizeDelta(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) { return s_defaultCache._getSizeDelta(saveFile, prefix, chunkX, chunkZ); } + static DataInputStream *getChunkDataInputStream(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) { return s_defaultCache._getChunkDataInputStream(saveFile, prefix, chunkX, chunkZ); } + static DataOutputStream *getChunkDataOutputStream(ConsoleSaveFile *saveFile, const wstring &prefix, int chunkX, int chunkZ) { return s_defaultCache._getChunkDataOutputStream(saveFile, prefix, chunkX, chunkZ); } + +private: + bool useSplitSaves(ESavePlatform platform); +}; diff --git a/Minecraft.World/RegionHillsLayer.cpp b/Minecraft.World/RegionHillsLayer.cpp new file mode 100644 index 00000000..dad114b0 --- /dev/null +++ b/Minecraft.World/RegionHillsLayer.cpp @@ -0,0 +1,78 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" +#include "IntCache.h" +#include "RegionHillsLayer.h" + +RegionHillsLayer::RegionHillsLayer(__int64 seed, shared_ptr parent) : Layer(seed) +{ + this->parent = parent; +} + +intArray RegionHillsLayer::getArea(int xo, int yo, int w, int h) +{ + intArray b = parent->getArea(xo - 1, yo - 1, w + 2, h + 2); + + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + initRandom(x + xo, y + yo); + int old = b[(x + 1) + (y + 1) * (w + 2)]; + if (nextRandom(3) == 0) + { + int next = old; + if (old == Biome::desert->id) + { + next = Biome::desertHills->id; + } + else if (old == Biome::forest->id) + { + next = Biome::forestHills->id; + } + else if (old == Biome::taiga->id) + { + next = Biome::taigaHills->id; + } + else if (old == Biome::plains->id) + { + next = Biome::forest->id; + } + else if (old == Biome::iceFlats->id) + { + next = Biome::iceMountains->id; + } + else if (old == Biome::jungle->id) + { + next = Biome::jungleHills->id; + + } + if (next == old) + { + result[x + y * w] = old; + } + else + { + int _n = b[(x + 1) + (y + 1 - 1) * (w + 2)]; + int _e = b[(x + 1 + 1) + (y + 1) * (w + 2)]; + int _w = b[(x + 1 - 1) + (y + 1) * (w + 2)]; + int _s = b[(x + 1) + (y + 1 + 1) * (w + 2)]; + if (_n == old && _e == old && _w == old && _s == old) + { + result[x + y * w] = next; + } + else + { + result[x + y * w] = old; + } + } + } + else + { + result[x + y * w] = old; + } + } + } + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/RegionHillsLayer.h b/Minecraft.World/RegionHillsLayer.h new file mode 100644 index 00000000..30d7eced --- /dev/null +++ b/Minecraft.World/RegionHillsLayer.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Layer.h" + +class RegionHillsLayer : public Layer +{ +public: + RegionHillsLayer(__int64 seed, shared_ptr parent); + + intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/RemoveEntitiesPacket.cpp b/Minecraft.World/RemoveEntitiesPacket.cpp new file mode 100644 index 00000000..609cfa77 --- /dev/null +++ b/Minecraft.World/RemoveEntitiesPacket.cpp @@ -0,0 +1,56 @@ +#include "stdafx.h" +#include +#include "ArrayWithLength.h" +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "RemoveEntitiesPacket.h" + +RemoveEntitiesPacket::RemoveEntitiesPacket() +{ +} + +RemoveEntitiesPacket::RemoveEntitiesPacket(intArray ids) +{ + this->ids = ids; +} + +RemoveEntitiesPacket::~RemoveEntitiesPacket() +{ + delete ids.data; +} + +void RemoveEntitiesPacket::read(DataInputStream *dis) //throws IOException +{ + ids = intArray(dis->readByte()); + for(unsigned int i = 0; i < ids.length; ++i) + { + ids[i] = dis->readInt(); + } +} + +void RemoveEntitiesPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeByte(ids.length); + for(unsigned int i = 0; i < ids.length; ++i) + { + dos->writeInt(ids[i]); + } +} + +void RemoveEntitiesPacket::handle(PacketListener *listener) +{ + listener->handleRemoveEntity(shared_from_this()); +} + +int RemoveEntitiesPacket::getEstimatedSize() +{ + return 1 + (ids.length * 4); +} + +/* + 4J: These are necesary on the PS3. + (and 4). +*/ +#if (defined __PS3__ || defined __ORBIS__ || defined __PSVITA__) +const int RemoveEntitiesPacket::MAX_PER_PACKET; +#endif diff --git a/Minecraft.World/RemoveEntitiesPacket.h b/Minecraft.World/RemoveEntitiesPacket.h new file mode 100644 index 00000000..2e734e71 --- /dev/null +++ b/Minecraft.World/RemoveEntitiesPacket.h @@ -0,0 +1,27 @@ +#pragma once +using namespace std; + +#include "BasicTypeContainers.h" +#include "Packet.h" + +class RemoveEntitiesPacket : public Packet, public enable_shared_from_this +{ +public: + static const int MAX_PER_PACKET = Byte::MAX_VALUE; + + intArray ids; + + RemoveEntitiesPacket(); + RemoveEntitiesPacket(intArray ids); + ~RemoveEntitiesPacket(); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new RemoveEntitiesPacket()); } + virtual int getId() { return 29; } +}; + diff --git a/Minecraft.World/RemoveMobEffectPacket.cpp b/Minecraft.World/RemoveMobEffectPacket.cpp new file mode 100644 index 00000000..fcda6911 --- /dev/null +++ b/Minecraft.World/RemoveMobEffectPacket.cpp @@ -0,0 +1,39 @@ +#include "stdafx.h" +#include "net.minecraft.world.effect.h" +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "RemoveMobEffectPacket.h" + + + +RemoveMobEffectPacket::RemoveMobEffectPacket() +{ +} + +RemoveMobEffectPacket::RemoveMobEffectPacket(int entityId, MobEffectInstance *effect) +{ + this->entityId = entityId; + this->effectId = (byte) (effect->getId() & 0xff); +} + +void RemoveMobEffectPacket::read(DataInputStream *dis) +{ + entityId = dis->readInt(); + effectId = dis->readByte(); +} + +void RemoveMobEffectPacket::write(DataOutputStream *dos) +{ + dos->writeInt(entityId); + dos->writeByte(effectId); +} + +void RemoveMobEffectPacket::handle(PacketListener *listener) +{ + listener->handleRemoveMobEffect(shared_from_this()); +} + +int RemoveMobEffectPacket::getEstimatedSize() +{ + return 5; +} \ No newline at end of file diff --git a/Minecraft.World/RemoveMobEffectPacket.h b/Minecraft.World/RemoveMobEffectPacket.h new file mode 100644 index 00000000..d69a4ed4 --- /dev/null +++ b/Minecraft.World/RemoveMobEffectPacket.h @@ -0,0 +1,23 @@ +#pragma once + +class MobEffectInstance; + +#include "Packet.h" + +class RemoveMobEffectPacket : public Packet, public enable_shared_from_this +{ + public: + int entityId; + char effectId; + + RemoveMobEffectPacket(); + RemoveMobEffectPacket(int entityId, MobEffectInstance *effect); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); +public: + static shared_ptr create() { return shared_ptr(new RemoveMobEffectPacket()); } + virtual int getId() { return 42; } +}; \ No newline at end of file diff --git a/Minecraft.World/RepairContainer.cpp b/Minecraft.World/RepairContainer.cpp new file mode 100644 index 00000000..7b2a51bc --- /dev/null +++ b/Minecraft.World/RepairContainer.cpp @@ -0,0 +1,14 @@ +#include "stdafx.h" +#include "RepairMenu.h" +#include "RepairContainer.h" + +RepairContainer::RepairContainer(RepairMenu *menu, int name, int size) : SimpleContainer(name, size) +{ + m_menu = menu; +} + +void RepairContainer::setChanged() +{ + SimpleContainer::setChanged(); + m_menu->slotsChanged(shared_from_this()); +} \ No newline at end of file diff --git a/Minecraft.World/RepairContainer.h b/Minecraft.World/RepairContainer.h new file mode 100644 index 00000000..758132c0 --- /dev/null +++ b/Minecraft.World/RepairContainer.h @@ -0,0 +1,15 @@ +#pragma once + +#include "SimpleContainer.h" + +class RepairMenu; + +class RepairContainer : public SimpleContainer, public enable_shared_from_this +{ +private: + RepairMenu *m_menu; + +public: + RepairContainer(RepairMenu *menu, int name, int size); + void setChanged(); +}; \ No newline at end of file diff --git a/Minecraft.World/RepairMenu.cpp b/Minecraft.World/RepairMenu.cpp new file mode 100644 index 00000000..d246b73f --- /dev/null +++ b/Minecraft.World/RepairMenu.cpp @@ -0,0 +1,403 @@ +#include "stdafx.h" +#include "net.minecraft.world.inventory.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "RepairMenu.h" + +RepairMenu::RepairMenu(shared_ptr inventory, Level *level, int xt, int yt, int zt, shared_ptr player) +{ + resultSlots = shared_ptr( new ResultContainer() ); + repairSlots = shared_ptr( new RepairContainer(this,IDS_REPAIR_AND_NAME, 2) ); + cost = 0; + repairItemCountCost = 0; + + this->level = level; + this->x = xt; + this->y = yt; + this->z = zt; + this->player = player; + + addSlot(new Slot(repairSlots, INPUT_SLOT, 27, 43 + 4)); + addSlot(new Slot(repairSlots, ADDITIONAL_SLOT, 76, 43 + 4)); + + // 4J Stu - Anonymous class here is now RepairResultSlot + addSlot(new RepairResultSlot(this, xt, yt, zt, resultSlots, RESULT_SLOT, 134, 43 + 4)); + + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x + y * 9 + 9, 8 + x * 18, 84 + y * 18)); + } + } + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x, 8 + x * 18, 142)); + } +} + +void RepairMenu::slotsChanged(shared_ptr container) +{ + AbstractContainerMenu::slotsChanged(); + + if (container == repairSlots) createResult(); +} + +void RepairMenu::createResult() +{ + shared_ptr input = repairSlots->getItem(INPUT_SLOT); + cost = 0; + int price = 0; + int tax = 0; + int namingCost = 0; + + if (DEBUG_COST) app.DebugPrintf("----"); + + if (input == NULL) + { + resultSlots->setItem(0, nullptr); + cost = 0; + return; + } + else + { + shared_ptr result = input->copy(); + shared_ptr addition = repairSlots->getItem(ADDITIONAL_SLOT); + unordered_map *enchantments = EnchantmentHelper::getEnchantments(result); + bool usingBook = false; + + tax += input->getBaseRepairCost() + (addition == NULL ? 0 : addition->getBaseRepairCost()); + if (DEBUG_COST) + { + app.DebugPrintf("Starting with base repair tax of %d (%d + %d)\n", tax, input->getBaseRepairCost(), (addition == NULL ? 0 : addition->getBaseRepairCost())); + } + + repairItemCountCost = 0; + + if (addition != NULL) + { + usingBook = addition->id == Item::enchantedBook_Id && Item::enchantedBook->getEnchantments(addition)->size() > 0; + + if (result->isDamageableItem() && Item::items[result->id]->isValidRepairItem(input, addition)) + { + int repairAmount = min(result->getDamageValue(), result->getMaxDamage() / 4); + if (repairAmount <= 0) + { + resultSlots->setItem(0, nullptr); + cost = 0; + return; + } + else + { + int count = 0; + while (repairAmount > 0 && count < addition->count) + { + int resultDamage = result->getDamageValue() - repairAmount; + result->setAuxValue(resultDamage); + price += max(1, repairAmount / 100) + enchantments->size(); + + repairAmount = min(result->getDamageValue(), result->getMaxDamage() / 4); + count++; + } + repairItemCountCost = count; + } + } + else if (!usingBook && (result->id != addition->id || !result->isDamageableItem())) + { + resultSlots->setItem(0, nullptr); + cost = 0; + return; + } + else + { + if (result->isDamageableItem() && !usingBook) + { + int remaining1 = input->getMaxDamage() - input->getDamageValue(); + int remaining2 = addition->getMaxDamage() - addition->getDamageValue(); + int additional = remaining2 + result->getMaxDamage() * 12 / 100; + int remaining = remaining1 + additional; + int resultDamage = result->getMaxDamage() - remaining; + if (resultDamage < 0) resultDamage = 0; + + if (resultDamage < result->getAuxValue()) + { + result->setAuxValue(resultDamage); + price += max(1, additional / 100); + if (DEBUG_COST) + { + app.DebugPrintf("Repairing; price is now %d (went up by %d)\n", price, max(1, additional / 100) ); + } + } + } + + unordered_map *additionalEnchantments = EnchantmentHelper::getEnchantments(addition); + + for(AUTO_VAR(it, additionalEnchantments->begin()); it != additionalEnchantments->end(); ++it) + { + int id = it->first; + Enchantment *enchantment = Enchantment::enchantments[id]; + AUTO_VAR(localIt, enchantments->find(id)); + int current = localIt != enchantments->end() ? localIt->second : 0; + int level = it->second; + level = (current == level) ? level += 1 : max(level, current); + int extra = level - current; + bool compatible = enchantment->canEnchant(input); + + if (player->abilities.instabuild) compatible = true; + + for(AUTO_VAR(it2, enchantments->begin()); it2 != enchantments->end(); ++it2) + { + int other = it2->first; + if (other != id && !enchantment->isCompatibleWith(Enchantment::enchantments[other])) + { + compatible = false; + + price += extra; + if (DEBUG_COST) + { + app.DebugPrintf("Enchantment incompatibility fee; price is now %d (went up by %d)\n", price, extra); + } + } + } + + if (!compatible) continue; + if (level > enchantment->getMaxLevel()) level = enchantment->getMaxLevel(); + (*enchantments)[id] = level; + int fee = 0; + + switch (enchantment->getFrequency()) + { + case Enchantment::FREQ_COMMON: + fee = 1; + break; + case Enchantment::FREQ_UNCOMMON: + fee = 2; + break; + case Enchantment::FREQ_RARE: + fee = 4; + break; + case Enchantment::FREQ_VERY_RARE: + fee = 8; + break; + } + + if (usingBook) fee = max(1, fee / 2); + + price += fee * extra; + if (DEBUG_COST) + { + app.DebugPrintf("Enchantment increase fee; price is now %d (went up by %d)\n", price, fee*extra); + } + } + delete additionalEnchantments; + } + } + + if (itemName.length() > 0 && !equalsIgnoreCase(itemName, input->getHoverName()) && itemName.length() > 0) + { + namingCost = input->isDamageableItem() ? 7 : input->count * 5; + + price += namingCost; + if (DEBUG_COST) + { + app.DebugPrintf("Naming cost; price is now %d (went up by %d)", price, namingCost); + } + + if (input->hasCustomHoverName()) + { + tax += namingCost / 2; + + if (DEBUG_COST) + { + app.DebugPrintf("Already-named tax; tax is now %d (went up by %d)", tax, (namingCost / 2)); + } + } + + result->setHoverName(itemName); + } + + int count = 0; + for(AUTO_VAR(it, enchantments->begin()); it != enchantments->end(); ++it) + { + int id = it->first; + Enchantment *enchantment = Enchantment::enchantments[id]; + int level = it->second; + int fee = 0; + + count++; + + switch (enchantment->getFrequency()) + { + case Enchantment::FREQ_COMMON: + fee = 1; + break; + case Enchantment::FREQ_UNCOMMON: + fee = 2; + break; + case Enchantment::FREQ_RARE: + fee = 4; + break; + case Enchantment::FREQ_VERY_RARE: + fee = 8; + break; + } + + if (usingBook) fee = max(1, fee / 2); + + tax += count + level * fee; + if (DEBUG_COST) + { + app.DebugPrintf("Enchantment tax; tax is now %d (went up by %d)", tax, (count + level * fee)); + } + } + + if (usingBook) tax = max(1, tax / 2); + + cost = tax + price; + if (price <= 0) + { + if (DEBUG_COST) app.DebugPrintf("No purchase, only tax; aborting"); + result = nullptr; + } + if (namingCost == price && namingCost > 0 && cost >= 40) + { + if (DEBUG_COST) app.DebugPrintf("Cost is too high; aborting"); + app.DebugPrintf("Naming an item only, cost too high; giving discount to cap cost to 39 levels"); + cost = 39; + } + if (cost >= 40 && !player->abilities.instabuild) + { + if (DEBUG_COST) app.DebugPrintf("Cost is too high; aborting"); + result = nullptr; + } + + if (result != NULL) + { + int baseCost = result->getBaseRepairCost(); + if (addition != NULL && baseCost < addition->getBaseRepairCost()) baseCost = addition->getBaseRepairCost(); + if (result->hasCustomHoverName()) baseCost -= 9; + if (baseCost < 0) baseCost = 0; + baseCost += 2; + + result->setRepairCost(baseCost); + EnchantmentHelper::setEnchantments(enchantments, result); + } + + resultSlots->setItem(0, result); + } + + broadcastChanges(); + + if (DEBUG_COST) + { + if (level->isClientSide) + { + app.DebugPrintf("CLIENT Cost is %d (%d price, %d tax)\n", cost, price, tax); + } + else + { + app.DebugPrintf("SERVER Cost is %d (%d price, %d tax)\n", cost, price, tax); + } + } +} + +void RepairMenu::sendData(int id, int value) +{ + AbstractContainerMenu::sendData(id, value); +} + +void RepairMenu::addSlotListener(ContainerListener *listener) +{ + AbstractContainerMenu::addSlotListener(listener); + listener->setContainerData(this, DATA_TOTAL_COST, cost); +} + +void RepairMenu::setData(int id, int value) +{ + if (id == DATA_TOTAL_COST) cost = value; +} + +void RepairMenu::removed(shared_ptr player) +{ + AbstractContainerMenu::removed(player); + if (level->isClientSide) return; + + for (int i = 0; i < repairSlots->getContainerSize(); i++) + { + shared_ptr item = repairSlots->removeItemNoUpdate(i); + if (item != NULL) + { + player->drop(item); + } + } +} + +bool RepairMenu::stillValid(shared_ptr player) +{ + if (level->getTile(x, y, z) != Tile::anvil_Id) return false; + if (player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) > 8 * 8) return false; + return true; +} + +shared_ptr RepairMenu::quickMoveStack(shared_ptr player, int slotIndex) +{ + shared_ptr clicked = nullptr; + Slot *slot = slots->at(slotIndex); + if (slot != NULL && slot->hasItem()) + { + shared_ptr stack = slot->getItem(); + clicked = stack->copy(); + + if (slotIndex == RESULT_SLOT) + { + if (!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, true)) + { + return nullptr; + } + slot->onQuickCraft(stack, clicked); + } + else if (slotIndex == INPUT_SLOT || slotIndex == ADDITIONAL_SLOT) + { + if (!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, false)) + { + return nullptr; + } + } + else if (slotIndex >= INV_SLOT_START && slotIndex < USE_ROW_SLOT_END) + { + if (!moveItemStackTo(stack, INPUT_SLOT, RESULT_SLOT, false)) + { + return nullptr; + } + } + if (stack->count == 0) + { + slot->set(nullptr); + } + else + { + slot->setChanged(); + } + if (stack->count == clicked->count) + { + return nullptr; + } + else + { + slot->onTake(player, stack); + } + } + return clicked; +} + +void RepairMenu::setItemName(const wstring &name) +{ + this->itemName = name; + if (getSlot(RESULT_SLOT)->hasItem()) + { + getSlot(RESULT_SLOT)->getItem()->setHoverName(itemName); + } + createResult(); +} \ No newline at end of file diff --git a/Minecraft.World/RepairMenu.h b/Minecraft.World/RepairMenu.h new file mode 100644 index 00000000..e0c90267 --- /dev/null +++ b/Minecraft.World/RepairMenu.h @@ -0,0 +1,55 @@ +#pragma once + +#include "AbstractContainerMenu.h" + +class RepairMenu : public AbstractContainerMenu +{ + friend class RepairResultSlot; +private: + static const bool DEBUG_COST = false; + +public: + static const int INPUT_SLOT = 0; + static const int ADDITIONAL_SLOT = 1; + static const int RESULT_SLOT = 2; + + static const int INV_SLOT_START = RESULT_SLOT + 1; + static const int INV_SLOT_END = INV_SLOT_START + 9 * 3; + static const int USE_ROW_SLOT_START = INV_SLOT_END; + static const int USE_ROW_SLOT_END = USE_ROW_SLOT_START + 9; + +public: + static const int DATA_TOTAL_COST = 0; + +private: + shared_ptr resultSlots; + + // 4J Stu - anonymous class here now RepairContainer + shared_ptr repairSlots; + + Level *level; + int x, y, z; + +public: + int cost; + +private: + int repairItemCountCost; + wstring itemName; + shared_ptr player; + +public: + using AbstractContainerMenu::slotsChanged; + + RepairMenu(shared_ptr inventory, Level *level, int xt, int yt, int zt, shared_ptr player); + + void slotsChanged(shared_ptr container); + void createResult(); + void sendData(int id, int value); + void addSlotListener(ContainerListener *listener); + void setData(int id, int value); + void removed(shared_ptr player); + bool stillValid(shared_ptr player); + shared_ptr quickMoveStack(shared_ptr player, int slotIndex); + void setItemName(const wstring &name); +}; diff --git a/Minecraft.World/RepairResultSlot.cpp b/Minecraft.World/RepairResultSlot.cpp new file mode 100644 index 00000000..041a64a7 --- /dev/null +++ b/Minecraft.World/RepairResultSlot.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include "net.minecraft.world.inventory.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.entity.player.h" +#include "RepairResultSlot.h" + +RepairResultSlot::RepairResultSlot(RepairMenu *menu, int xt, int yt, int zt, shared_ptr container, int slot, int x, int y) : Slot(container, slot, x, y) +{ + m_menu = menu; + this->xt = xt; + this->yt = yt; + this->zt = zt; +} + +bool RepairResultSlot::mayPlace(shared_ptr item) +{ + return false; +} + +bool RepairResultSlot::mayPickup(shared_ptr player) +{ + return (player->abilities.instabuild || player->experienceLevel >= m_menu->cost) && (m_menu->cost > 0 && hasItem()); +} + +void RepairResultSlot::onTake(shared_ptr player, shared_ptr carried) +{ + if (!player->abilities.instabuild) player->withdrawExperienceLevels(m_menu->cost); + m_menu->repairSlots->setItem(RepairMenu::INPUT_SLOT, nullptr); + if (m_menu->repairItemCountCost > 0) + { + shared_ptr addition = m_menu->repairSlots->getItem(RepairMenu::ADDITIONAL_SLOT); + if (addition != NULL && addition->count > m_menu->repairItemCountCost) + { + addition->count -= m_menu->repairItemCountCost; + m_menu->repairSlots->setItem(RepairMenu::ADDITIONAL_SLOT, addition); + } + else + { + m_menu->repairSlots->setItem(RepairMenu::ADDITIONAL_SLOT, nullptr); + } + } + else + { + m_menu->repairSlots->setItem(RepairMenu::ADDITIONAL_SLOT, nullptr); + } + m_menu->cost = 0; + + if (!player->abilities.instabuild && !m_menu->level->isClientSide && m_menu->level->getTile(xt, yt, zt) == Tile::anvil->id && player->getRandom()->nextFloat() < 0.12f) + { + int data = m_menu->level->getData(xt, yt, zt); + int dir = data & 0x3; + int dmg = data >> 2; + + if (++dmg > 2) + { + m_menu->level->setTile(xt, yt, zt, 0); + m_menu->level->levelEvent(LevelEvent::SOUND_ANVIL_BROKEN, xt, yt, zt, 0); + } + else + { + m_menu->level->setData(xt, yt, zt, dir | (dmg << 2)); + m_menu->level->levelEvent(LevelEvent::SOUND_ANVIL_USED, xt, yt, zt, 0); + } + } + else if (!m_menu->level->isClientSide) + { + m_menu->level->levelEvent(LevelEvent::SOUND_ANVIL_USED, xt, yt, zt, 0); + } +} + +bool RepairResultSlot::mayCombine(shared_ptr second) +{ + return false; +} \ No newline at end of file diff --git a/Minecraft.World/RepairResultSlot.h b/Minecraft.World/RepairResultSlot.h new file mode 100644 index 00000000..1895ca30 --- /dev/null +++ b/Minecraft.World/RepairResultSlot.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Slot.h" + +class RepairMenu; + +class RepairResultSlot : public Slot +{ +private: + RepairMenu *m_menu; + int xt, yt, zt; + +public: + RepairResultSlot(RepairMenu *menu, int xt, int yt, int zt, shared_ptr container, int slot, int x, int y); + + bool mayPlace(shared_ptr item); + bool mayPickup(shared_ptr player); + void onTake(shared_ptr player, shared_ptr carried); + virtual bool mayCombine(shared_ptr item); // 4J Added +}; \ No newline at end of file diff --git a/Minecraft.World/RespawnPacket.cpp b/Minecraft.World/RespawnPacket.cpp new file mode 100644 index 00000000..5c04bbeb --- /dev/null +++ b/Minecraft.World/RespawnPacket.cpp @@ -0,0 +1,97 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "RespawnPacket.h" +#include "LevelType.h" + +RespawnPacket::RespawnPacket() +{ + this->dimension = 0; + this->difficulty = 1; + this->mapSeed = 0; + this->mapHeight = 0; + this->playerGameType = NULL; + this->m_newSeaLevel = false; + m_pLevelType = NULL; + m_newEntityId = 0; + m_xzSize = LEVEL_MAX_WIDTH; + m_hellScale = HELL_LEVEL_MAX_SCALE; +} + +RespawnPacket::RespawnPacket(char dimension, __int64 mapSeed, int mapHeight, GameType *playerGameType, char difficulty, LevelType *pLevelType, bool newSeaLevel, int newEntityId, int xzSize, int hellScale) +{ + this->dimension = dimension; + this->mapSeed = mapSeed; + this->mapHeight = mapHeight; + this->playerGameType = playerGameType; + this->difficulty = difficulty; + this->m_newSeaLevel = newSeaLevel; + this->m_pLevelType=pLevelType; + this->m_newEntityId = newEntityId; + m_xzSize = xzSize; + m_hellScale = hellScale; + app.DebugPrintf("RespawnPacket - Difficulty = %d\n",difficulty); + +} + +void RespawnPacket::handle(PacketListener *listener) +{ + listener->handleRespawn(shared_from_this()); +} + +void RespawnPacket::read(DataInputStream *dis) //throws IOException +{ + dimension = dis->readByte(); + playerGameType = GameType::byId(dis->readByte()); + mapHeight = dis->readShort(); + wstring typeName = readUtf(dis, 16); + m_pLevelType = LevelType::getLevelType(typeName); + if (m_pLevelType == NULL) + { + m_pLevelType = LevelType::lvl_normal; + } + mapSeed = dis->readLong(); + difficulty = dis->readByte(); + m_newSeaLevel = dis->readBoolean(); + m_newEntityId = dis->readShort(); +#ifdef _LARGE_WORLDS + m_xzSize = dis->readShort(); + m_hellScale = dis->read(); +#endif + app.DebugPrintf("RespawnPacket::read - Difficulty = %d\n",difficulty); + +} + +void RespawnPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeByte(dimension); + dos->writeByte(playerGameType->getId()); + dos->writeShort(mapHeight); + if (m_pLevelType == NULL) + { + writeUtf(L"", dos); + } + else + { + writeUtf(m_pLevelType->getGeneratorName(), dos); + } + dos->writeLong(mapSeed); + dos->writeByte(difficulty); + dos->writeBoolean(m_newSeaLevel); + dos->writeShort(m_newEntityId); +#ifdef _LARGE_WORLDS + dos->writeShort(m_xzSize); + dos->write(m_hellScale); +#endif +} + +int RespawnPacket::getEstimatedSize() +{ + int length=0; + if (m_pLevelType != NULL) + { + length = (int)m_pLevelType->getGeneratorName().length(); + } + return 13+length; +} diff --git a/Minecraft.World/RespawnPacket.h b/Minecraft.World/RespawnPacket.h new file mode 100644 index 00000000..dc341ea1 --- /dev/null +++ b/Minecraft.World/RespawnPacket.h @@ -0,0 +1,34 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class LevelType; +class GameType; + +class RespawnPacket : public Packet, public enable_shared_from_this +{ +public: + char dimension; + char difficulty; + __int64 mapSeed; + int mapHeight; + GameType *playerGameType; + bool m_newSeaLevel; // 4J added + LevelType *m_pLevelType; + int m_newEntityId; + int m_xzSize; // 4J Added + int m_hellScale; // 4J Added + + RespawnPacket(); + RespawnPacket(char dimension, __int64 mapSeed, int mapHeight, GameType *playerGameType, char difficulty, LevelType *pLevelType, bool newSeaLevel, int newEntityId, int xzSize, int hellScale); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new RespawnPacket()); } + virtual int getId() { return 9; } +}; diff --git a/Minecraft.World/RestrictOpenDoorGoal.cpp b/Minecraft.World/RestrictOpenDoorGoal.cpp new file mode 100644 index 00000000..e8620a71 --- /dev/null +++ b/Minecraft.World/RestrictOpenDoorGoal.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.village.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "RestrictOpenDoorGoal.h" + +RestrictOpenDoorGoal::RestrictOpenDoorGoal(PathfinderMob *mob) +{ + this->mob = mob; +} + +bool RestrictOpenDoorGoal::canUse() +{ + if (mob->level->isDay()) return false; + shared_ptr village = mob->level->villages->getClosestVillage(Mth::floor(mob->x), Mth::floor(mob->y), Mth::floor(mob->z), 16); + if (village == NULL) return false; + shared_ptr _doorInfo = village->getClosestDoorInfo(Mth::floor(mob->x), Mth::floor(mob->y), Mth::floor(mob->z)); + if (_doorInfo == NULL) return false; + doorInfo = _doorInfo; + return _doorInfo->distanceToInsideSqr(Mth::floor(mob->x), Mth::floor(mob->y), Mth::floor(mob->z)) < 1.5 * 1.5; +} + +bool RestrictOpenDoorGoal::canContinueToUse() +{ + if (mob->level->isDay()) return false; + shared_ptr _doorInfo = doorInfo.lock(); + if ( _doorInfo == NULL ) return false; + return !_doorInfo->removed && _doorInfo->isInsideSide(Mth::floor(mob->x), Mth::floor(mob->z)); +} + +void RestrictOpenDoorGoal::start() +{ + mob->getNavigation()->setCanOpenDoors(false); + mob->getNavigation()->setCanPassDoors(false); +} + +void RestrictOpenDoorGoal::stop() +{ + mob->getNavigation()->setCanOpenDoors(true); + mob->getNavigation()->setCanPassDoors(true); + doorInfo = weak_ptr(); +} + +void RestrictOpenDoorGoal::tick() +{ + shared_ptr _doorInfo = doorInfo.lock(); + if ( _doorInfo ) _doorInfo->incBookingCount(); +} \ No newline at end of file diff --git a/Minecraft.World/RestrictOpenDoorGoal.h b/Minecraft.World/RestrictOpenDoorGoal.h new file mode 100644 index 00000000..184ca513 --- /dev/null +++ b/Minecraft.World/RestrictOpenDoorGoal.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Goal.h" + +class PathfinderMob; +class DoorInfo; + +class RestrictOpenDoorGoal : public Goal +{ +private: + PathfinderMob *mob; + weak_ptr doorInfo; + +public: + RestrictOpenDoorGoal(PathfinderMob *mob); + + virtual bool canUse(); + virtual bool canContinueToUse(); + virtual void start(); + virtual void stop(); + virtual void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/RestrictSunGoal.cpp b/Minecraft.World/RestrictSunGoal.cpp new file mode 100644 index 00000000..b845c53f --- /dev/null +++ b/Minecraft.World/RestrictSunGoal.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "RestrictSunGoal.h" + +RestrictSunGoal::RestrictSunGoal(PathfinderMob *mob) +{ + this->mob = mob; +} + +bool RestrictSunGoal::canUse() +{ + return mob->level->isDay(); +} + +void RestrictSunGoal::start() +{ + mob->getNavigation()->setAvoidSun(true); +} + +void RestrictSunGoal::stop() +{ + mob->getNavigation()->setAvoidSun(false); +} \ No newline at end of file diff --git a/Minecraft.World/RestrictSunGoal.h b/Minecraft.World/RestrictSunGoal.h new file mode 100644 index 00000000..bf89aeef --- /dev/null +++ b/Minecraft.World/RestrictSunGoal.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Goal.h" + +class RestrictSunGoal : public Goal +{ +private: + PathfinderMob *mob; + +public: + RestrictSunGoal(PathfinderMob *mob); + + bool canUse(); + void start(); + void stop(); +}; \ No newline at end of file diff --git a/Minecraft.World/ResultContainer.cpp b/Minecraft.World/ResultContainer.cpp new file mode 100644 index 00000000..c28b3cf3 --- /dev/null +++ b/Minecraft.World/ResultContainer.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "ResultContainer.h" + +ResultContainer::ResultContainer() : Container() +{ + items = new ItemInstanceArray(1); +} + +unsigned int ResultContainer::getContainerSize() +{ + return 1; +} + +shared_ptr ResultContainer::getItem(unsigned int slot) +{ + return (*items)[0]; +} + +int ResultContainer::getName() +{ + return 0; +} + +shared_ptr ResultContainer::removeItem(unsigned int slot, int count) +{ + if ((*items)[0] != NULL) + { + shared_ptr item = (*items)[0]; + (*items)[0] = nullptr; + return item; + } + return nullptr; +} + +shared_ptr ResultContainer::removeItemNoUpdate(int slot) +{ + if ((*items)[0] != NULL) + { + shared_ptr item = (*items)[0]; + (*items)[0] = nullptr; + return item; + } + return nullptr; +} + +void ResultContainer::setItem(unsigned int slot, shared_ptr item) +{ + (*items)[0] = item; +} + +int ResultContainer::getMaxStackSize() +{ + return Container::LARGE_MAX_STACK_SIZE; +} + +void ResultContainer::setChanged() +{ +} + +bool ResultContainer::stillValid(shared_ptr player) +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/ResultContainer.h b/Minecraft.World/ResultContainer.h new file mode 100644 index 00000000..62df65d8 --- /dev/null +++ b/Minecraft.World/ResultContainer.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Container.h" + +class ResultContainer : public Container +{ +private: + ItemInstanceArray *items; + +public: + // 4J Stu Added a ctor to init items + ResultContainer(); + + virtual unsigned int getContainerSize(); + virtual shared_ptr getItem(unsigned int slot); + virtual int getName(); + virtual shared_ptr removeItem(unsigned int slot, int count); + virtual shared_ptr removeItemNoUpdate(int slot); + virtual void setItem(unsigned int slot, shared_ptr item); + virtual int getMaxStackSize(); + virtual void setChanged(); + virtual bool stillValid(shared_ptr player); + + void startOpen() { } // TODO Auto-generated method stub + void stopOpen() { } // TODO Auto-generated method stub +}; \ No newline at end of file diff --git a/Minecraft.World/ResultSlot.cpp b/Minecraft.World/ResultSlot.cpp new file mode 100644 index 00000000..b79329af --- /dev/null +++ b/Minecraft.World/ResultSlot.cpp @@ -0,0 +1,102 @@ +#include "stdafx.h" +#include "Container.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.h" +#include "ResultSlot.h" + +ResultSlot::ResultSlot(Player *player, shared_ptr craftSlots, shared_ptr container, int id, int x, int y) : Slot( container, id, x, y ) +{ + this->player = player; + this->craftSlots = craftSlots; + removeCount = 0; +} + +bool ResultSlot::mayPlace(shared_ptr item) +{ + return false; +} + +shared_ptr ResultSlot::remove(int c) +{ + if (hasItem()) + { + removeCount += min(c, getItem()->count); + } + return Slot::remove(c); +} + +void ResultSlot::onQuickCraft(shared_ptr picked, int count) +{ + removeCount += count; + checkTakeAchievements(picked); +} + +void ResultSlot::checkTakeAchievements(shared_ptr carried) +{ + carried->onCraftedBy(player->level, dynamic_pointer_cast( player->shared_from_this() ), removeCount); + removeCount = 0; + + if (carried->id == Tile::workBench_Id) player->awardStat(GenericStats::buildWorkbench(), GenericStats::param_buildWorkbench()); + else if (carried->id == Item::pickAxe_wood_Id) player->awardStat(GenericStats::buildPickaxe(), GenericStats::param_buildPickaxe()); + else if (carried->id == Tile::furnace_Id) player->awardStat(GenericStats::buildFurnace(), GenericStats::param_buildFurnace()); + else if (carried->id == Item::hoe_wood_Id) player->awardStat(GenericStats::buildHoe(), GenericStats::param_buildHoe()); + else if (carried->id == Item::bread_Id) player->awardStat(GenericStats::makeBread(), GenericStats::param_makeBread()); + else if (carried->id == Item::cake_Id) player->awardStat(GenericStats::bakeCake(), GenericStats::param_bakeCake()); + else if (carried->id == Item::pickAxe_stone_Id) player->awardStat(GenericStats::buildBetterPickaxe(), GenericStats::param_buildBetterPickaxe()); + else if (carried->id == Item::sword_wood_Id) player->awardStat(GenericStats::buildSword(), GenericStats::param_buildSword()); + //else if (carried->id == Tile::enchantTable_Id) player->awardStat(GenericStats::enchantments(), GenericStats::param_achievement(eAward_)); + else if (carried->id == Tile::bookshelf_Id) player->awardStat(GenericStats::bookcase(), GenericStats::param_bookcase()); + + // 4J : WESTY : Added new acheivements. + else if (carried->id == Tile::dispenser_Id) player->awardStat(GenericStats::dispenseWithThis(), GenericStats::param_dispenseWithThis()); +} + +void ResultSlot::onTake(shared_ptr player, shared_ptr carried) +{ + checkTakeAchievements(carried); + + for (unsigned int i = 0; i < craftSlots->getContainerSize(); i++) + { + shared_ptr item = craftSlots->getItem(i); + if (item != NULL) + { + craftSlots->removeItem(i, 1); + + if (item->getItem()->hasCraftingRemainingItem()) + { + + // (TheApathetic) + shared_ptr craftResult = shared_ptr(new ItemInstance(item->getItem()->getCraftingRemainingItem())); + + /* + * Try to place this in the player's inventory (See we.java + * for new method) + */ + if (item->getItem()->shouldMoveCraftingResultToInventory(item) && this->player->inventory->add(craftResult)) + { + continue; + } + + // If this slot is now empty, place it there (current + // behavior) + if (craftSlots->getItem(i) == NULL) + { + craftSlots->setItem(i, craftResult); + } + else + { + // Finally, if nothing else, just drop the item + this->player->drop(craftResult); + } + } + + } + } +} + +bool ResultSlot::mayCombine(shared_ptr second) +{ + return false; +} diff --git a/Minecraft.World/ResultSlot.h b/Minecraft.World/ResultSlot.h new file mode 100644 index 00000000..28e464f0 --- /dev/null +++ b/Minecraft.World/ResultSlot.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Slot.h" + +class ResultSlot : public Slot +{ +private: + shared_ptr craftSlots; + Player *player; // This can't be a shared_ptr, as we create a result slot in the inventorymenu in the Player ctor + int removeCount; + +public: + ResultSlot(Player *player, shared_ptr craftSlots, shared_ptr container, int id, int x, int y); + virtual ~ResultSlot() {} + + virtual bool mayPlace(shared_ptr item); + virtual shared_ptr remove(int c); + +protected: + virtual void onQuickCraft(shared_ptr picked, int count); + virtual void checkTakeAchievements(shared_ptr carried); + +public: + virtual void onTake(shared_ptr player, shared_ptr carried); + virtual bool mayCombine(shared_ptr item); // 4J Added +}; \ No newline at end of file diff --git a/Minecraft.World/RiverBiome.h b/Minecraft.World/RiverBiome.h new file mode 100644 index 00000000..45a6839e --- /dev/null +++ b/Minecraft.World/RiverBiome.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Biome.h" + +class RiverBiome : public Biome +{ +public: + RiverBiome(int id) : Biome(id) + { + friendlies.clear(); + friendlies_chicken.clear(); // 4J added since chicken now separated from main friendlies + friendlies_wolf.clear(); // 4J added since wolf now separated from main friendlies + } +}; \ No newline at end of file diff --git a/Minecraft.World/RiverInitLayer.cpp b/Minecraft.World/RiverInitLayer.cpp new file mode 100644 index 00000000..e9497896 --- /dev/null +++ b/Minecraft.World/RiverInitLayer.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" + +RiverInitLayer::RiverInitLayer(__int64 seed, shared_ptrparent) : Layer(seed) +{ + this->parent = parent; +} + +intArray RiverInitLayer::getArea(int xo, int yo, int w, int h) +{ + intArray b = parent->getArea(xo, yo, w, h); + + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + initRandom(x + xo, y + yo); + result[x + y * w] = b[x + y * w] > 0 ? nextRandom(2) + 2 : 0; + } + } + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/RiverInitLayer.h b/Minecraft.World/RiverInitLayer.h new file mode 100644 index 00000000..6deb50e9 --- /dev/null +++ b/Minecraft.World/RiverInitLayer.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Layer.h" + +class RiverInitLayer : public Layer +{ +public: + RiverInitLayer(__int64 seed, shared_ptrparent); + + intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/RiverLayer.cpp b/Minecraft.World/RiverLayer.cpp new file mode 100644 index 00000000..8f805bab --- /dev/null +++ b/Minecraft.World/RiverLayer.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.newbiome.layer.h" + +RiverLayer::RiverLayer(__int64 seedMixup, shared_ptrparent) : Layer(seedMixup) +{ + this->parent = parent; +} + +intArray RiverLayer::getArea(int xo, int yo, int w, int h) +{ + int px = xo - 1; + int py = yo - 1; + int pw = w + 2; + int ph = h + 2; + intArray p = parent->getArea(px, py, pw, ph); + + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + int l = p[(x + 0) + (y + 1) * pw]; + int r = p[(x + 2) + (y + 1) * pw]; + int u = p[(x + 1) + (y + 0) * pw]; + int d = p[(x + 1) + (y + 2) * pw]; + int c = p[(x + 1) + (y + 1) * pw]; + if (c == 0 || (l == 0 || r == 0 || u == 0 || d == 0) || c != l || c != u || c != r || c != d) + { + result[x + y * w] = Biome::river->id; + } + else + { + result[x + y * w] = -1; + } + } + } + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/RiverLayer.h b/Minecraft.World/RiverLayer.h new file mode 100644 index 00000000..657ac07c --- /dev/null +++ b/Minecraft.World/RiverLayer.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Layer.h" + +class RiverLayer : public Layer +{ +public: + RiverLayer(__int64 seedMixup, shared_ptrparent); + intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/RiverMixerLayer.cpp b/Minecraft.World/RiverMixerLayer.cpp new file mode 100644 index 00000000..62dfdd6c --- /dev/null +++ b/Minecraft.World/RiverMixerLayer.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.newbiome.layer.h" + +RiverMixerLayer::RiverMixerLayer(__int64 seed, shared_ptrbiomes, shared_ptrrivers) : Layer(seed) +{ + this->biomes = biomes; + this->rivers = rivers; +} + +void RiverMixerLayer::init(__int64 seed) +{ + biomes->init(seed); + rivers->init(seed); + Layer::init(seed); +} + +intArray RiverMixerLayer::getArea(int xo, int yo, int w, int h) +{ + intArray b = biomes->getArea(xo, yo, w, h); + intArray r = rivers->getArea(xo, yo, w, h); + + intArray result = IntCache::allocate(w * h); + for (int i = 0; i < w * h; i++) + { + if (b[i] == Biome::ocean->id) + { + result[i] = b[i]; + + } + else + { + if (r[i] >= 0) + { + if (b[i] == Biome::iceFlats->id) result[i] = Biome::frozenRiver->id; + else if (b[i] == Biome::mushroomIsland->id || b[i] == Biome::mushroomIslandShore->id) result[i] = Biome::mushroomIsland->id; // 4J - don't make mushroom island shores as we don't have any island left once we do this as our islands are small (this used to change to mushroomIslandShore) + else result[i] = r[i]; + } + else + { + result[i] = b[i]; + } + } + } + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/RiverMixerLayer.h b/Minecraft.World/RiverMixerLayer.h new file mode 100644 index 00000000..eec51610 --- /dev/null +++ b/Minecraft.World/RiverMixerLayer.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Layer.h" + +class RiverMixerLayer : public Layer +{ +private: + shared_ptrbiomes; + shared_ptrrivers; + +public: + RiverMixerLayer(__int64 seed, shared_ptrbiomes, shared_ptrrivers); + + virtual void init(__int64 seed); + virtual intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/Rotate.cpp b/Minecraft.World/Rotate.cpp new file mode 100644 index 00000000..f422343e --- /dev/null +++ b/Minecraft.World/Rotate.cpp @@ -0,0 +1,15 @@ +#include "stdafx.h" +#include "Rotate.h" + +Rotate::Rotate(Synth *synth, float angle) +{ + this->synth = synth; + + _sin = sin(angle); + _cos = cos(angle); +} + +double Rotate::getValue(double x, double y) +{ + return synth->getValue(x * _cos + y * _sin, y * _cos - x * _sin); +} \ No newline at end of file diff --git a/Minecraft.World/Rotate.h b/Minecraft.World/Rotate.h new file mode 100644 index 00000000..ba76f589 --- /dev/null +++ b/Minecraft.World/Rotate.h @@ -0,0 +1,15 @@ +#pragma once +#include "Synth.h" + +class Rotate : public Synth +{ +private: + Synth *synth; + double _sin; + double _cos; + +public: + Rotate(Synth *synth, float angle); + + virtual double getValue(double x, double y); +}; \ No newline at end of file diff --git a/Minecraft.World/RotateHeadPacket.cpp b/Minecraft.World/RotateHeadPacket.cpp new file mode 100644 index 00000000..a0cd02d7 --- /dev/null +++ b/Minecraft.World/RotateHeadPacket.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" + +#include "RotateHeadPacket.h" + +RotateHeadPacket::RotateHeadPacket() +{ +} + +RotateHeadPacket::RotateHeadPacket(int id, char yHeadRot) +{ + this->id = id; + this->yHeadRot = yHeadRot; +} + +void RotateHeadPacket::read(DataInputStream *dis) +{ + id = dis->readInt(); + yHeadRot = dis->readByte(); +} + +void RotateHeadPacket::write(DataOutputStream *dos) +{ + dos->writeInt(id); + dos->writeByte(yHeadRot); +} + +void RotateHeadPacket::handle(PacketListener *listener) +{ + listener->handleRotateMob(shared_from_this()); +} + +int RotateHeadPacket::getEstimatedSize() +{ + return 5; +} + +bool RotateHeadPacket::canBeInvalidated() +{ + return true; +} + +bool RotateHeadPacket::isInvalidatedBy(shared_ptr packet) +{ + shared_ptr target = dynamic_pointer_cast(packet); + return target->id == id; +} + +bool RotateHeadPacket::isAync() +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/RotateHeadPacket.h b/Minecraft.World/RotateHeadPacket.h new file mode 100644 index 00000000..19ccf97f --- /dev/null +++ b/Minecraft.World/RotateHeadPacket.h @@ -0,0 +1,27 @@ +#pragma once + +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "Packet.h" + +class RotateHeadPacket : public Packet, public enable_shared_from_this +{ +public: + int id; + char yHeadRot; + + RotateHeadPacket(); + RotateHeadPacket(int id, char yHeadRot); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + virtual bool isAync(); + +public: + static shared_ptr create() { return shared_ptr(new RotateHeadPacket()); } + virtual int getId() { return 35; } +}; \ No newline at end of file diff --git a/Minecraft.World/SaddleItem.cpp b/Minecraft.World/SaddleItem.cpp new file mode 100644 index 00000000..0effeb53 --- /dev/null +++ b/Minecraft.World/SaddleItem.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.h" +#include "SaddleItem.h" + +SaddleItem::SaddleItem(int id) : Item(id) +{ + maxStackSize = 1; +} + +bool SaddleItem::interactEnemy(shared_ptr itemInstance, shared_ptr mob) +{ + if ( dynamic_pointer_cast(mob) ) + { + shared_ptr pig = dynamic_pointer_cast(mob); + if (!pig->hasSaddle() && !pig->isBaby()) + { + pig->setSaddle(true); + itemInstance->count--; + } + return true; + } + return false; +} + +bool SaddleItem::hurtEnemy(shared_ptr itemInstance, shared_ptr mob, shared_ptr attacker) +{ + interactEnemy(itemInstance, mob); + return true; +} \ No newline at end of file diff --git a/Minecraft.World/SaddleItem.h b/Minecraft.World/SaddleItem.h new file mode 100644 index 00000000..129922db --- /dev/null +++ b/Minecraft.World/SaddleItem.h @@ -0,0 +1,13 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class SaddleItem : public Item +{ +public: + SaddleItem(int id); + + virtual bool interactEnemy(shared_ptr itemInstance, shared_ptr mob); + virtual bool hurtEnemy(shared_ptr itemInstance, shared_ptr mob, shared_ptr attacker); +}; \ No newline at end of file diff --git a/Minecraft.World/SandFeature.cpp b/Minecraft.World/SandFeature.cpp new file mode 100644 index 00000000..b678e290 --- /dev/null +++ b/Minecraft.World/SandFeature.cpp @@ -0,0 +1,58 @@ +#include "stdafx.h" +#include "SandFeature.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" + +SandFeature::SandFeature(int radius, int tile) +{ + this->tile = tile; + this->radius = radius; +} + +bool SandFeature::place(Level *level, Random *random, int x, int y, int z) +{ + if (level->getMaterial(x, y, z) != Material::water) return false; + + // 4J - optimisation. Without this, we can end up creating a huge number of HeavyTiles to be ticked + // a few frames away. I think instatick ought to be fine here - we're only turning rock into gravel, + // so should instantly know if we've made a rock with nothing underneath and that should fall. + level->setInstaTick(true); + + int r = random->nextInt(radius-2)+2; + int yr = 2; + + // 4J Stu Added to stop tree features generating areas previously place by game rule generation + if(app.getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + bool intersects = levelGenOptions->checkIntersects(x - r, y - yr, z - r, x + r, y + yr, z + r); + if(intersects) + { + level->setInstaTick(false); + //app.DebugPrintf("Skipping reeds feature generation as it overlaps a game rule structure\n"); + return false; + } + } + + for (int xx = x - r; xx <= x + r; xx++) + { + for (int zz = z - r; zz <= z + r; zz++) + { + int xd = xx - x; + int zd = zz - z; + if (xd * xd + zd * zd > r * r) continue; + for (int yy = y - yr; yy <= y + yr; yy++) + { + int t = level->getTile(xx, yy, zz); + if (t == Tile::dirt_Id || t == Tile::grass_Id) + { + level->setTileNoUpdate(xx, yy, zz, tile); + } + } + } + } + + level->setInstaTick(false); + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/SandFeature.h b/Minecraft.World/SandFeature.h new file mode 100644 index 00000000..582e40d1 --- /dev/null +++ b/Minecraft.World/SandFeature.h @@ -0,0 +1,14 @@ +#pragma once +#include "Feature.h" +class Random; + +class SandFeature : public Feature +{ +private: + int tile; + int radius; + +public: + SandFeature(int radius, int tile); + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/SandStoneTile.cpp b/Minecraft.World/SandStoneTile.cpp new file mode 100644 index 00000000..daf42db8 --- /dev/null +++ b/Minecraft.World/SandStoneTile.cpp @@ -0,0 +1,52 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.h" +#include "SandStoneTile.h" + +const wstring SandStoneTile::TEXTURE_TOP = L"sandstone_top"; +const wstring SandStoneTile::TEXTURE_BOTTOM = L"sandstone_bottom"; +const wstring SandStoneTile::TEXTURE_NAMES[] = {L"sandstone_side", L"sandstone_carved", L"sandstone_smooth"}; + +int SandStoneTile::SANDSTONE_NAMES[SANDSTONE_BLOCK_NAMES] = { + IDS_TILE_SANDSTONE, IDS_TILE_SANDSTONE_CHISELED, IDS_TILE_SANDSTONE_SMOOTH +}; + +SandStoneTile::SandStoneTile(int id) : Tile(id, Material::stone) +{ + icons = NULL; + iconTop = NULL; + iconBottom = NULL; +} + +Icon *SandStoneTile::getTexture(int face, int data) +{ + if (face == Facing::UP || (face == Facing::DOWN && (data == TYPE_HEIROGLYPHS || data == TYPE_SMOOTHSIDE))) + { + return iconTop; + } + if (face == Facing::DOWN) + { + return iconBottom; + } + if (data < 0 || data >= SANDSTONE_TILE_TEXTURE_COUNT) data = 0; + return icons[data]; +} + +int SandStoneTile::getSpawnResourcesAuxValue(int data) +{ + return data; +} + +void SandStoneTile::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[SANDSTONE_TILE_TEXTURE_COUNT]; + + for (int i = 0; i < SANDSTONE_TILE_TEXTURE_COUNT; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURE_NAMES[i]); + } + + iconTop = iconRegister->registerIcon(TEXTURE_TOP); + iconBottom = iconRegister->registerIcon(TEXTURE_BOTTOM); +} \ No newline at end of file diff --git a/Minecraft.World/SandStoneTile.h b/Minecraft.World/SandStoneTile.h new file mode 100644 index 00000000..ac6f9ff7 --- /dev/null +++ b/Minecraft.World/SandStoneTile.h @@ -0,0 +1,38 @@ +#pragma once +using namespace std; + +#include "Tile.h" + +class ChunkRebuildData; + +class SandStoneTile : public Tile +{ + friend class ChunkRebuildData; +public: + static const int TYPE_DEFAULT = 0; + static const int TYPE_HEIROGLYPHS = 1; + static const int TYPE_SMOOTHSIDE = 2; + + // Add this in when we need it + //static final String[] SANDSTONE_NAMES = {"default", "chiseled", "smooth"}; + + static const int SANDSTONE_BLOCK_NAMES = 3; + static int SANDSTONE_NAMES[SANDSTONE_BLOCK_NAMES]; + +private: + static const wstring TEXTURE_TOP; + static const wstring TEXTURE_BOTTOM; + static const wstring TEXTURE_NAMES[]; + static const int SANDSTONE_TILE_TEXTURE_COUNT = 3; + + Icon **icons; + Icon *iconTop; + Icon *iconBottom; +public: + SandStoneTile(int id); + +public: + Icon *getTexture(int face, int data); + virtual int getSpawnResourcesAuxValue(int data); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/Sapling.cpp b/Minecraft.World/Sapling.cpp new file mode 100644 index 00000000..42bd53ee --- /dev/null +++ b/Minecraft.World/Sapling.cpp @@ -0,0 +1,166 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.h" + +#include "Sapling.h" + +const unsigned int Sapling::SAPLING_NAMES[SAPLING_NAMES_SIZE] = { IDS_TILE_SAPLING_OAK, + IDS_TILE_SAPLING_SPRUCE, + IDS_TILE_SAPLING_BIRCH, + IDS_TILE_SAPLING_JUNGLE + }; + +const wstring Sapling::TEXTURE_NAMES[] = {L"sapling", L"sapling_spruce", L"sapling_birch", L"sapling_jungle"}; + +Sapling::Sapling(int id) : Bush( id ) +{ + this->updateDefaultShape(); + icons = NULL; +} + +// 4J Added override +void Sapling::updateDefaultShape() +{ + float ss = 0.4f; + this->setShape(0.5f - ss, 0, 0.5f - ss, 0.5f + ss, ss * 2, 0.5f + ss); +} + +void Sapling::tick(Level *level, int x, int y, int z, Random *random) +{ + if (level->isClientSide) return; + + Bush::tick(level, x, y, z, random); + + if (level->getRawBrightness(x, y + 1, z) >= Level::MAX_BRIGHTNESS - 6) + { + if (random->nextInt(7) == 0) + { + int data = level->getData(x, y, z); + if ((data & AGE_BIT) == 0) + { + level->setData(x, y, z, data | AGE_BIT); + } + else + { + growTree(level, x, y, z, random); + } + } + } +} + +Icon *Sapling::getTexture(int face, int data) +{ + data = data & TYPE_MASK; + return icons[data]; +} + +void Sapling::growTree(Level *level, int x, int y, int z, Random *random) +{ + int data = level->getData(x, y, z) & TYPE_MASK; + + Feature *f = NULL; + + int ox = 0, oz = 0; + bool multiblock = false; + + if (data == TYPE_EVERGREEN) + { + f = new SpruceFeature(true); + } + else if (data == TYPE_BIRCH) + { + f = new BirchFeature(true); + } + else if (data == TYPE_JUNGLE) + { + // check for mega tree + for (ox = 0; ox >= -1; ox--) + { + for (oz = 0; oz >= -1; oz--) + { + if (isSapling(level, x + ox, y, z + oz, TYPE_JUNGLE) && + isSapling(level, x + ox + 1, y, z + oz, TYPE_JUNGLE) && + isSapling(level, x + ox, y, z + oz + 1, TYPE_JUNGLE) && + isSapling(level, x + ox + 1, y, z + oz + 1, TYPE_JUNGLE)) + { + f = new MegaTreeFeature(true, 10 + random->nextInt(20), TreeTile::JUNGLE_TRUNK, LeafTile::JUNGLE_LEAF); + multiblock = true; + break; + } + } + if (f != NULL) + { + break; + } + } + if (f == NULL) + { + ox = oz = 0; + f = new TreeFeature(true, 4 + random->nextInt(7), TreeTile::JUNGLE_TRUNK, LeafTile::JUNGLE_LEAF, false); + } + } + else + { + f = new TreeFeature(true); + if (random->nextInt(10) == 0) + { + delete f; + f = new BasicTree(true); + } + } + if (multiblock) + { + level->setTileNoUpdate(x + ox, y, z + oz, 0); + level->setTileNoUpdate(x + ox + 1, y, z + oz, 0); + level->setTileNoUpdate(x + ox, y, z + oz + 1, 0); + level->setTileNoUpdate(x + ox + 1, y, z + oz + 1, 0); + } + else + { + level->setTileNoUpdate(x, y, z, 0); + } + if (!f->place(level, random, x + ox, y, z + oz)) + { + if (multiblock) + { + level->setTileAndDataNoUpdate(x + ox, y, z + oz, this->id, data); + level->setTileAndDataNoUpdate(x + ox + 1, y, z + oz, this->id, data); + level->setTileAndDataNoUpdate(x + ox, y, z + oz + 1, this->id, data); + level->setTileAndDataNoUpdate(x + ox + 1, y, z + oz + 1, this->id, data); + } + else + { + level->setTileAndDataNoUpdate(x, y, z, this->id, data); + } + } + if( f != NULL ) + delete f; +} + +unsigned int Sapling::getDescriptionId(int iData /*= -1*/) +{ + if(iData < 0 ) iData = 0; + return Sapling::SAPLING_NAMES[iData]; +} + +int Sapling::getSpawnResourcesAuxValue(int data) +{ + return data & TYPE_MASK; +} + +bool Sapling::isSapling(Level *level, int x, int y, int z, int type) +{ + return (level->getTile(x, y, z) == id) && ((level->getData(x, y, z) & TYPE_MASK) == type); +} + +void Sapling::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[SAPLING_NAMES_SIZE]; + + for (int i = 0; i < SAPLING_NAMES_SIZE; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURE_NAMES[i]); + } +} diff --git a/Minecraft.World/Sapling.h b/Minecraft.World/Sapling.h new file mode 100644 index 00000000..f44888af --- /dev/null +++ b/Minecraft.World/Sapling.h @@ -0,0 +1,50 @@ +#pragma once + +#include "LeafTile.h" +#include "Bush.h" + +class Random; +class ChunkRebuildData; + +class Sapling : public Bush +{ + friend class Tile; + friend class ChunkRebuildData; +public: + static const int TYPE_DEFAULT = LeafTile::NORMAL_LEAF; + static const int TYPE_EVERGREEN = LeafTile::EVERGREEN_LEAF; + static const int TYPE_BIRCH = LeafTile::BIRCH_LEAF; + static const int TYPE_JUNGLE = LeafTile::JUNGLE_LEAF; + + static const int SAPLING_NAMES_SIZE = 4; + + static const unsigned int SAPLING_NAMES[SAPLING_NAMES_SIZE]; + +private: + static const wstring TEXTURE_NAMES[]; + + Icon **icons; + + static const int TYPE_MASK = 3; + static const int AGE_BIT = 8; + +protected: + Sapling(int id); + +public: + virtual void updateDefaultShape(); // 4J Added override + void tick(Level *level, int x, int y, int z, Random *random); + + Icon *getTexture(int face, int data); + + void growTree(Level *level, int x, int y, int z, Random *random); + + virtual unsigned int getDescriptionId(int iData = -1); + bool isSapling(Level *level, int x, int y, int z, int type); + +protected: + int getSpawnResourcesAuxValue(int data); + +public: + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/SaplingTileItem.cpp b/Minecraft.World/SaplingTileItem.cpp new file mode 100644 index 00000000..c88275f3 --- /dev/null +++ b/Minecraft.World/SaplingTileItem.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "TileItem.h" +#include "net.minecraft.world.level.tile.h" +#include "SaplingTileItem.h" + +SaplingTileItem::SaplingTileItem(int id) : TileItem(id) +{ + setMaxDamage(0); + setStackedByData(true); +} + +int SaplingTileItem::getLevelDataForAuxValue(int auxValue) +{ + return auxValue; +} + +Icon *SaplingTileItem::getIcon(int itemAuxValue) +{ + return Tile::sapling->getTexture(0, itemAuxValue); +} + +// 4J brought forward to have unique names for different sapling types +unsigned int SaplingTileItem::getDescriptionId(shared_ptr instance) +{ + int auxValue = instance->getAuxValue(); + if (auxValue < 0 || auxValue >= Sapling::SAPLING_NAMES_SIZE) + { + auxValue = 0; + } + return Sapling::SAPLING_NAMES[auxValue]; +} \ No newline at end of file diff --git a/Minecraft.World/SaplingTileItem.h b/Minecraft.World/SaplingTileItem.h new file mode 100644 index 00000000..f0b03cc4 --- /dev/null +++ b/Minecraft.World/SaplingTileItem.h @@ -0,0 +1,16 @@ +#pragma once +using namespace std; + +#include "TileItem.h" + +class SaplingTileItem : public TileItem +{ +public: + SaplingTileItem(int id); + + virtual int getLevelDataForAuxValue(int auxValue); + virtual Icon *getIcon(int itemAuxValue); + + // 4J brought forward to have unique names for different sapling types + virtual unsigned int getDescriptionId(shared_ptr instance); +}; \ No newline at end of file diff --git a/Minecraft.World/SavedData.cpp b/Minecraft.World/SavedData.cpp new file mode 100644 index 00000000..4a2e87e9 --- /dev/null +++ b/Minecraft.World/SavedData.cpp @@ -0,0 +1,24 @@ +#include "stdafx.h" +#include "SavedData.h" + + + +SavedData::SavedData(const wstring& id) : id( id ) +{ + dirty = false; +} + +void SavedData::setDirty() +{ + setDirty(true); +} + +void SavedData::setDirty(bool dirty) +{ + this->dirty = dirty; +} + +bool SavedData::isDirty() +{ + return dirty; +} \ No newline at end of file diff --git a/Minecraft.World/SavedData.h b/Minecraft.World/SavedData.h new file mode 100644 index 00000000..2fe97963 --- /dev/null +++ b/Minecraft.World/SavedData.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "Class.h" + +class CompoundTag; + +class SavedData : public enable_shared_from_this +{ +public: + const wstring id; + +private: + bool dirty; + +public: + SavedData(const wstring& id); + + virtual void load(CompoundTag *tag) = 0; + virtual void save(CompoundTag *tag) = 0; + + void setDirty(); + void setDirty(bool dirty); + bool isDirty(); +}; \ No newline at end of file diff --git a/Minecraft.World/SavedDataStorage.cpp b/Minecraft.World/SavedDataStorage.cpp new file mode 100644 index 00000000..cacfa688 --- /dev/null +++ b/Minecraft.World/SavedDataStorage.cpp @@ -0,0 +1,211 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.saveddata.h" +#include "net.minecraft.world.level.storage.h" +#include "net.minecraft.world.entity.ai.village.h" +#include "com.mojang.nbt.h" +#include "File.h" +#include "SavedDataStorage.h" + +#include "ConsoleSaveFileIO.h" + +SavedDataStorage::SavedDataStorage(LevelStorage *levelStorage) +{ + /* + cache = new unordered_map >; + savedDatas = new vector >; + usedAuxIds = new unordered_map; + */ + + this->levelStorage = levelStorage; + loadAuxValues(); +} + +shared_ptr SavedDataStorage::get(const type_info& clazz, const wstring& id) +{ + AUTO_VAR(it, cache.find( id )); + if (it != cache.end()) return (*it).second; + + shared_ptr data = nullptr; + if (levelStorage != NULL) + { + //File file = levelStorage->getDataFile(id); + ConsoleSavePath file = levelStorage->getDataFile(id); + if (!file.getName().empty() && levelStorage->getSaveFile()->doesFileExist( file ) ) + { + // mob = dynamic_pointer_cast(Mob::_class->newInstance( level )); + //data = clazz.getConstructor(String.class).newInstance(id); + + if( clazz == typeid(MapItemSavedData) ) + { + data = dynamic_pointer_cast( shared_ptr(new MapItemSavedData(id)) ); + } + else if( clazz == typeid(Villages) ) + { + data = dynamic_pointer_cast( shared_ptr(new Villages(id) ) ); + } + else + { + // Handling of new SavedData class required + __debugbreak(); + } + + ConsoleSaveFileInputStream fis = ConsoleSaveFileInputStream(levelStorage->getSaveFile(), file); + CompoundTag *root = NbtIo::readCompressed(&fis); + fis.close(); + + data->load(root->getCompound(L"data")); + } + } + + if (data != NULL) + { + cache.insert( unordered_map >::value_type( id , data ) ); + savedDatas.push_back(data); + } + return data; +} + +void SavedDataStorage::set(const wstring& id, shared_ptr data) +{ + if (data == NULL) + { + // TODO 4J Stu - throw new RuntimeException("Can't set null data"); + assert( false ); + } + AUTO_VAR(it, cache.find(id)); + if ( it != cache.end() ) + { + AUTO_VAR(it2, find( savedDatas.begin(), savedDatas.end(), it->second )); + if( it2 != savedDatas.end() ) + { + savedDatas.erase( it2 ); + } + cache.erase( it ); + } + cache.insert( cacheMapType::value_type(id, data) ); + savedDatas.push_back(data); +} + +void SavedDataStorage::save() +{ + AUTO_VAR(itEnd, savedDatas.end()); + for (AUTO_VAR(it, savedDatas.begin()); it != itEnd; it++) + { + shared_ptr data = *it; //savedDatas->at(i); + if (data->isDirty()) + { + save(data); + data->setDirty(false); + } + } +} + +void SavedDataStorage::save(shared_ptr data) +{ + if (levelStorage == NULL) return; + //File file = levelStorage->getDataFile(data->id); + ConsoleSavePath file = levelStorage->getDataFile(data->id); + if (!file.getName().empty()) + { + CompoundTag *dataTag = new CompoundTag(); + data->save(dataTag); + + CompoundTag *tag = new CompoundTag(); + tag->putCompound(L"data", dataTag); + + ConsoleSaveFileOutputStream fos = ConsoleSaveFileOutputStream(levelStorage->getSaveFile(), file); + NbtIo::writeCompressed(tag, &fos); + fos.close(); + + delete tag; + } +} + +void SavedDataStorage::loadAuxValues() +{ + usedAuxIds.clear(); + + if (levelStorage == NULL) return; + //File file = levelStorage->getDataFile(L"idcounts"); + ConsoleSavePath file = levelStorage->getDataFile(L"idcounts"); + if (!file.getName().empty() && levelStorage->getSaveFile()->doesFileExist( file ) ) + { + ConsoleSaveFileInputStream fis = ConsoleSaveFileInputStream(levelStorage->getSaveFile(), file); + DataInputStream dis = DataInputStream(&fis); + CompoundTag *tags = NbtIo::read(&dis); + dis.close(); + + Tag *tag; + vector *allTags = tags->getAllTags(); + AUTO_VAR(itEnd, allTags->end()); + for (AUTO_VAR(it, allTags->begin()); it != itEnd; it++) + { + tag = *it; //tags->getAllTags()->at(i); + + if (dynamic_cast(tag) != NULL) + { + ShortTag *sTag = (ShortTag *) tag; + wstring id = sTag->getName(); + short val = sTag->data; + usedAuxIds.insert( uaiMapType::value_type( id, val ) ); + } + } + delete allTags; + } +} + +int SavedDataStorage::getFreeAuxValueFor(const wstring& id) +{ + AUTO_VAR(it, usedAuxIds.find( id )); + short val = 0; + if ( it != usedAuxIds.end() ) + { + val = (*it).second; + val++; + } + + usedAuxIds[id] = val; + if (levelStorage == NULL) return val; + //File file = levelStorage->getDataFile(L"idcounts"); + ConsoleSavePath file = levelStorage->getDataFile(L"idcounts"); + if (!file.getName().empty()) + { + CompoundTag *tag = new CompoundTag(); + + // TODO 4J Stu - This was iterating over the keySet in Java, so potentially we are looking at more items? + AUTO_VAR(itEndAuxIds, usedAuxIds.end()); + for(uaiMapType::iterator it2 = usedAuxIds.begin(); it2 != itEndAuxIds; it2++) + { + short value = it2->second; + tag->putShort( (wchar_t *) it2->first.c_str(), value); + } + + ConsoleSaveFileOutputStream fos = ConsoleSaveFileOutputStream(levelStorage->getSaveFile(), file); + DataOutputStream dos = DataOutputStream(&fos); + NbtIo::write(tag, &dos); + dos.close(); + } + return val; +} + +// 4J Added +int SavedDataStorage::getAuxValueForMap(PlayerUID xuid, int dimension, int centreXC, int centreZC, int scale) +{ + if( levelStorage == NULL ) + { + switch(dimension) + { + case -1: + return MAP_NETHER_DEFAULT_INDEX; + case 1: + return MAP_END_DEFAULT_INDEX; + case 0: + default: + return MAP_OVERWORLD_DEFAULT_INDEX; + } + } + else + { + return levelStorage->getAuxValueForMap(xuid, dimension, centreXC, centreZC, scale); + } +} diff --git a/Minecraft.World/SavedDataStorage.h b/Minecraft.World/SavedDataStorage.h new file mode 100644 index 00000000..9a342ab6 --- /dev/null +++ b/Minecraft.World/SavedDataStorage.h @@ -0,0 +1,35 @@ +#pragma once +using namespace std; + +class ConsoleSaveFile; +#include "SavedData.h" + +class SavedDataStorage +{ +private: + LevelStorage *levelStorage; + + typedef unordered_map > cacheMapType; + cacheMapType cache; + + vector > savedDatas; + + typedef unordered_map uaiMapType; + uaiMapType usedAuxIds; + +public: + SavedDataStorage(LevelStorage *); + shared_ptr get(const type_info& clazz, const wstring& id); + void set(const wstring& id, shared_ptr data); + void save(); + +private: + void save(shared_ptr data); + void loadAuxValues(); + +public: + int getFreeAuxValueFor(const wstring& id); + + // 4J Added + int getAuxValueForMap(PlayerUID xuid, int dimension, int centreXC, int centreZC, int scale); +}; diff --git a/Minecraft.World/Scale.cpp b/Minecraft.World/Scale.cpp new file mode 100644 index 00000000..f73c2634 --- /dev/null +++ b/Minecraft.World/Scale.cpp @@ -0,0 +1,14 @@ +#include "stdafx.h" +#include "Scale.h" + +Scale::Scale(Synth *synth, double xScale, double yScale) +{ + this->synth = synth; + this->xScale = 1.0 / xScale; + this->yScale = 1.0 / yScale; +} + +double Scale::getValue(double x, double y) +{ + return synth->getValue(x * xScale, y * yScale); +} diff --git a/Minecraft.World/Scale.h b/Minecraft.World/Scale.h new file mode 100644 index 00000000..0354674d --- /dev/null +++ b/Minecraft.World/Scale.h @@ -0,0 +1,15 @@ +#pragma once +#include "Synth.h" + +class Scale : public Synth +{ +private: + Synth *synth; + double xScale; + double yScale; + +public: + Scale(Synth *synth, double xScale, double yScale); + + virtual double getValue(double x, double y); +}; \ No newline at end of file diff --git a/Minecraft.World/ScatteredFeaturePieces.cpp b/Minecraft.World/ScatteredFeaturePieces.cpp new file mode 100644 index 00000000..07c2e7e6 --- /dev/null +++ b/Minecraft.World/ScatteredFeaturePieces.cpp @@ -0,0 +1,537 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "WeighedTreasure.h" +#include "ScatteredFeaturePieces.h" + +ScatteredFeaturePieces::ScatteredFeaturePiece::ScatteredFeaturePiece(Random *random, int west, int floor, int north, int width, int height, int depth) : StructurePiece(0) +{ + heightPosition = -1; + this->width = width; + this->height = height; + this->depth = depth; + + orientation = random->nextInt(4); + + switch (orientation) + { + case Direction::NORTH: + case Direction::SOUTH: + boundingBox = new BoundingBox(west, floor, north, west + width - 1, floor + height - 1, north + depth - 1); + break; + default: + boundingBox = new BoundingBox(west, floor, north, west + depth - 1, floor + height - 1, north + width - 1); + break; + } +} + +bool ScatteredFeaturePieces::ScatteredFeaturePiece::updateAverageGroundHeight(Level *level, BoundingBox *chunkBB, int offset) +{ + if (heightPosition >= 0) + { + return true; + } + + int total = 0; + int count = 0; + for (int z = boundingBox->z0; z <= boundingBox->z1; z++) + { + for (int x = boundingBox->x0; x <= boundingBox->x1; x++) + { + if (chunkBB->isInside(x, 64, z)) + { + total += max(level->getTopSolidBlock(x, z), level->dimension->getSpawnYPosition()); + count++; + } + } + } + + if (count == 0) + { + return false; + } + heightPosition = total / count; + boundingBox->move(0, heightPosition - boundingBox->y0 + offset, 0); + return true; +} + +WeighedTreasure *ScatteredFeaturePieces::DesertPyramidPiece::treasureItems[ScatteredFeaturePieces::DesertPyramidPiece::TREASURE_ITEMS_COUNT] = +{ + new WeighedTreasure(Item::diamond_Id, 0, 1, 3, 3), + new WeighedTreasure(Item::ironIngot_Id, 0, 1, 5, 10), + new WeighedTreasure(Item::goldIngot_Id, 0, 2, 7, 15), + new WeighedTreasure(Item::emerald_Id, 0, 1, 3, 2), + new WeighedTreasure(Item::bone_Id, 0, 4, 6, 20), + new WeighedTreasure(Item::rotten_flesh_Id, 0, 3, 7, 16), +}; + +ScatteredFeaturePieces::DesertPyramidPiece::DesertPyramidPiece(Random *random, int west, int north) : ScatteredFeaturePiece(random, west, 64, north, 21, 15, 21) +{ + hasPlacedChest[0] = false; + hasPlacedChest[1] = false; + hasPlacedChest[2] = false; + hasPlacedChest[3] = false; +} + +bool ScatteredFeaturePieces::DesertPyramidPiece::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // pyramid + generateBox(level, chunkBB, 0, -4, 0, width - 1, 0, depth - 1, Tile::sandStone_Id, Tile::sandStone_Id, false); + for (int pos = 1; pos <= 9; pos++) + { + generateBox(level, chunkBB, pos, pos, pos, width - 1 - pos, pos, depth - 1 - pos, Tile::sandStone_Id, Tile::sandStone_Id, false); + generateBox(level, chunkBB, pos + 1, pos, pos + 1, width - 2 - pos, pos, depth - 2 - pos, 0, 0, false); + } + for (int x = 0; x < width; x++) + { + for (int z = 0; z < depth; z++) + { + fillColumnDown(level, Tile::sandStone_Id, 0, x, -5, z, chunkBB); + } + } + + int stairsNorth = getOrientationData(Tile::stairs_sandstone_Id, 3); + int stairsSouth = getOrientationData(Tile::stairs_sandstone_Id, 2); + int stairsEast = getOrientationData(Tile::stairs_sandstone_Id, 0); + int stairsWest = getOrientationData(Tile::stairs_sandstone_Id, 1); + int baseDecoColor = ~DyePowderItem::ORANGE & 0xf; + int blue = ~DyePowderItem::BLUE & 0xf; + + // towers + generateBox(level, chunkBB, 0, 0, 0, 4, 9, 4, Tile::sandStone_Id, 0, false); + generateBox(level, chunkBB, 1, 10, 1, 3, 10, 3, Tile::sandStone_Id, Tile::sandStone_Id, false); + placeBlock(level, Tile::stairs_sandstone_Id, stairsNorth, 2, 10, 0, chunkBB); + placeBlock(level, Tile::stairs_sandstone_Id, stairsSouth, 2, 10, 4, chunkBB); + placeBlock(level, Tile::stairs_sandstone_Id, stairsEast, 0, 10, 2, chunkBB); + placeBlock(level, Tile::stairs_sandstone_Id, stairsWest, 4, 10, 2, chunkBB); + generateBox(level, chunkBB, width - 5, 0, 0, width - 1, 9, 4, Tile::sandStone_Id, 0, false); + generateBox(level, chunkBB, width - 4, 10, 1, width - 2, 10, 3, Tile::sandStone_Id, Tile::sandStone_Id, false); + placeBlock(level, Tile::stairs_sandstone_Id, stairsNorth, width - 3, 10, 0, chunkBB); + placeBlock(level, Tile::stairs_sandstone_Id, stairsSouth, width - 3, 10, 4, chunkBB); + placeBlock(level, Tile::stairs_sandstone_Id, stairsEast, width - 5, 10, 2, chunkBB); + placeBlock(level, Tile::stairs_sandstone_Id, stairsWest, width - 1, 10, 2, chunkBB); + + // entrance + generateBox(level, chunkBB, 8, 0, 0, 12, 4, 4, Tile::sandStone_Id, 0, false); + generateBox(level, chunkBB, 9, 1, 0, 11, 3, 4, 0, 0, false); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, 9, 1, 1, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, 9, 2, 1, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, 9, 3, 1, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, 10, 3, 1, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, 11, 3, 1, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, 11, 2, 1, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, 11, 1, 1, chunkBB); + + // tower pathways + generateBox(level, chunkBB, 4, 1, 1, 8, 3, 3, Tile::sandStone_Id, 0, false); + generateBox(level, chunkBB, 4, 1, 2, 8, 2, 2, 0, 0, false); + generateBox(level, chunkBB, 12, 1, 1, 16, 3, 3, Tile::sandStone_Id, 0, false); + generateBox(level, chunkBB, 12, 1, 2, 16, 2, 2, 0, 0, false); + + // hall floor and pillars + generateBox(level, chunkBB, 5, 4, 5, width - 6, 4, depth - 6, Tile::sandStone_Id, Tile::sandStone_Id, false); + generateBox(level, chunkBB, 9, 4, 9, 11, 4, 11, 0, 0, false); + generateBox(level, chunkBB, 8, 1, 8, 8, 3, 8, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, false); + generateBox(level, chunkBB, 12, 1, 8, 12, 3, 8, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, false); + generateBox(level, chunkBB, 8, 1, 12, 8, 3, 12, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, false); + generateBox(level, chunkBB, 12, 1, 12, 12, 3, 12, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, false); + + // catwalks + generateBox(level, chunkBB, 1, 1, 5, 4, 4, 11, Tile::sandStone_Id, Tile::sandStone_Id, false); + generateBox(level, chunkBB, width - 5, 1, 5, width - 2, 4, 11, Tile::sandStone_Id, Tile::sandStone_Id, false); + generateBox(level, chunkBB, 6, 7, 9, 6, 7, 11, Tile::sandStone_Id, Tile::sandStone_Id, false); + generateBox(level, chunkBB, width - 7, 7, 9, width - 7, 7, 11, Tile::sandStone_Id, Tile::sandStone_Id, false); + generateBox(level, chunkBB, 5, 5, 9, 5, 7, 11, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, false); + generateBox(level, chunkBB, width - 6, 5, 9, width - 6, 7, 11, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, false); + placeBlock(level, 0, 0, 5, 5, 10, chunkBB); + placeBlock(level, 0, 0, 5, 6, 10, chunkBB); + placeBlock(level, 0, 0, 6, 6, 10, chunkBB); + placeBlock(level, 0, 0, width - 6, 5, 10, chunkBB); + placeBlock(level, 0, 0, width - 6, 6, 10, chunkBB); + placeBlock(level, 0, 0, width - 7, 6, 10, chunkBB); + + // tower stairs + generateBox(level, chunkBB, 2, 4, 4, 2, 6, 4, 0, 0, false); + generateBox(level, chunkBB, width - 3, 4, 4, width - 3, 6, 4, 0, 0, false); + placeBlock(level, Tile::stairs_sandstone_Id, stairsNorth, 2, 4, 5, chunkBB); + placeBlock(level, Tile::stairs_sandstone_Id, stairsNorth, 2, 3, 4, chunkBB); + placeBlock(level, Tile::stairs_sandstone_Id, stairsNorth, width - 3, 4, 5, chunkBB); + placeBlock(level, Tile::stairs_sandstone_Id, stairsNorth, width - 3, 3, 4, chunkBB); + generateBox(level, chunkBB, 1, 1, 3, 2, 2, 3, Tile::sandStone_Id, Tile::sandStone_Id, false); + generateBox(level, chunkBB, width - 3, 1, 3, width - 2, 2, 3, Tile::sandStone_Id, Tile::sandStone_Id, false); + placeBlock(level, Tile::stairs_sandstone_Id, 0, 1, 1, 2, chunkBB); + placeBlock(level, Tile::stairs_sandstone_Id, 0, width - 2, 1, 2, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::SAND_SLAB, 1, 2, 2, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::SAND_SLAB, width - 2, 2, 2, chunkBB); + placeBlock(level, Tile::stairs_sandstone_Id, stairsWest, 2, 1, 2, chunkBB); + placeBlock(level, Tile::stairs_sandstone_Id, stairsEast, width - 3, 1, 2, chunkBB); + + // indoor decoration + generateBox(level, chunkBB, 4, 3, 5, 4, 3, 18, Tile::sandStone_Id, Tile::sandStone_Id, false); + generateBox(level, chunkBB, width - 5, 3, 5, width - 5, 3, 17, Tile::sandStone_Id, Tile::sandStone_Id, false); + generateBox(level, chunkBB, 3, 1, 5, 4, 2, 16, 0, 0, false); + generateBox(level, chunkBB, width - 6, 1, 5, width - 5, 2, 16, 0, 0, false); + for (int z = 5; z <= 17; z += 2) + { + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, 4, 1, z, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, 4, 2, z, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, width - 5, 1, z, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, width - 5, 2, z, chunkBB); + } + placeBlock(level, Tile::cloth_Id, baseDecoColor, 10, 0, 7, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 10, 0, 8, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 9, 0, 9, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 11, 0, 9, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 8, 0, 10, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 12, 0, 10, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 7, 0, 10, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 13, 0, 10, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 9, 0, 11, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 11, 0, 11, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 10, 0, 12, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 10, 0, 13, chunkBB); + placeBlock(level, Tile::cloth_Id, blue, 10, 0, 10, chunkBB); + + // outdoor decoration + for (int x = 0; x <= width - 1; x += width - 1) + { + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x, 2, 1, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 2, 2, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x, 2, 3, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x, 3, 1, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 3, 2, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x, 3, 3, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 4, 1, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, x, 4, 2, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 4, 3, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x, 5, 1, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 5, 2, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x, 5, 3, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 6, 1, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, x, 6, 2, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 6, 3, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 7, 1, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 7, 2, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 7, 3, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x, 8, 1, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x, 8, 2, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x, 8, 3, chunkBB); + } + for (int x = 2; x <= width - 3; x += width - 3 - 2) + { + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x - 1, 2, 0, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 2, 0, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x + 1, 2, 0, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x - 1, 3, 0, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 3, 0, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x + 1, 3, 0, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x - 1, 4, 0, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, x, 4, 0, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x + 1, 4, 0, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x - 1, 5, 0, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 5, 0, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x + 1, 5, 0, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x - 1, 6, 0, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, x, 6, 0, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x + 1, 6, 00, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x - 1, 7, 0, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x, 7, 0, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, x + 1, 7, 0, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x - 1, 8, 0, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x, 8, 0, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, x + 1, 8, 0, chunkBB); + } + generateBox(level, chunkBB, 8, 4, 0, 12, 6, 0, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, false); + placeBlock(level, 0, 0, 8, 6, 0, chunkBB); + placeBlock(level, 0, 0, 12, 6, 0, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 9, 5, 0, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, 10, 5, 0, chunkBB); + placeBlock(level, Tile::cloth_Id, baseDecoColor, 11, 5, 0, chunkBB); + + // tombs + generateBox(level, chunkBB, 8, -14, 8, 12, -11, 12, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, false); + generateBox(level, chunkBB, 8, -10, 8, 12, -10, 12, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, false); + generateBox(level, chunkBB, 8, -9, 8, 12, -9, 12, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, false); + generateBox(level, chunkBB, 8, -8, 8, 12, -1, 12, Tile::sandStone_Id, Tile::sandStone_Id, false); + generateBox(level, chunkBB, 9, -11, 9, 11, -1, 11, 0, 0, false); + placeBlock(level, Tile::pressurePlate_stone_Id, 0, 10, -11, 10, chunkBB); + generateBox(level, chunkBB, 9, -13, 9, 11, -13, 11, Tile::tnt_Id, 0, false); + placeBlock(level, 0, 0, 8, -11, 10, chunkBB); + placeBlock(level, 0, 0, 8, -10, 10, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, 7, -10, 10, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, 7, -11, 10, chunkBB); + placeBlock(level, 0, 0, 12, -11, 10, chunkBB); + placeBlock(level, 0, 0, 12, -10, 10, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, 13, -10, 10, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, 13, -11, 10, chunkBB); + placeBlock(level, 0, 0, 10, -11, 8, chunkBB); + placeBlock(level, 0, 0, 10, -10, 8, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, 10, -10, 7, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, 10, -11, 7, chunkBB); + placeBlock(level, 0, 0, 10, -11, 12, chunkBB); + placeBlock(level, 0, 0, 10, -10, 12, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_HEIROGLYPHS, 10, -10, 13, chunkBB); + placeBlock(level, Tile::sandStone_Id, SandStoneTile::TYPE_SMOOTHSIDE, 10, -11, 13, chunkBB); + + // chests! + for (int i = 0; i < 4; i++) + { + if (!hasPlacedChest[i]) + { + int xo = Direction::STEP_X[i] * 2; + int zo = Direction::STEP_Z[i] * 2; + hasPlacedChest[i] = createChest(level, chunkBB, random, 10 + xo, -11, 10 + zo, WeighedTreasure::addToTreasure(WeighedTreasureArray(treasureItems,TREASURE_ITEMS_COUNT), Item::enchantedBook->createForRandomTreasure(random)), 2 + random->nextInt(5)); + } + } + + return true; +} + +WeighedTreasure *ScatteredFeaturePieces::JunglePyramidPiece::treasureItems[ScatteredFeaturePieces::JunglePyramidPiece::TREASURE_ITEMS_COUNT] = +{ + new WeighedTreasure(Item::diamond_Id, 0, 1, 3, 3), + new WeighedTreasure(Item::ironIngot_Id, 0, 1, 5, 10), + new WeighedTreasure(Item::goldIngot_Id, 0, 2, 7, 15), + new WeighedTreasure(Item::emerald_Id, 0, 1, 3, 2), + new WeighedTreasure(Item::bone_Id, 0, 4, 6, 20), + new WeighedTreasure(Item::rotten_flesh_Id, 0, 3, 7, 16), +}; + + +WeighedTreasure *ScatteredFeaturePieces::JunglePyramidPiece::dispenserItems[ScatteredFeaturePieces::JunglePyramidPiece::DISPENSER_ITEMS_COUNT] = +{ + new WeighedTreasure(Item::arrow_Id, 0, 2, 7, 30), + // new WeighedTreasure(Item.fireball.id, 0, 1, 1, 10), +}; + +ScatteredFeaturePieces::JunglePyramidPiece::JunglePyramidPiece(Random *random, int west, int north) : ScatteredFeaturePiece(random, west, 64, north, 12, 10, 15) +{ + placedMainChest = false; + placedHiddenChest = false; + placedTrap1 = false; + placedTrap2 = false; +} + +bool ScatteredFeaturePieces::JunglePyramidPiece::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (!updateAverageGroundHeight(level, chunkBB, 0)) + { + return false; + } + + int stairsNorth = getOrientationData(Tile::stairs_stone_Id, 3); + int stairsSouth = getOrientationData(Tile::stairs_stone_Id, 2); + int stairsEast = getOrientationData(Tile::stairs_stone_Id, 0); + int stairsWest = getOrientationData(Tile::stairs_stone_Id, 1); + + // floor + generateBox(level, chunkBB, 0, -4, 0, width - 1, 0, depth - 1, false, random, &stoneSelector); + + // first floor walls + generateBox(level, chunkBB, 2, 1, 2, 9, 2, 2, false, random, &stoneSelector); + generateBox(level, chunkBB, 2, 1, 12, 9, 2, 12, false, random, &stoneSelector); + generateBox(level, chunkBB, 2, 1, 3, 2, 2, 11, false, random, &stoneSelector); + generateBox(level, chunkBB, 9, 1, 3, 9, 2, 11, false, random, &stoneSelector); + + // second floor walls + generateBox(level, chunkBB, 1, 3, 1, 10, 6, 1, false, random, &stoneSelector); + generateBox(level, chunkBB, 1, 3, 13, 10, 6, 13, false, random, &stoneSelector); + generateBox(level, chunkBB, 1, 3, 2, 1, 6, 12, false, random, &stoneSelector); + generateBox(level, chunkBB, 10, 3, 2, 10, 6, 12, false, random, &stoneSelector); + + // roof levels + generateBox(level, chunkBB, 2, 3, 2, 9, 3, 12, false, random, &stoneSelector); + generateBox(level, chunkBB, 2, 6, 2, 9, 6, 12, false, random, &stoneSelector); + generateBox(level, chunkBB, 3, 7, 3, 8, 7, 11, false, random, &stoneSelector); + generateBox(level, chunkBB, 4, 8, 4, 7, 8, 10, false, random, &stoneSelector); + + // clear interior + generateAirBox(level, chunkBB, 3, 1, 3, 8, 2, 11); + generateAirBox(level, chunkBB, 4, 3, 6, 7, 3, 9); + generateAirBox(level, chunkBB, 2, 4, 2, 9, 5, 12); + generateAirBox(level, chunkBB, 4, 6, 5, 7, 6, 9); + generateAirBox(level, chunkBB, 5, 7, 6, 6, 7, 8); + + // doors and windows + generateAirBox(level, chunkBB, 5, 1, 2, 6, 2, 2); + generateAirBox(level, chunkBB, 5, 2, 12, 6, 2, 12); + generateAirBox(level, chunkBB, 5, 5, 1, 6, 5, 1); + generateAirBox(level, chunkBB, 5, 5, 13, 6, 5, 13); + placeBlock(level, 0, 0, 1, 5, 5, chunkBB); + placeBlock(level, 0, 0, 10, 5, 5, chunkBB); + placeBlock(level, 0, 0, 1, 5, 9, chunkBB); + placeBlock(level, 0, 0, 10, 5, 9, chunkBB); + + // outside decoration + for (int z = 0; z <= 14; z += 14) + { + generateBox(level, chunkBB, 2, 4, z, 2, 5, z, false, random, &stoneSelector); + generateBox(level, chunkBB, 4, 4, z, 4, 5, z, false, random, &stoneSelector); + generateBox(level, chunkBB, 7, 4, z, 7, 5, z, false, random, &stoneSelector); + generateBox(level, chunkBB, 9, 4, z, 9, 5, z, false, random, &stoneSelector); + } + generateBox(level, chunkBB, 5, 6, 0, 6, 6, 0, false, random, &stoneSelector); + for (int x = 0; x <= 11; x += 11) + { + for (int z = 2; z <= 12; z += 2) + { + generateBox(level, chunkBB, x, 4, z, x, 5, z, false, random, &stoneSelector); + } + generateBox(level, chunkBB, x, 6, 5, x, 6, 5, false, random, &stoneSelector); + generateBox(level, chunkBB, x, 6, 9, x, 6, 9, false, random, &stoneSelector); + } + generateBox(level, chunkBB, 2, 7, 2, 2, 9, 2, false, random, &stoneSelector); + generateBox(level, chunkBB, 9, 7, 2, 9, 9, 2, false, random, &stoneSelector); + generateBox(level, chunkBB, 2, 7, 12, 2, 9, 12, false, random, &stoneSelector); + generateBox(level, chunkBB, 9, 7, 12, 9, 9, 12, false, random, &stoneSelector); + generateBox(level, chunkBB, 4, 9, 4, 4, 9, 4, false, random, &stoneSelector); + generateBox(level, chunkBB, 7, 9, 4, 7, 9, 4, false, random, &stoneSelector); + generateBox(level, chunkBB, 4, 9, 10, 4, 9, 10, false, random, &stoneSelector); + generateBox(level, chunkBB, 7, 9, 10, 7, 9, 10, false, random, &stoneSelector); + generateBox(level, chunkBB, 5, 9, 7, 6, 9, 7, false, random, &stoneSelector); + placeBlock(level, Tile::stairs_stone_Id, stairsNorth, 5, 9, 6, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsNorth, 6, 9, 6, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsSouth, 5, 9, 8, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsSouth, 6, 9, 8, chunkBB); + + // front stairs + placeBlock(level, Tile::stairs_stone_Id, stairsNorth, 4, 0, 0, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsNorth, 5, 0, 0, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsNorth, 6, 0, 0, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsNorth, 7, 0, 0, chunkBB); + + // indoor stairs up + placeBlock(level, Tile::stairs_stone_Id, stairsNorth, 4, 1, 8, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsNorth, 4, 2, 9, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsNorth, 4, 3, 10, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsNorth, 7, 1, 8, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsNorth, 7, 2, 9, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsNorth, 7, 3, 10, chunkBB); + generateBox(level, chunkBB, 4, 1, 9, 4, 1, 9, false, random, &stoneSelector); + generateBox(level, chunkBB, 7, 1, 9, 7, 1, 9, false, random, &stoneSelector); + generateBox(level, chunkBB, 4, 1, 10, 7, 2, 10, false, random, &stoneSelector); + + // indoor hand rail + generateBox(level, chunkBB, 5, 4, 5, 6, 4, 5, false, random, &stoneSelector); + placeBlock(level, Tile::stairs_stone_Id, stairsEast, 4, 4, 5, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsWest, 7, 4, 5, chunkBB); + + // indoor stairs down + for (int i = 0; i < 4; i++) + { + placeBlock(level, Tile::stairs_stone_Id, stairsSouth, 5, 0 - i, 6 + i, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, stairsSouth, 6, 0 - i, 6 + i, chunkBB); + generateAirBox(level, chunkBB, 5, 0 - i, 7 + i, 6, 0 - i, 9 + i); + } + + // underground corridors + generateAirBox(level, chunkBB, 1, -3, 12, 10, -1, 13); + generateAirBox(level, chunkBB, 1, -3, 1, 3, -1, 13); + generateAirBox(level, chunkBB, 1, -3, 1, 9, -1, 5); + for (int z = 1; z <= 13; z += 2) + { + generateBox(level, chunkBB, 1, -3, z, 1, -2, z, false, random, &stoneSelector); + } + for (int z = 2; z <= 12; z += 2) + { + generateBox(level, chunkBB, 1, -1, z, 3, -1, z, false, random, &stoneSelector); + } + generateBox(level, chunkBB, 2, -2, 1, 5, -2, 1, false, random, &stoneSelector); + generateBox(level, chunkBB, 7, -2, 1, 9, -2, 1, false, random, &stoneSelector); + generateBox(level, chunkBB, 6, -3, 1, 6, -3, 1, false, random, &stoneSelector); + generateBox(level, chunkBB, 6, -1, 1, 6, -1, 1, false, random, &stoneSelector); + + // trip wire trap 1 + placeBlock(level, Tile::tripWireSource_Id, getOrientationData(Tile::tripWireSource_Id, Direction::EAST) | TripWireSourceTile::MASK_ATTACHED, 1, -3, 8, chunkBB); + placeBlock(level, Tile::tripWireSource_Id, getOrientationData(Tile::tripWireSource_Id, Direction::WEST) | TripWireSourceTile::MASK_ATTACHED, 4, -3, 8, chunkBB); + placeBlock(level, Tile::tripWire_Id, TripWireTile::MASK_ATTACHED, 2, -3, 8, chunkBB); + placeBlock(level, Tile::tripWire_Id, TripWireTile::MASK_ATTACHED, 3, -3, 8, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 5, -3, 7, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 5, -3, 6, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 5, -3, 5, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 5, -3, 4, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 5, -3, 3, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 5, -3, 2, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 5, -3, 1, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 4, -3, 1, chunkBB); + placeBlock(level, Tile::mossStone_Id, 0, 3, -3, 1, chunkBB); + if (!placedTrap1) + { + placedTrap1 = createDispenser(level, chunkBB, random, 3, -2, 1, Facing::NORTH, WeighedTreasureArray(dispenserItems,DISPENSER_ITEMS_COUNT), 2); + } + placeBlock(level, Tile::vine_Id, 0xf, 3, -2, 2, chunkBB); + + // trip wire trap 2 + placeBlock(level, Tile::tripWireSource_Id, getOrientationData(Tile::tripWireSource_Id, Direction::NORTH) | TripWireSourceTile::MASK_ATTACHED, 7, -3, 1, chunkBB); + placeBlock(level, Tile::tripWireSource_Id, getOrientationData(Tile::tripWireSource_Id, Direction::SOUTH) | TripWireSourceTile::MASK_ATTACHED, 7, -3, 5, chunkBB); + placeBlock(level, Tile::tripWire_Id, TripWireTile::MASK_ATTACHED, 7, -3, 2, chunkBB); + placeBlock(level, Tile::tripWire_Id, TripWireTile::MASK_ATTACHED, 7, -3, 3, chunkBB); + placeBlock(level, Tile::tripWire_Id, TripWireTile::MASK_ATTACHED, 7, -3, 4, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 8, -3, 6, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 9, -3, 6, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 9, -3, 5, chunkBB); + placeBlock(level, Tile::mossStone_Id, 0, 9, -3, 4, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 9, -2, 4, chunkBB); + if (!placedTrap2) + { + placedTrap2 = createDispenser(level, chunkBB, random, 9, -2, 3, Facing::WEST, WeighedTreasureArray(dispenserItems,DISPENSER_ITEMS_COUNT), 2); + } + placeBlock(level, Tile::vine_Id, 0xf, 8, -1, 3, chunkBB); + placeBlock(level, Tile::vine_Id, 0xf, 8, -2, 3, chunkBB); + if (!placedMainChest) + { + placedMainChest = createChest(level, chunkBB, random, 8, -3, 3, WeighedTreasure::addToTreasure(WeighedTreasureArray(treasureItems,TREASURE_ITEMS_COUNT), Item::enchantedBook->createForRandomTreasure(random)), 2 + random->nextInt(5)); + } + placeBlock(level, Tile::mossStone_Id, 0, 9, -3, 2, chunkBB); + placeBlock(level, Tile::mossStone_Id, 0, 8, -3, 1, chunkBB); + placeBlock(level, Tile::mossStone_Id, 0, 4, -3, 5, chunkBB); + placeBlock(level, Tile::mossStone_Id, 0, 5, -2, 5, chunkBB); + placeBlock(level, Tile::mossStone_Id, 0, 5, -1, 5, chunkBB); + placeBlock(level, Tile::mossStone_Id, 0, 6, -3, 5, chunkBB); + placeBlock(level, Tile::mossStone_Id, 0, 7, -2, 5, chunkBB); + placeBlock(level, Tile::mossStone_Id, 0, 7, -1, 5, chunkBB); + placeBlock(level, Tile::mossStone_Id, 0, 8, -3, 5, chunkBB); + generateBox(level, chunkBB, 9, -1, 1, 9, -1, 5, false, random, &stoneSelector); + + // hidden room + generateAirBox(level, chunkBB, 8, -3, 8, 10, -1, 10); + placeBlock(level, Tile::stoneBrickSmooth_Id, SmoothStoneBrickTile::TYPE_DETAIL, 8, -2, 11, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, SmoothStoneBrickTile::TYPE_DETAIL, 9, -2, 11, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, SmoothStoneBrickTile::TYPE_DETAIL, 10, -2, 11, chunkBB); + placeBlock(level, Tile::lever_Id, LeverTile::getLeverFacing(getOrientationData(Tile::lever_Id, Facing::NORTH)), 8, -2, 12, chunkBB); + placeBlock(level, Tile::lever_Id, LeverTile::getLeverFacing(getOrientationData(Tile::lever_Id, Facing::NORTH)), 9, -2, 12, chunkBB); + placeBlock(level, Tile::lever_Id, LeverTile::getLeverFacing(getOrientationData(Tile::lever_Id, Facing::NORTH)), 10, -2, 12, chunkBB); + generateBox(level, chunkBB, 8, -3, 8, 8, -3, 10, false, random, &stoneSelector); + generateBox(level, chunkBB, 10, -3, 8, 10, -3, 10, false, random, &stoneSelector); + placeBlock(level, Tile::mossStone_Id, 0, 10, -2, 9, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 8, -2, 9, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 8, -2, 10, chunkBB); + placeBlock(level, Tile::redStoneDust_Id, 0, 10, -1, 9, chunkBB); + placeBlock(level, Tile::pistonStickyBase_Id, Facing::UP, 9, -2, 8, chunkBB); + placeBlock(level, Tile::pistonStickyBase_Id, getOrientationData(Tile::pistonStickyBase_Id, Facing::WEST), 10, -2, 8, chunkBB); + placeBlock(level, Tile::pistonStickyBase_Id, getOrientationData(Tile::pistonStickyBase_Id, Facing::WEST), 10, -1, 8, chunkBB); + placeBlock(level, Tile::diode_off_Id, getOrientationData(Tile::diode_off_Id, Direction::NORTH), 10, -2, 10, chunkBB); + if (!placedHiddenChest) + { + placedHiddenChest = createChest(level, chunkBB, random, 9, -3, 10, WeighedTreasure::addToTreasure(WeighedTreasureArray(treasureItems,TREASURE_ITEMS_COUNT), Item::enchantedBook->createForRandomTreasure(random)), 2 + random->nextInt(5)); + } + + return true; +} + +void ScatteredFeaturePieces::JunglePyramidPiece::MossStoneSelector::next(Random *random, int worldX, int worldY, int worldZ, bool isEdge) +{ + if (random->nextFloat() < .4f) + { + nextId = Tile::stoneBrick_Id; + } + else + { + nextId = Tile::mossStone_Id; + } +} + +ScatteredFeaturePieces::JunglePyramidPiece::MossStoneSelector ScatteredFeaturePieces::JunglePyramidPiece::stoneSelector; \ No newline at end of file diff --git a/Minecraft.World/ScatteredFeaturePieces.h b/Minecraft.World/ScatteredFeaturePieces.h new file mode 100644 index 00000000..08ab92ba --- /dev/null +++ b/Minecraft.World/ScatteredFeaturePieces.h @@ -0,0 +1,67 @@ +#pragma once + +#include "StructurePiece.h" + +class ScatteredFeaturePieces +{ +private: + class ScatteredFeaturePiece : public StructurePiece + { + protected: + int width; + int height; + int depth; + + int heightPosition; + + ScatteredFeaturePiece(Random *random, int west, int floor, int north, int width, int height, int depth); + + bool updateAverageGroundHeight(Level *level, BoundingBox *chunkBB, int offset); + }; + +public: + class DesertPyramidPiece : public ScatteredFeaturePiece + { + public: + static const int TREASURE_ITEMS_COUNT = 6; + private: + bool hasPlacedChest[4]; + static WeighedTreasure *treasureItems[TREASURE_ITEMS_COUNT]; + + public: + DesertPyramidPiece(Random *random, int west, int north); + + bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + + }; + + class JunglePyramidPiece : public ScatteredFeaturePiece + { + public: + static const int TREASURE_ITEMS_COUNT = 6; + static const int DISPENSER_ITEMS_COUNT = 1; + private: + bool placedMainChest; + bool placedHiddenChest; + bool placedTrap1; + bool placedTrap2; + + static WeighedTreasure *treasureItems[TREASURE_ITEMS_COUNT]; + static WeighedTreasure *dispenserItems[DISPENSER_ITEMS_COUNT]; + + public: + JunglePyramidPiece(Random *random, int west, int north); + + bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + + private: + class MossStoneSelector : public BlockSelector + { + public: + void next(Random *random, int worldX, int worldY, int worldZ, bool isEdge); + }; + + static MossStoneSelector stoneSelector; + + }; +}; \ No newline at end of file diff --git a/Minecraft.World/SeedFoodItem.cpp b/Minecraft.World/SeedFoodItem.cpp new file mode 100644 index 00000000..4ba9a14a --- /dev/null +++ b/Minecraft.World/SeedFoodItem.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.h" +#include "net.minecraft.world.level.h" +#include "SeedFoodItem.h" + +SeedFoodItem::SeedFoodItem(int id, int nutrition, float saturationMod, int resultId, int targetLand) : FoodItem(id, nutrition, saturationMod, false) +{ + this->resultId = resultId; + this->targetLand = targetLand; + +} + +bool SeedFoodItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + if (face != Facing::UP) return false; + + if (!player->mayBuild(x, y, z) || !player->mayBuild(x, y + 1, z)) return false; + int targetType = level->getTile(x, y, z); + + if (targetType == targetLand && level->isEmptyTile(x, y + 1, z)) + { + if(!bTestUseOnOnly) + { + level->setTile(x, y + 1, z, resultId); + instance->count--; + } + return true; + } + return false; +} \ No newline at end of file diff --git a/Minecraft.World/SeedFoodItem.h b/Minecraft.World/SeedFoodItem.h new file mode 100644 index 00000000..fe24430f --- /dev/null +++ b/Minecraft.World/SeedFoodItem.h @@ -0,0 +1,15 @@ +#pragma once + +#include "FoodItem.h" + +class SeedFoodItem : public FoodItem +{ +private: + int resultId; + int targetLand; + +public: + SeedFoodItem(int id, int nutrition, float saturationMod, int resultId, int targetLand); + + bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly); +}; \ No newline at end of file diff --git a/Minecraft.World/SeedItem.cpp b/Minecraft.World/SeedItem.cpp new file mode 100644 index 00000000..7a5ac1b8 --- /dev/null +++ b/Minecraft.World/SeedItem.cpp @@ -0,0 +1,36 @@ +using namespace std; + +#include "stdafx.h" +#include "Item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "ItemInstance.h" +#include "SeedItem.h" + +SeedItem::SeedItem(int id, int resultId, int targetLand) : Item(id) +{ + this->resultId = resultId; + this->targetLand = targetLand; +} + +bool SeedItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + // 4J-PB - Adding a test only version to allow tooltips to be displayed + if (face != 1) return false; + + if (!player->mayBuild(x, y, z) || !player->mayBuild(x, y + 1, z)) return false; + + int targetType = level->getTile(x, y, z); + + if (targetType == targetLand && level->isEmptyTile(x, y + 1, z)) + { + if(!bTestUseOnOnly) + { + level->setTile(x, y + 1, z, resultId); + instance->count--; + } + return true; + } + return false; +} \ No newline at end of file diff --git a/Minecraft.World/SeedItem.h b/Minecraft.World/SeedItem.h new file mode 100644 index 00000000..c16a76ce --- /dev/null +++ b/Minecraft.World/SeedItem.h @@ -0,0 +1,16 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class SeedItem : public Item +{ +private: + int resultId; + int targetLand; + +public: + SeedItem(int id, int resultId, int targetLand); + + virtual bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); +}; \ No newline at end of file diff --git a/Minecraft.World/Sensing.cpp b/Minecraft.World/Sensing.cpp new file mode 100644 index 00000000..d451f483 --- /dev/null +++ b/Minecraft.World/Sensing.cpp @@ -0,0 +1,35 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "Sensing.h" + +Sensing::Sensing(Mob *mob) +{ + this->mob = mob; +} + +void Sensing::tick() +{ + seen.clear(); + unseen.clear(); +} + +bool Sensing::canSee(shared_ptr target) +{ + //if ( find(seen.begin(), seen.end(), target) != seen.end() ) return true; + //if ( find(unseen.begin(), unseen.end(), target) != unseen.end()) return false; + for(AUTO_VAR(it, seen.begin()); it != seen.end(); ++it) + { + if(target == (*it).lock()) return true; + } + for(AUTO_VAR(it, unseen.begin()); it != unseen.end(); ++it) + { + if(target == (*it).lock()) return false; + } + + //util.Timer.push("canSee"); + bool canSee = mob->canSee(target); + //util.Timer.pop(); + if (canSee) seen.push_back(weak_ptr(target)); + else unseen.push_back(weak_ptr(target)); + return canSee; +} \ No newline at end of file diff --git a/Minecraft.World/Sensing.h b/Minecraft.World/Sensing.h new file mode 100644 index 00000000..d6428515 --- /dev/null +++ b/Minecraft.World/Sensing.h @@ -0,0 +1,15 @@ +#pragma once + +class Sensing +{ +private: + Mob *mob; + vector > seen; + vector > unseen; + +public: + Sensing(Mob *mob); + + void tick(); + bool canSee(shared_ptr target); +}; \ No newline at end of file diff --git a/Minecraft.World/ServerAuthDataPacket.h b/Minecraft.World/ServerAuthDataPacket.h new file mode 100644 index 00000000..c56844d1 --- /dev/null +++ b/Minecraft.World/ServerAuthDataPacket.h @@ -0,0 +1,58 @@ +#pragma once + +#include "Packet.h" + +class ServerAuthDataPacket : public Packet +{ +#if 0 + private String serverId; + private PublicKey publicKey; + private byte[] nonce = new byte[]{}; + + public ServerAuthDataPacket() { + // Needed + } + + public ServerAuthDataPacket(final String serverId, final PublicKey publicKey, final byte[] nonce) { + this.serverId = serverId; + this.publicKey = publicKey; + this.nonce = nonce; + } + + @Override + public void read(DataInputStream dis) throws IOException { + serverId = readUtf(dis, 20); + publicKey = Crypt.byteToPublicKey(readBytes(dis)); + nonce = readBytes(dis); + } + + @Override + public void write(DataOutputStream dos) throws IOException { + writeUtf(serverId, dos); + writeBytes(dos, publicKey.getEncoded()); + writeBytes(dos, nonce); + } + + @Override + public void handle(PacketListener listener) { + listener.handleServerAuthData(this); + } + + @Override + public int getEstimatedSize() { + return 2 + serverId.length() * 2 + 2 + publicKey.getEncoded().length + 2 + nonce.length; + } + + public String getServerId() { + return serverId; + } + + public PublicKey getPublicKey() { + return publicKey; + } + + public byte[] getNonce() { + return nonce; + } +#endif +}; \ No newline at end of file diff --git a/Minecraft.World/ServerSettingsChangedPacket.cpp b/Minecraft.World/ServerSettingsChangedPacket.cpp new file mode 100644 index 00000000..ae5a8f3c --- /dev/null +++ b/Minecraft.World/ServerSettingsChangedPacket.cpp @@ -0,0 +1,52 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.item.h" +#include "PacketListener.h" +#include "ServerSettingsChangedPacket.h" + + + +const int ServerSettingsChangedPacket::HOST_DIFFICULTY = 0; +const int ServerSettingsChangedPacket::HOST_OPTIONS = 1; +const int ServerSettingsChangedPacket::HOST_IN_GAME_SETTINGS = 2; + +ServerSettingsChangedPacket::~ServerSettingsChangedPacket() +{ +} + +ServerSettingsChangedPacket::ServerSettingsChangedPacket() +{ + action = HOST_DIFFICULTY; + data = 1; +} + +ServerSettingsChangedPacket::ServerSettingsChangedPacket(char action, unsigned int data) +{ + this->action = action; + this->data = data; + + //app.DebugPrintf("ServerSettingsChangedPacket - Difficulty = %d",difficulty); +} + +void ServerSettingsChangedPacket::handle(PacketListener *listener) +{ + listener->handleServerSettingsChanged(shared_from_this()); +} + +void ServerSettingsChangedPacket::read(DataInputStream *dis) //throws IOException +{ + action = dis->read(); + data = dis->readInt(); +} + +void ServerSettingsChangedPacket::write(DataOutputStream *dos) // throws IOException +{ + dos->write(action); + dos->writeInt(data); +} + +int ServerSettingsChangedPacket::getEstimatedSize() +{ + return 2; +} diff --git a/Minecraft.World/ServerSettingsChangedPacket.h b/Minecraft.World/ServerSettingsChangedPacket.h new file mode 100644 index 00000000..e6ab7356 --- /dev/null +++ b/Minecraft.World/ServerSettingsChangedPacket.h @@ -0,0 +1,31 @@ +#pragma once + +// 4J ADDED THIS PACKET + +using namespace std; + +#include "Packet.h" + +class ServerSettingsChangedPacket : public Packet, public enable_shared_from_this +{ +public: + static const int HOST_DIFFICULTY; + static const int HOST_OPTIONS; + static const int HOST_IN_GAME_SETTINGS; + + char action; + unsigned int data; + + ServerSettingsChangedPacket(); + ~ServerSettingsChangedPacket(); + ServerSettingsChangedPacket(char action, unsigned int data); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new ServerSettingsChangedPacket()); } + virtual int getId() { return 153; } +}; \ No newline at end of file diff --git a/Minecraft.World/SetCarriedItemPacket.cpp b/Minecraft.World/SetCarriedItemPacket.cpp new file mode 100644 index 00000000..2c640609 --- /dev/null +++ b/Minecraft.World/SetCarriedItemPacket.cpp @@ -0,0 +1,47 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "SetCarriedItemPacket.h" + + + +SetCarriedItemPacket::SetCarriedItemPacket() +{ + slot = 0; +} + +SetCarriedItemPacket::SetCarriedItemPacket(int slot) +{ + this->slot = slot; +} + +void SetCarriedItemPacket::read(DataInputStream *dis) //throws IOException +{ + slot = dis->readShort(); +} + +void SetCarriedItemPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeShort(slot); +} + +void SetCarriedItemPacket::handle(PacketListener *listener) +{ + listener->handleSetCarriedItem(shared_from_this()); +} + +int SetCarriedItemPacket::getEstimatedSize() +{ + return 2; +} + +bool SetCarriedItemPacket::canBeInvalidated() +{ + return true; +} + +bool SetCarriedItemPacket::isInvalidatedBy(shared_ptr packet) +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/SetCarriedItemPacket.h b/Minecraft.World/SetCarriedItemPacket.h new file mode 100644 index 00000000..06fb3c30 --- /dev/null +++ b/Minecraft.World/SetCarriedItemPacket.h @@ -0,0 +1,24 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class SetCarriedItemPacket : public Packet, public enable_shared_from_this +{ +public: + int slot; + + SetCarriedItemPacket(); + SetCarriedItemPacket(int slot); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + +public: + static shared_ptr create() { return shared_ptr(new SetCarriedItemPacket()); } + virtual int getId() { return 16; } +}; \ No newline at end of file diff --git a/Minecraft.World/SetCreativeModeSlotPacket.cpp b/Minecraft.World/SetCreativeModeSlotPacket.cpp new file mode 100644 index 00000000..17e36b37 --- /dev/null +++ b/Minecraft.World/SetCreativeModeSlotPacket.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "SetCreativeModeSlotPacket.h" + + + +SetCreativeModeSlotPacket::SetCreativeModeSlotPacket() +{ + this->slotNum = 0; + this->item = nullptr; +} + +SetCreativeModeSlotPacket::SetCreativeModeSlotPacket(int slotNum, shared_ptr item) +{ + this->slotNum = slotNum; + // 4J - take copy of item as we want our packets to have full ownership of any referenced data + this->item = item ? item->copy() : shared_ptr(); +} + +void SetCreativeModeSlotPacket::handle(PacketListener *listener) +{ + listener->handleSetCreativeModeSlot(shared_from_this()); +} + +void SetCreativeModeSlotPacket::read(DataInputStream *dis) +{ + slotNum = dis->readShort(); + item = readItem(dis); +} + +void SetCreativeModeSlotPacket::write(DataOutputStream *dos) +{ + dos->writeShort(slotNum); + writeItem(item, dos); + +} + +int SetCreativeModeSlotPacket::getEstimatedSize() +{ + return 8; +} \ No newline at end of file diff --git a/Minecraft.World/SetCreativeModeSlotPacket.h b/Minecraft.World/SetCreativeModeSlotPacket.h new file mode 100644 index 00000000..94ae7807 --- /dev/null +++ b/Minecraft.World/SetCreativeModeSlotPacket.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Packet.h" + +class SetCreativeModeSlotPacket : public Packet, public enable_shared_from_this +{ + public: + int slotNum; + shared_ptr item; + + SetCreativeModeSlotPacket(); + SetCreativeModeSlotPacket(int slotNum, shared_ptr item); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + + +public: + static shared_ptr create() { return shared_ptr(new SetCreativeModeSlotPacket()); } + virtual int getId() { return 107; } +}; \ No newline at end of file diff --git a/Minecraft.World/SetEntityDataPacket.cpp b/Minecraft.World/SetEntityDataPacket.cpp new file mode 100644 index 00000000..1538c152 --- /dev/null +++ b/Minecraft.World/SetEntityDataPacket.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "PacketListener.h" +#include "SetEntityDataPacket.h" + + + +SetEntityDataPacket::SetEntityDataPacket() +{ + id = -1; + packedItems = NULL; +} + +SetEntityDataPacket::~SetEntityDataPacket() +{ + delete packedItems; +} + +SetEntityDataPacket::SetEntityDataPacket(int id, shared_ptr entityData, bool notJustDirty) +{ + this->id = id; + if(notJustDirty) + { + this->packedItems = entityData->getAll(); + } + else + { + this->packedItems = entityData->packDirty(); + } +} + +void SetEntityDataPacket::read(DataInputStream *dis) //throws IOException +{ + id = dis->readInt(); + packedItems = SynchedEntityData::unpack(dis); +} + +void SetEntityDataPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(id); + SynchedEntityData::pack(packedItems, dos); +} + +void SetEntityDataPacket::handle(PacketListener *listener) +{ + listener->handleSetEntityData(shared_from_this()); +} + +int SetEntityDataPacket::getEstimatedSize() +{ + return 5; +} + +bool SetEntityDataPacket::isAync() +{ + return true; +} + +vector > *SetEntityDataPacket::getUnpackedData() +{ + return packedItems; +} diff --git a/Minecraft.World/SetEntityDataPacket.h b/Minecraft.World/SetEntityDataPacket.h new file mode 100644 index 00000000..1b31aa4c --- /dev/null +++ b/Minecraft.World/SetEntityDataPacket.h @@ -0,0 +1,31 @@ +#pragma once +using namespace std; + +#include "Packet.h" +#include "SynchedEntityData.h" + +class SetEntityDataPacket : public Packet, public enable_shared_from_this +{ +public: + int id; + +private: + vector > *packedItems; + +public: + SetEntityDataPacket(); + ~SetEntityDataPacket(); + SetEntityDataPacket(int id, shared_ptr, bool notJustDirty); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool isAync(); + + vector > *getUnpackedData(); + +public: + static shared_ptr create() { return shared_ptr(new SetEntityDataPacket()); } + virtual int getId() { return 40; } +}; \ No newline at end of file diff --git a/Minecraft.World/SetEntityMotionPacket.cpp b/Minecraft.World/SetEntityMotionPacket.cpp new file mode 100644 index 00000000..a0749b74 --- /dev/null +++ b/Minecraft.World/SetEntityMotionPacket.cpp @@ -0,0 +1,113 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "PacketListener.h" +#include "SetEntityMotionPacket.h" + + + +void SetEntityMotionPacket::_init(int id, double xd, double yd, double zd) +{ + this->id = id; + double m = 3.9; + if (xd < -m) xd = -m; + if (yd < -m) yd = -m; + if (zd < -m) zd = -m; + if (xd > m) xd = m; + if (yd > m) yd = m; + if (zd > m) zd = m; + xa = (int) (xd * 8000.0); + ya = (int) (yd * 8000.0); + za = (int) (zd * 8000.0); + // 4J - if we could transmit this as bytes (in 1/16 accuracy) then flag to do so + if( ( xa >= (-128 * 16 ) ) && ( ya >= (-128 * 16 ) ) && ( za >= (-128 * 16 ) ) && + ( xa < (128 * 16 ) ) && ( ya < (128 * 16 ) ) && ( za < (128 * 16 ) ) ) + { + useBytes = true; + } + else + { + useBytes = false; + } +} + +SetEntityMotionPacket::SetEntityMotionPacket() +{ + _init(0, 0.0f, 0.0f, 0.0f); +} + +SetEntityMotionPacket::SetEntityMotionPacket(shared_ptr e) +{ + _init(e->entityId, e->xd, e->yd, e->zd); +} + +SetEntityMotionPacket::SetEntityMotionPacket(int id, double xd, double yd, double zd) +{ + _init(id, xd, yd, zd); +} + +void SetEntityMotionPacket::read(DataInputStream *dis) //throws IOException +{ + short idAndFlag = dis->readShort(); + id = idAndFlag & 0x07ff; + if( idAndFlag & 0x0800 ) + { + xa = (int)dis->readByte(); + ya = (int)dis->readByte(); + za = (int)dis->readByte(); + xa = ( xa << 24 ) >> 24; + ya = ( ya << 24 ) >> 24; + za = ( za << 24 ) >> 24; + xa *= 16; + ya *= 16; + za *= 16; + useBytes = true; + } + else + { + xa = dis->readShort(); + ya = dis->readShort(); + za = dis->readShort(); + useBytes = false; + } +} + +void SetEntityMotionPacket::write(DataOutputStream *dos) //throws IOException +{ + if( useBytes ) + { + dos->writeShort(id | 0x800); + dos->writeByte(xa/16); + dos->writeByte(ya/16); + dos->writeByte(za/16); + } + else + { + dos->writeShort(id); + dos->writeShort(xa); + dos->writeShort(ya); + dos->writeShort(za); + } +} + +void SetEntityMotionPacket::handle(PacketListener *listener) +{ + listener->handleSetEntityMotion(shared_from_this()); +} + +int SetEntityMotionPacket::getEstimatedSize() +{ + return useBytes ? 5 : 8; +} + +bool SetEntityMotionPacket::canBeInvalidated() +{ + return true; +} + +bool SetEntityMotionPacket::isInvalidatedBy(shared_ptr packet) +{ + shared_ptr target = dynamic_pointer_cast(packet); + return target->id == id; +} diff --git a/Minecraft.World/SetEntityMotionPacket.h b/Minecraft.World/SetEntityMotionPacket.h new file mode 100644 index 00000000..00c019da --- /dev/null +++ b/Minecraft.World/SetEntityMotionPacket.h @@ -0,0 +1,31 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class SetEntityMotionPacket : public Packet, public enable_shared_from_this +{ +public: + int id; + int xa, ya, za; + bool useBytes; // 4J added + +private: + void _init(int id, double xd, double yd, double zd); + +public: + SetEntityMotionPacket(); + SetEntityMotionPacket(shared_ptr e); + SetEntityMotionPacket(int id, double xd, double yd, double zd); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + +public: + static shared_ptr create() { return shared_ptr(new SetEntityMotionPacket()); } + virtual int getId() { return 28; } +}; \ No newline at end of file diff --git a/Minecraft.World/SetEquippedItemPacket.cpp b/Minecraft.World/SetEquippedItemPacket.cpp new file mode 100644 index 00000000..1da93b84 --- /dev/null +++ b/Minecraft.World/SetEquippedItemPacket.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.item.h" +#include "PacketListener.h" +#include "SetEquippedItemPacket.h" + + + +SetEquippedItemPacket::SetEquippedItemPacket() +{ + entity = 0; + slot = 0; + item = nullptr; +} + +SetEquippedItemPacket::SetEquippedItemPacket(int entity, int slot, shared_ptr item) +{ + this->entity = entity; + this->slot = slot; + + // 4J Stu - Brought forward change from 1.3 to fix #64688 - Customer Encountered: TU7: Content: Art: Aura of enchanted item is not displayed for other players in online game + this->item = item == NULL ? nullptr : item->copy(); +} + +void SetEquippedItemPacket::read(DataInputStream *dis) //throws IOException +{ + entity = dis->readInt(); + slot = dis->readShort(); + + // 4J Stu - Brought forward change from 1.3 to fix #64688 - Customer Encountered: TU7: Content: Art: Aura of enchanted item is not displayed for other players in online game + item = readItem(dis); +} + +void SetEquippedItemPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(entity); + dos->writeShort(slot); + + // 4J Stu - Brought forward change from 1.3 to fix #64688 - Customer Encountered: TU7: Content: Art: Aura of enchanted item is not displayed for other players in online game + writeItem(item, dos); +} + +void SetEquippedItemPacket::handle(PacketListener *listener) +{ + listener->handleSetEquippedItem(shared_from_this()); +} + +int SetEquippedItemPacket::getEstimatedSize() +{ + return 4 + 2 * 2; +} + +// 4J Stu - Brought forward from 1.3 to fix #64688 - Customer Encountered: TU7: Content: Art: Aura of enchanted item is not displayed for other players in online game +shared_ptr SetEquippedItemPacket::getItem() +{ + return item; +} + +bool SetEquippedItemPacket::canBeInvalidated() +{ + return true; +} + +bool SetEquippedItemPacket::isInvalidatedBy(shared_ptr packet) +{ + shared_ptr target = dynamic_pointer_cast(packet); + return target->entity == entity && target->slot == slot; +} \ No newline at end of file diff --git a/Minecraft.World/SetEquippedItemPacket.h b/Minecraft.World/SetEquippedItemPacket.h new file mode 100644 index 00000000..def39120 --- /dev/null +++ b/Minecraft.World/SetEquippedItemPacket.h @@ -0,0 +1,33 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class SetEquippedItemPacket : public Packet, public enable_shared_from_this +{ +public: + int entity; + int slot; + +private: + // 4J Stu - Brought forward from 1.3 to fix #64688 - Customer Encountered: TU7: Content: Art: Aura of enchanted item is not displayed for other players in online game + shared_ptr item; + +public: + SetEquippedItemPacket(); + SetEquippedItemPacket(int entity, int slot, shared_ptr item); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + + // 4J Stu - Brought forward from 1.3 to fix #64688 - Customer Encountered: TU7: Content: Art: Aura of enchanted item is not displayed for other players in online game + shared_ptr getItem(); + +public: + static shared_ptr create() { return shared_ptr(new SetEquippedItemPacket()); } + virtual int getId() { return 5; } +}; \ No newline at end of file diff --git a/Minecraft.World/SetExperiencePacket.cpp b/Minecraft.World/SetExperiencePacket.cpp new file mode 100644 index 00000000..ffe90a67 --- /dev/null +++ b/Minecraft.World/SetExperiencePacket.cpp @@ -0,0 +1,59 @@ +#include "stdafx.h" +#include "SetExperiencePacket.h" +#include "PacketListener.h" +#include "InputOutputStream.h" + + + +SetExperiencePacket::SetExperiencePacket() +{ + this->experienceProgress = 0; + this->totalExperience = 0; + this->experienceLevel = 0; +} + +SetExperiencePacket::SetExperiencePacket(float experienceProgress, int totalExperience, int experienceLevel) +{ + this->experienceProgress = experienceProgress; + this->totalExperience = totalExperience; + this->experienceLevel = experienceLevel; +} + +void SetExperiencePacket::read(DataInputStream *dis) +{ + experienceProgress = dis->readFloat(); + experienceLevel = dis->readShort(); + totalExperience = dis->readShort(); +} + +void SetExperiencePacket::write(DataOutputStream *dos) +{ + dos->writeFloat(experienceProgress); + dos->writeShort(experienceLevel); + dos->writeShort(totalExperience); +} + +void SetExperiencePacket::handle(PacketListener *listener) +{ + listener->handleSetExperience(shared_from_this()); +} + +int SetExperiencePacket::getEstimatedSize() +{ + return 8; +} + +bool SetExperiencePacket::canBeInvalidated() +{ + return true; +} + +bool SetExperiencePacket::isInvalidatedBy(shared_ptr packet) +{ + return true; +} + +bool SetExperiencePacket::isAync() +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/SetExperiencePacket.h b/Minecraft.World/SetExperiencePacket.h new file mode 100644 index 00000000..499b7efd --- /dev/null +++ b/Minecraft.World/SetExperiencePacket.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Packet.h" + +class SetExperiencePacket : public Packet, public enable_shared_from_this +{ +public: + float experienceProgress; + int totalExperience; + int experienceLevel; + + SetExperiencePacket(); + SetExperiencePacket(float experienceProgress, int totalExperience, int experienceLevel); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + virtual bool isAync(); + +public: + static shared_ptr create() { return shared_ptr(new SetExperiencePacket()); } + virtual int getId() { return 43; } +}; \ No newline at end of file diff --git a/Minecraft.World/SetHealthPacket.cpp b/Minecraft.World/SetHealthPacket.cpp new file mode 100644 index 00000000..55d7ccc3 --- /dev/null +++ b/Minecraft.World/SetHealthPacket.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "SetHealthPacket.h" + + + +SetHealthPacket::SetHealthPacket() +{ + this->health = 0; + this->food = 0; + this->saturation = 0; + + this->damageSource = eTelemetryChallenges_Unknown; +} + +SetHealthPacket::SetHealthPacket(int health, int food, float saturation, ETelemetryChallenges damageSource) +{ + this->health = health; + this->food = food; + this->saturation = saturation; + // this.exhaustion = exhaustion; // 4J - Original comment + + this->damageSource = damageSource; +} + +void SetHealthPacket::read(DataInputStream *dis) //throws IOException +{ + health = dis->readShort(); + food = dis->readShort(); + saturation = dis->readFloat(); + // exhaustion = dis.readFloat(); + + damageSource = (ETelemetryChallenges)dis->readByte(); +} + +void SetHealthPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeShort(health); + dos->writeShort(food); + dos->writeFloat(saturation); + // dos.writeFloat(exhaustion); + + dos->writeByte(damageSource); +} + +void SetHealthPacket::handle(PacketListener *listener) +{ + listener->handleSetHealth(shared_from_this()); +} + +int SetHealthPacket::getEstimatedSize() +{ + return 9; +} + +bool SetHealthPacket::canBeInvalidated() +{ + return true; +} + +bool SetHealthPacket::isInvalidatedBy(shared_ptr packet) +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/SetHealthPacket.h b/Minecraft.World/SetHealthPacket.h new file mode 100644 index 00000000..de8f4cb9 --- /dev/null +++ b/Minecraft.World/SetHealthPacket.h @@ -0,0 +1,30 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class SetHealthPacket : public Packet, public enable_shared_from_this +{ +public: + int health; + int food; + float saturation; + // public float exhaustion; // 4J - Original comment + + ETelemetryChallenges damageSource; // 4J Added + + SetHealthPacket(); + SetHealthPacket(int health, int food, float saturation, ETelemetryChallenges damageSource); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + +public: + static shared_ptr create() { return shared_ptr(new SetHealthPacket()); } + virtual int getId() { return 8; } +}; + diff --git a/Minecraft.World/SetRidingPacket.cpp b/Minecraft.World/SetRidingPacket.cpp new file mode 100644 index 00000000..454da047 --- /dev/null +++ b/Minecraft.World/SetRidingPacket.cpp @@ -0,0 +1,53 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "net.minecraft.world.entity.h" +#include "SetRidingPacket.h" + + + +SetRidingPacket::SetRidingPacket() +{ + riderId = -1; + riddenId = -1; +} + +SetRidingPacket::SetRidingPacket(shared_ptr rider, shared_ptr riding) +{ + this->riderId = rider->entityId; + this->riddenId = riding != NULL ? riding->entityId : -1; +} + +int SetRidingPacket::getEstimatedSize() +{ + return 8; +} + +void SetRidingPacket::read(DataInputStream *dis) //throws IOException +{ + riderId = dis->readInt(); + riddenId = dis->readInt(); +} + +void SetRidingPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(riderId); + dos->writeInt(riddenId); +} + +void SetRidingPacket::handle(PacketListener *listener) +{ + listener->handleRidePacket(shared_from_this()); +} + +bool SetRidingPacket::canBeInvalidated() +{ + return true; +} + +bool SetRidingPacket::isInvalidatedBy(shared_ptr packet) +{ + shared_ptr target = dynamic_pointer_cast(packet); + return target->riderId == riderId; +} diff --git a/Minecraft.World/SetRidingPacket.h b/Minecraft.World/SetRidingPacket.h new file mode 100644 index 00000000..60c3ac43 --- /dev/null +++ b/Minecraft.World/SetRidingPacket.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class SetRidingPacket : public Packet, public enable_shared_from_this +{ +public: + int riderId, riddenId; + + SetRidingPacket(); + SetRidingPacket(shared_ptr rider, shared_ptr riding); + + virtual int getEstimatedSize(); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + +public: + static shared_ptr create() { return shared_ptr(new SetRidingPacket()); } + virtual int getId() { return 39; } + +}; \ No newline at end of file diff --git a/Minecraft.World/SetSpawnPositionPacket.cpp b/Minecraft.World/SetSpawnPositionPacket.cpp new file mode 100644 index 00000000..035da347 --- /dev/null +++ b/Minecraft.World/SetSpawnPositionPacket.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "SetSpawnPositionPacket.h" + + + +SetSpawnPositionPacket::SetSpawnPositionPacket() +{ + x = 0; + y = 0; + z = 0; +} + +SetSpawnPositionPacket::SetSpawnPositionPacket(int x, int y, int z) +{ + this->x = x; + this->y = y; + this->z = z; +} + +void SetSpawnPositionPacket::read(DataInputStream *dis) //throws IOException +{ + x = dis->readInt(); + y = dis->readInt(); + z = dis->readInt(); +} + +void SetSpawnPositionPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(x); + dos->writeInt(y); + dos->writeInt(z); +} + +void SetSpawnPositionPacket::handle(PacketListener *listener) +{ + listener->handleSetSpawn(shared_from_this()); +} + +int SetSpawnPositionPacket::getEstimatedSize() +{ + return 3*4; +} + +bool SetSpawnPositionPacket::canBeInvalidated() +{ + return true; +} + +bool SetSpawnPositionPacket::isInvalidatedBy(shared_ptr packet) +{ + return true; +} + +bool SetSpawnPositionPacket::isAync() +{ + return false; +} \ No newline at end of file diff --git a/Minecraft.World/SetSpawnPositionPacket.h b/Minecraft.World/SetSpawnPositionPacket.h new file mode 100644 index 00000000..3ba66af8 --- /dev/null +++ b/Minecraft.World/SetSpawnPositionPacket.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class SetSpawnPositionPacket : public Packet, public enable_shared_from_this +{ +public: + int x, y, z; + + SetSpawnPositionPacket(); + SetSpawnPositionPacket(int x, int y, int z); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + virtual bool isAync(); + +public: + static shared_ptr create() { return shared_ptr(new SetSpawnPositionPacket()); } + virtual int getId() { return 6; } +}; \ No newline at end of file diff --git a/Minecraft.World/SetTimePacket.cpp b/Minecraft.World/SetTimePacket.cpp new file mode 100644 index 00000000..2f707970 --- /dev/null +++ b/Minecraft.World/SetTimePacket.cpp @@ -0,0 +1,52 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "SetTimePacket.h" + + + +SetTimePacket::SetTimePacket() +{ + time = 0; +} + +SetTimePacket::SetTimePacket(__int64 time) +{ + this->time = time; +} + +void SetTimePacket::read(DataInputStream *dis) //throws IOException +{ + time = dis->readLong(); +} + +void SetTimePacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeLong(time); +} + +void SetTimePacket::handle(PacketListener *listener) +{ + listener->handleSetTime(shared_from_this()); +} + +int SetTimePacket::getEstimatedSize() +{ + return 8; +} + +bool SetTimePacket::canBeInvalidated() +{ + return true; +} + +bool SetTimePacket::isInvalidatedBy(shared_ptr packet) +{ + return true; +} + +bool SetTimePacket::isAync() +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/SetTimePacket.h b/Minecraft.World/SetTimePacket.h new file mode 100644 index 00000000..5b658a0b --- /dev/null +++ b/Minecraft.World/SetTimePacket.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class SetTimePacket : public Packet, public enable_shared_from_this +{ +public: + __int64 time; + + SetTimePacket(); + SetTimePacket(__int64 time); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + virtual bool isAync(); + +public: + static shared_ptr create() { return shared_ptr(new SetTimePacket()); } + virtual int getId() { return 4; } +}; \ No newline at end of file diff --git a/Minecraft.World/ShapedRecipy.cpp b/Minecraft.World/ShapedRecipy.cpp new file mode 100644 index 00000000..15ccca2d --- /dev/null +++ b/Minecraft.World/ShapedRecipy.cpp @@ -0,0 +1,230 @@ +// package net.minecraft.world.item.crafting; +// +// import net.minecraft.world.inventory.CraftingContainer; +// import net.minecraft.world.item.ItemInstance; + +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.inventory.h" +#include "Tile.h" +#include "Recipy.h" +#include "Recipes.h" +#include "ShapedRecipy.h" + +// 4J-PB - for new crafting - Adding group to define type of item that the recipe produces +ShapedRecipy::ShapedRecipy(int width, int height, ItemInstance **recipeItems, ItemInstance *result, int iGroup) + : resultId(result->id) +{ + this->width = width; + this->height = height; + this->recipeItems = recipeItems; + this->result = result; + this->group = iGroup; + _keepTag = false; +} + +const int ShapedRecipy::getGroup() +{ + return group; +} + +const ItemInstance *ShapedRecipy::getResultItem() +{ + return result; +} + +bool ShapedRecipy::matches(shared_ptr craftSlots, Level *level) +{ + for (int xOffs = 0; xOffs <= (3 - width); xOffs++) + { + for (int yOffs = 0; yOffs <= (3 - height); yOffs++) + { + if (matches(craftSlots, xOffs, yOffs, true)) return true; + if (matches(craftSlots, xOffs, yOffs, false)) return true; + } + } + return false; +} + +bool ShapedRecipy::matches(shared_ptr craftSlots, int xOffs, int yOffs, bool xFlip) +{ + for (int x = 0; x < 3; x++) { + for (int y = 0; y < 3; y++) { + int xs = x - xOffs; + int ys = y - yOffs; + ItemInstance *expected = NULL; + if (xs >= 0 && ys >= 0 && xs < width && ys < height) + { + if (xFlip) expected = recipeItems[(width - xs - 1) + ys * width]; + else expected = recipeItems[xs + ys * width]; + } + shared_ptr item = craftSlots->getItem(x, y); + if (item == NULL && expected == NULL) + { + continue; + } + if ((item == NULL && expected != NULL) || (item != NULL && expected == NULL)) + { + return false; + } + if (expected->id != item->id) + { + return false; + } + if (expected->getAuxValue() != Recipes::ANY_AUX_VALUE && expected->getAuxValue() != item->getAuxValue()) + { + return false; + } + } + } + return true; +} + +shared_ptr ShapedRecipy::assemble(shared_ptr craftSlots) +{ + shared_ptr result = getResultItem()->copy(); + + if (_keepTag && craftSlots != NULL) + { + for (int i = 0; i < craftSlots->getContainerSize(); i++) + { + shared_ptr item = craftSlots->getItem(i); + + if (item != NULL && item->hasTag()) + { + result->setTag((CompoundTag *) item->tag->copy()); + } + } + } + + return result; +} + +int ShapedRecipy::size() +{ + return width * height; +} + +// 4J-PB +bool ShapedRecipy::requires(int iRecipe) +{ + app.DebugPrintf("ShapedRecipy %d\n",iRecipe); + int iCount=0; + for (int x = 0; x < 3; x++) + { + for (int y = 0; y < 3; y++) + { + if (x < width && y < height) + { + ItemInstance *expected = recipeItems[x+y*width]; + if (expected!=NULL) + { + //printf("\tIngredient %d is %d\n",iCount++,expected->id); + } + } + } + } + + + + return false; +} + +void ShapedRecipy::requires(INGREDIENTS_REQUIRED *pIngReq) +{ + //printf("ShapedRecipy %d\n",iRecipe); + + int iCount=0; + bool bFound; + int j; + INGREDIENTS_REQUIRED TempIngReq; + TempIngReq.iIngC=0; + TempIngReq.iType = ((width>2) ||(height>2))?RECIPE_TYPE_3x3:RECIPE_TYPE_2x2; // 3x3 + // 3x3 + TempIngReq.uiGridA = new unsigned int [9]; + TempIngReq.iIngIDA= new int [9]; + TempIngReq.iIngValA = new int [9]; + TempIngReq.iIngAuxValA = new int [9]; + + ZeroMemory(TempIngReq.iIngIDA,sizeof(int)*9); + ZeroMemory(TempIngReq.iIngValA,sizeof(int)*9); + memset(TempIngReq.iIngAuxValA,Recipes::ANY_AUX_VALUE,sizeof(int)*9); + ZeroMemory(TempIngReq.uiGridA,sizeof(unsigned int)*9); + + for (int x = 0; x < 3; x++) + { + for (int y = 0; y < 3; y++) + { + if (x < width && y < height) + { + ItemInstance *expected = recipeItems[x+y*width]; + + if (expected!=NULL) + { + int iAuxVal = expected->getAuxValue(); + TempIngReq.uiGridA[x+y*3]=expected->id | iAuxVal<<24; + + bFound=false; + for(j=0;jid) && (iAuxVal == Recipes::ANY_AUX_VALUE || TempIngReq.iIngAuxValA[j] == iAuxVal)) + { + bFound= true; + break; + } + } + if(bFound) + { + TempIngReq.iIngValA[j]++; + } + else + { + TempIngReq.iIngIDA[TempIngReq.iIngC]=expected->id; + TempIngReq.iIngAuxValA[TempIngReq.iIngC]=iAuxVal; + TempIngReq.iIngValA[TempIngReq.iIngC++]++; + } + //printf("\tIngredient %d is %d\n",iCount++,expected->id); + } + + } + } + } + pIngReq->iIngIDA= new int [TempIngReq.iIngC]; + pIngReq->iIngValA= new int [TempIngReq.iIngC]; + pIngReq->iIngAuxValA = new int [TempIngReq.iIngC]; + pIngReq->uiGridA = new unsigned int [9]; + + pIngReq->iIngC=TempIngReq.iIngC; + pIngReq->iType=TempIngReq.iType; + + pIngReq->pRecipy=this; + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + pIngReq->bCanMake[i]=false; + } + + for(j=0;j<9;j++) + { + pIngReq->uiGridA[j]=TempIngReq.uiGridA[j]; + } + + if(pIngReq->iIngC!=0) + { + memcpy(pIngReq->iIngIDA,TempIngReq.iIngIDA,sizeof(int)*TempIngReq.iIngC); + memcpy(pIngReq->iIngValA,TempIngReq.iIngValA,sizeof(int)*TempIngReq.iIngC); + memcpy(pIngReq->iIngAuxValA,TempIngReq.iIngAuxValA,sizeof(int)*TempIngReq.iIngC); + } + memcpy(pIngReq->uiGridA,TempIngReq.uiGridA,sizeof(unsigned int)*9); + + delete [] TempIngReq.iIngIDA; + delete [] TempIngReq.iIngValA; + delete [] TempIngReq.iIngAuxValA; + delete [] TempIngReq.uiGridA; +} + +ShapedRecipy *ShapedRecipy::keepTag() +{ + _keepTag = true; + return this; +} \ No newline at end of file diff --git a/Minecraft.World/ShapedRecipy.h b/Minecraft.World/ShapedRecipy.h new file mode 100644 index 00000000..56cae3fd --- /dev/null +++ b/Minecraft.World/ShapedRecipy.h @@ -0,0 +1,32 @@ +#pragma once + +class ShapedRecipy : public Recipy +{ +private: + int width, height, group; + ItemInstance **recipeItems; + ItemInstance *result; + bool _keepTag; +public: + const int resultId; + +public: + ShapedRecipy(int width, int height, ItemInstance **recipeItems, ItemInstance *result, int iGroup=Recipy::eGroupType_Decoration); + + virtual const ItemInstance *getResultItem(); + virtual const int getGroup(); + virtual bool matches(shared_ptr craftSlots, Level *level); + +private: + bool matches(shared_ptr craftSlots, int xOffs, int yOffs, bool xFlip); + +public: + virtual shared_ptr assemble(shared_ptr craftSlots); + virtual int size(); + ShapedRecipy *keepTag(); + + // 4J-PB - to return the items required to make a recipe + virtual bool requires(int iRecipe); + virtual void requires(INGREDIENTS_REQUIRED *pIngReq); +}; + diff --git a/Minecraft.World/ShapelessRecipy.cpp b/Minecraft.World/ShapelessRecipy.cpp new file mode 100644 index 00000000..67ed0381 --- /dev/null +++ b/Minecraft.World/ShapelessRecipy.cpp @@ -0,0 +1,182 @@ +// package net.minecraft.world.item.crafting; +// +// import java.util.*; +// +// import net.minecraft.world.inventory.CraftingContainer; +// import net.minecraft.world.item.ItemInstance; +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.inventory.h" +#include "Tile.h" +#include "Recipy.h" +#include "Recipes.h" +#include "ShapelessRecipy.h" + +ShapelessRecipy::ShapelessRecipy(ItemInstance *result, vector *ingredients, _eGroupType egroup) : + result(result), + ingredients(ingredients), + group(egroup) +{ +} + +const int ShapelessRecipy::getGroup() +{ + return group; +} + +const ItemInstance *ShapelessRecipy::getResultItem() +{ + return result; +} + +bool ShapelessRecipy::matches(shared_ptr craftSlots, Level *level) +{ + vector tempList = *ingredients; + + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 3; x++) + { + shared_ptr item = craftSlots->getItem(x, y); + + if (item != NULL) + { + bool found = false; + + AUTO_VAR(citEnd, ingredients->end()); + for (AUTO_VAR(cit, ingredients->begin()); cit != citEnd; ++cit) + { + ItemInstance *ingredient = *cit; + if (item->id == ingredient->id && (ingredient->getAuxValue() == Recipes::ANY_AUX_VALUE || item->getAuxValue() == ingredient->getAuxValue())) + { + found = true; + AUTO_VAR( it, find(tempList.begin(), tempList.end(), ingredient ) ); + if(it != tempList.end() ) tempList.erase(it); + break; + } + } + + if (!found) + { + return false; + } + } + } + } + + return tempList.empty(); +} + +shared_ptr ShapelessRecipy::assemble(shared_ptr craftSlots) +{ + return result->copy(); +} + +int ShapelessRecipy::size() +{ + return (int)ingredients->size(); +} + +// 4J-PB +bool ShapelessRecipy::requires(int iRecipe) +{ + vector *tempList = new vector; + + *tempList=*ingredients; + + //printf("ShapelessRecipy %d\n",iRecipe); + + AUTO_VAR(citEnd, ingredients->end()); + int iCount=0; + for (vector::iterator ingredient = ingredients->begin(); ingredient != citEnd; ingredient++) + { + //printf("\tIngredient %d is %d\n",iCount++,(*ingredient)->id); + //if (item->id == (*ingredient)->id && ((*ingredient)->getAuxValue() == Recipes::ANY_AUX_VALUE || item->getAuxValue() == (*ingredient)->getAuxValue())) + tempList->erase(ingredient); + } + + delete tempList; + return false; +} + +void ShapelessRecipy::requires(INGREDIENTS_REQUIRED *pIngReq) +{ + int iCount=0; + bool bFound; + int j; + INGREDIENTS_REQUIRED TempIngReq; + + // shapeless doesn't have the 3x3 shape, but we'll just use this to store the ingredients anyway + TempIngReq.iIngC=0; + TempIngReq.iType = RECIPE_TYPE_2x2; // all the dyes can be made in a 2x2 + TempIngReq.uiGridA = new unsigned int [9]; + TempIngReq.iIngIDA= new int [3*3]; + TempIngReq.iIngValA = new int [3*3]; + TempIngReq.iIngAuxValA = new int [3*3]; + + ZeroMemory(TempIngReq.iIngIDA,sizeof(int)*9); + ZeroMemory(TempIngReq.iIngValA,sizeof(int)*9); + memset(TempIngReq.iIngAuxValA,Recipes::ANY_AUX_VALUE,sizeof(int)*9); + ZeroMemory(TempIngReq.uiGridA,sizeof(unsigned int)*9); + + AUTO_VAR(citEnd, ingredients->end()); + + for (vector::const_iterator ingredient = ingredients->begin(); ingredient != citEnd; ingredient++) + { + ItemInstance *expected = *ingredient; + + if (expected!=NULL) + { + int iAuxVal = (*ingredient)->getAuxValue(); + TempIngReq.uiGridA[iCount++]=expected->id | iAuxVal<<24; + // 4J-PB - put the ingredients in boxes 1,2,4,5 so we can see them in a 2x2 crafting screen + if(iCount==2) iCount=3; + bFound=false; + for(j=0;jid) && (iAuxVal == Recipes::ANY_AUX_VALUE || TempIngReq.iIngAuxValA[j] == iAuxVal)) + { + bFound= true; + break; + } + } + if(bFound) + { + TempIngReq.iIngValA[j]++; + } + else + { + TempIngReq.iIngIDA[TempIngReq.iIngC]=expected->id; + TempIngReq.iIngAuxValA[TempIngReq.iIngC]=iAuxVal; + TempIngReq.iIngValA[TempIngReq.iIngC++]++; + } + } + } + pIngReq->iIngIDA = new int [TempIngReq.iIngC]; + pIngReq->iIngValA = new int [TempIngReq.iIngC]; + pIngReq->iIngAuxValA = new int [TempIngReq.iIngC]; + pIngReq->uiGridA = new unsigned int [9]; + + pIngReq->pRecipy=this; + + for(unsigned int i = 0; i < XUSER_MAX_COUNT; ++i) + { + pIngReq->bCanMake[i]=false; + } + + pIngReq->iIngC=TempIngReq.iIngC; + pIngReq->iType=TempIngReq.iType; + + if(pIngReq->iIngC!=0) + { + memcpy(pIngReq->iIngIDA,TempIngReq.iIngIDA,sizeof(int)*TempIngReq.iIngC); + memcpy(pIngReq->iIngValA,TempIngReq.iIngValA,sizeof(int)*TempIngReq.iIngC); + memcpy(pIngReq->iIngAuxValA,TempIngReq.iIngAuxValA,sizeof(int)*TempIngReq.iIngC); + } + memcpy(pIngReq->uiGridA,TempIngReq.uiGridA,sizeof(unsigned int) *9); + + delete [] TempIngReq.iIngIDA; + delete [] TempIngReq.iIngValA; + delete [] TempIngReq.iIngAuxValA; + delete [] TempIngReq.uiGridA; +} \ No newline at end of file diff --git a/Minecraft.World/ShapelessRecipy.h b/Minecraft.World/ShapelessRecipy.h new file mode 100644 index 00000000..5f7f6076 --- /dev/null +++ b/Minecraft.World/ShapelessRecipy.h @@ -0,0 +1,23 @@ +#pragma once + +class ShapelessRecipy : public Recipy +{ +private: + _eGroupType group; + const ItemInstance *result; + vector *ingredients; + +public: + ShapelessRecipy(ItemInstance *result, vector *ingredients, _eGroupType egroup=Recipy::eGroupType_Decoration); + + virtual const ItemInstance *getResultItem(); + virtual const int getGroup(); + virtual bool matches(shared_ptr craftSlots, Level *level); + virtual shared_ptr assemble(shared_ptr craftSlots); + virtual int size(); + + // 4J-PB - to return the items required to make a recipe + virtual bool requires(int iRecipe); + virtual void requires(INGREDIENTS_REQUIRED *pIngReq); + +}; diff --git a/Minecraft.World/SharedConstants.cpp b/Minecraft.World/SharedConstants.cpp new file mode 100644 index 00000000..497822bc --- /dev/null +++ b/Minecraft.World/SharedConstants.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "File.h" +#include "InputOutputStream.h" +#include "SharedConstants.h" + +const wstring SharedConstants::VERSION_STRING = L"1.2.3"; +const bool SharedConstants::TEXTURE_LIGHTING = true; + +wstring SharedConstants::readAcceptableChars() +{ + // 4J-PB - I've added ã in (for Portuguese in bed string) and added the character at the same place in the default.png font + wstring result = L" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_'abcdefghijklmnopqrstuvwxyz{|}~ ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø×ƒáíóúñѪº¿®¬½¼¡«»ã"; +#if 0 // 4J - do we actually really need to get this from a file? + //try { + //BufferedReader br = new BufferedReader(new InputStreamReader(SharedConstants.class.getResourceAsStream("/font.txt"), "UTF-8")); + BufferedReader *br = new BufferedReader(new InputStreamReader( new FileInputStream( new File( wstring(L"/font.txt") ) ) ) ); + wstring line; + while ( !(line = br->readLine()).empty()) + { + if (!( wcscmp( &line[0], L"#") == 0 ) ) + { + result.append( line ); + } + } + br->close(); + //} catch (Exception e) { + // TODO 4J Stu - Exception handling? + //} +#endif + return result; +} + +bool SharedConstants::isAllowedChatCharacter(char ch) +{ + //return ch != '§' && (acceptableLetters.indexOf(ch) >= 0 || (int) ch > 32); + // 4J Unused + return true; +} + +wstring SharedConstants::acceptableLetters; + +void SharedConstants::staticCtor() +{ + acceptableLetters = readAcceptableChars(); +} + +const wchar_t SharedConstants::ILLEGAL_FILE_CHARACTERS[ILLEGAL_FILE_CHARACTERS_LENGTH] = +{ + // 4J-PB - corrected + L'/', L'\n', L'\r', L'\t', L'\0', L'\f', L'`', L'?', L'*', L'\\', L'<', L'>', L'|', L'\"', L':' +}; \ No newline at end of file diff --git a/Minecraft.World/SharedConstants.h b/Minecraft.World/SharedConstants.h new file mode 100644 index 00000000..bd6dae89 --- /dev/null +++ b/Minecraft.World/SharedConstants.h @@ -0,0 +1,32 @@ +#pragma once +using namespace std; +#include "Class.h" + +class SharedConstants +{ + public: + static void staticCtor(); + static const wstring VERSION_STRING; + static const int NETWORK_PROTOCOL_VERSION = 39; + + // NOT texture resolution. How many sub-blocks each block face is made up of. + // 4J Added for texture packs + static const int WORLD_RESOLUTION = 16; + + static bool isAllowedChatCharacter(char ch); + + private: + static wstring readAcceptableChars(); + + public: + static const int maxChatLength = 100; + static wstring acceptableLetters; + + static const int ILLEGAL_FILE_CHARACTERS_LENGTH = 15; + static const wchar_t ILLEGAL_FILE_CHARACTERS[ILLEGAL_FILE_CHARACTERS_LENGTH]; + + static const bool TEXTURE_LIGHTING; // 4J - change brought forward from 1.8.2 + static const int TICKS_PER_SECOND = 20; + + static const int FULLBRIGHT_LIGHTVALUE = 15 << 20 | 15 << 4; +}; \ No newline at end of file diff --git a/Minecraft.World/SharedKeyPacket.h b/Minecraft.World/SharedKeyPacket.h new file mode 100644 index 00000000..4f50ad5c --- /dev/null +++ b/Minecraft.World/SharedKeyPacket.h @@ -0,0 +1,63 @@ +#pragma once + +#include "Packet.h" + +class SharedKeyPacket : public Packet +{ +#if 0 + private byte[] keybytes = new byte[]{}; + private byte[] nonce = new byte[]{}; + + private SecretKey secretKey; + + public SharedKeyPacket() { + // Needed + } + + public SharedKeyPacket(final SecretKey secretKey, final PublicKey publicKey, final byte[] nonce) { + this.secretKey = secretKey; + this.keybytes = Crypt.encryptUsingKey(publicKey, secretKey.getEncoded()); + this.nonce = Crypt.encryptUsingKey(publicKey, nonce); + } + + @Override + public void read(DataInputStream dis) throws IOException { + keybytes = readBytes(dis); + nonce = readBytes(dis); + } + + @Override + public void write(DataOutputStream dos) throws IOException { + writeBytes(dos, keybytes); + writeBytes(dos, nonce); + } + + @Override + public void handle(PacketListener listener) { + listener.handleSharedKey(this); + } + + @Override + public int getEstimatedSize() { + return 2 + keybytes.length + 2 + nonce.length; + } + + public SecretKey getSecretKey(PrivateKey privateKey) { + if (privateKey == null) { + return secretKey; + } + return secretKey = Crypt.decryptByteToSecretKey(privateKey, keybytes); + } + + public SecretKey getSecretKey() { + return getSecretKey(null); + } + + public byte[] getNonce(PrivateKey privateKey) { + if (privateKey == null) { + return nonce; + } + return Crypt.decryptUsingKey(privateKey, nonce); + } +#endif +}; \ No newline at end of file diff --git a/Minecraft.World/ShearsItem.cpp b/Minecraft.World/ShearsItem.cpp new file mode 100644 index 00000000..7f4d8d51 --- /dev/null +++ b/Minecraft.World/ShearsItem.cpp @@ -0,0 +1,38 @@ +#include "stdafx.h" +#include "ShearsItem.h" +#include "Tile.h" +#include "net.minecraft.world.entity.h" + +ShearsItem::ShearsItem(int itemId) : Item(itemId) +{ + setMaxStackSize(1); + setMaxDamage(238); +} + +bool ShearsItem::mineBlock(shared_ptr itemInstance, Level *level, int tile, int x, int y, int z, shared_ptr owner) +{ + if (tile == Tile::leaves_Id || tile == Tile::web_Id || tile == Tile::tallgrass_Id || tile == Tile::vine_Id || tile == Tile::tripWire_Id) + { + itemInstance->hurt(1, owner); + return true; + } + return Item::mineBlock(itemInstance, level, tile, x, y, z, owner); +} + +bool ShearsItem::canDestroySpecial(Tile *tile) +{ + return tile->id == Tile::web_Id || tile->id == Tile::redStoneDust_Id || tile->id == Tile::tripWire_Id; +} + +float ShearsItem::getDestroySpeed(shared_ptr itemInstance, Tile *tile) +{ + if (tile->id == Tile::web_Id || tile->id == Tile::leaves_Id) + { + return 15; + } + if (tile->id == Tile::cloth_Id) + { + return 5; + } + return Item::getDestroySpeed(itemInstance, tile); +} \ No newline at end of file diff --git a/Minecraft.World/ShearsItem.h b/Minecraft.World/ShearsItem.h new file mode 100644 index 00000000..06078645 --- /dev/null +++ b/Minecraft.World/ShearsItem.h @@ -0,0 +1,13 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class ShearsItem : public Item +{ +public: + ShearsItem(int itemId); + virtual bool mineBlock(shared_ptr itemInstance, Level *level, int tile, int x, int y, int z, shared_ptr owner); + virtual bool canDestroySpecial(Tile *tile); + virtual float getDestroySpeed(shared_ptr itemInstance, Tile *tile); +}; \ No newline at end of file diff --git a/Minecraft.World/Sheep.cpp b/Minecraft.World/Sheep.cpp new file mode 100644 index 00000000..30eeefcd --- /dev/null +++ b/Minecraft.World/Sheep.cpp @@ -0,0 +1,337 @@ +using namespace std; + +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.crafting.h" +#include "net.minecraft.world.inventory.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.global.h" +#include "net.minecraft.world.entity.player.h" +#include "Sheep.h" +#include "..\Minecraft.Client\Textures.h" +#include "MobCategory.h" +#include "GenericStats.h" + +const float Sheep::COLOR[][3] = +{ + { 1.0f, 1.0f, 1.0f }, // white + { 0.85f, 0.5f, 0.2f }, // orange + { 0.7f, 0.3f, 0.85f }, // magenta + { 0.4f, 0.6f, 0.85f }, // light blue + { 0.9f, 0.9f, 0.2f }, // yellow + { 0.5f, 0.8f, 0.1f }, // light green + { 0.95f, 0.5f, 0.65f }, // pink + { 0.3f, 0.3f, 0.3f }, // gray + { 0.6f, 0.6f, 0.6f }, // silver + { 0.3f, 0.5f, 0.65f }, // cyan + { 0.5f, 0.25f, 0.7f }, // purple + { 0.2f, 0.3f, 0.7f }, // blue + { 0.4f, 0.3f, 0.2f }, // brown + { 0.4f, 0.5f, 0.2f }, // green + { 0.6f, 0.2f, 0.2f }, // red + { 0.1f, 0.1f, 0.1f }, // black +}; + +Sheep::Sheep(Level *level) : Animal( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_SHEEP; // 4J - was L"/mob/sheep.png"; + this->setSize(0.9f, 1.3f); + + eatAnimationTick = 0; + + eatTileGoal = new EatTileGoal(this); + + float walkSpeed = 0.23f; + getNavigation()->setAvoidWater(true); + goalSelector.addGoal(0, new FloatGoal(this)); + goalSelector.addGoal(1, new PanicGoal(this, 0.38f)); + goalSelector.addGoal(2, new BreedGoal(this, walkSpeed)); + goalSelector.addGoal(3, new TemptGoal(this, 0.25f, Item::wheat_Id, false)); + goalSelector.addGoal(4, new FollowParentGoal(this, 0.25f)); + goalSelector.addGoal(5, eatTileGoal, false); + goalSelector.addGoal(6, new RandomStrollGoal(this, walkSpeed)); + goalSelector.addGoal(7, new LookAtPlayerGoal(this, typeid(Player), 6)); + goalSelector.addGoal(8, new RandomLookAroundGoal(this)); + + container = shared_ptr(new CraftingContainer(new SheepContainer(), 2, 1)); + container->setItem(0, shared_ptr( new ItemInstance(Item::dye_powder, 1, 0))); + container->setItem(1, shared_ptr( new ItemInstance(Item::dye_powder, 1, 0))); +} + +bool Sheep::useNewAi() +{ + return true; +} + +void Sheep::newServerAiStep() +{ + eatAnimationTick = eatTileGoal->getEatAnimationTick(); + Animal::newServerAiStep(); +} + +void Sheep::aiStep() +{ + if (level->isClientSide) eatAnimationTick = max(0, eatAnimationTick - 1); + Animal::aiStep(); +} + +int Sheep::getMaxHealth() +{ + return 8; +} + +void Sheep::defineSynchedData() +{ + Animal::defineSynchedData(); + + // sheared and color share a byte + entityData->define(DATA_WOOL_ID, ((byte) 0)); //was new Byte((byte), 0) +} + +void Sheep::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + if(!isSheared()) + { + // killing a non-sheared sheep will drop a single block of cloth + spawnAtLocation(shared_ptr( new ItemInstance(Tile::cloth_Id, 1, getColor()) ), 0.0f); + } +} + +int Sheep::getDeathLoot() +{ + return Tile::cloth_Id; +} + +void Sheep::handleEntityEvent(byte id) +{ + if (id == EntityEvent::EAT_GRASS) + { + eatAnimationTick = EAT_ANIMATION_TICKS; + } + else + { + Animal::handleEntityEvent(id); + } +} + +float Sheep::getHeadEatPositionScale(float a) +{ + if (eatAnimationTick <= 0) + { + return 0; + } + if (eatAnimationTick >= 4 && eatAnimationTick <= (EAT_ANIMATION_TICKS - 4)) + { + return 1; + } + if (eatAnimationTick < 4) + { + return ((float) eatAnimationTick - a) / 4.0f; + } + return -((float) (eatAnimationTick - EAT_ANIMATION_TICKS) - a) / 4.0f; +} + +float Sheep::getHeadEatAngleScale(float a) +{ + if (eatAnimationTick > 4 && eatAnimationTick <= (EAT_ANIMATION_TICKS - 4)) + { + float scale = ((float) (eatAnimationTick - 4) - a) / (float) (EAT_ANIMATION_TICKS - 8); + return PI * .20f + PI * .07f * Mth::sin(scale * 28.7f); + } + if (eatAnimationTick > 0) + { + return PI * .20f; + } + return ((xRot / (180.0f / PI))); +} + +bool Sheep::interact(shared_ptr player) +{ + shared_ptr item = player->inventory->getSelected(); + + // 4J-JEV: Fix for #88212, + // Untrusted players shouldn't be able to sheer sheep. + if (!player->isAllowedToInteract( shared_from_this() )) + return false; //Animal::interact(player); + + if (item != NULL && item->id == Item::shears->id && !isSheared() && !isBaby()) + { + if (!level->isClientSide) + { + setSheared(true); + int count = 1 + random->nextInt(3); + for (int i = 0; i < count; i++) + { + shared_ptr ie = spawnAtLocation(shared_ptr( new ItemInstance(Tile::cloth_Id, 1, getColor()) ), 1.0f); + ie->yd += random->nextFloat() * 0.05f; + ie->xd += (random->nextFloat() - random->nextFloat()) * 0.1f; + ie->zd += (random->nextFloat() - random->nextFloat()) * 0.1f; + } + + player->awardStat( GenericStats::shearedEntity(eTYPE_SHEEP), GenericStats::param_shearedEntity(eTYPE_SHEEP) ); + } + item->hurt(1, player); + } + + return Animal::interact(player); +} + +void Sheep::addAdditonalSaveData(CompoundTag *tag) +{ + Animal::addAdditonalSaveData(tag); + tag->putBoolean(L"Sheared", isSheared()); + tag->putByte(L"Color", (byte) getColor()); +} + +void Sheep::readAdditionalSaveData(CompoundTag *tag) +{ + Animal::readAdditionalSaveData(tag); + setSheared(tag->getBoolean(L"Sheared")); + setColor((int) tag->getByte(L"Color")); +} + +int Sheep::getAmbientSound() +{ + return eSoundType_MOB_SHEEP_AMBIENT; +} + +int Sheep::getHurtSound() +{ + return eSoundType_MOB_SHEEP_AMBIENT; +} + +int Sheep::getDeathSound() +{ + return eSoundType_MOB_SHEEP_AMBIENT; +} + +int Sheep::getColor() +{ + return (entityData->getByte(DATA_WOOL_ID) & 0x0f); +} + +void Sheep::setColor(int color) +{ + byte current = entityData->getByte(DATA_WOOL_ID); + entityData->set(DATA_WOOL_ID, (byte) ((current & 0xf0) | (color & 0x0f))); +} + +bool Sheep::isSheared() +{ + return (entityData->getByte(DATA_WOOL_ID) & 0x10) != 0; +} + +void Sheep::setSheared(bool value) +{ + byte current = entityData->getByte(DATA_WOOL_ID); + if (value) + { + entityData->set(DATA_WOOL_ID, (byte) (current | 0x10)); + } + else + { + entityData->set(DATA_WOOL_ID, (byte) (current & ~0x10)); + } +} + +int Sheep::getSheepColor(Random *random) +{ + int nextInt = random->nextInt(100); + if (nextInt < 5) + { + return 15 - DyePowderItem::BLACK; + } + if (nextInt < 10) + { + return 15 - DyePowderItem::GRAY; + } + if (nextInt < 15) + { + return 15 - DyePowderItem::SILVER; + } + if (nextInt < 18) + { + return 15 - DyePowderItem::BROWN; + } + if (random->nextInt(500) == 0) return 15 - DyePowderItem::PINK; + return 0; // white +} + +shared_ptr Sheep::getBreedOffspring(shared_ptr target) +{ + // 4J - added limit to number of animals that can be bred + if( level->canCreateMore( GetType(), Level::eSpawnType_Breed) ) + { + shared_ptr otherSheep = dynamic_pointer_cast( target ); + shared_ptr sheep = shared_ptr( new Sheep(level) ); + int color = getOffspringColor(dynamic_pointer_cast(shared_from_this()), otherSheep); + sheep->setColor(15 - color); + return sheep; + } + else + { + return nullptr; + } +} + +void Sheep::ate() +{ + setSheared(false); + if (isBaby()) + { + // remove a minute from aging + int age = getAge() + SharedConstants::TICKS_PER_SECOND * 60; + if (age > 0) + { + age = 0; + } + setAge(age); + } +} + +void Sheep::finalizeMobSpawn() +{ + setColor(Sheep::getSheepColor(level->random)); +} + +int Sheep::getOffspringColor(shared_ptr animal, shared_ptr partner) +{ + int parent1DyeColor = getDyeColor(animal); + int parent2DyeColor = getDyeColor(partner); + + container->getItem(0)->setAuxValue(parent1DyeColor); + container->getItem(1)->setAuxValue(parent2DyeColor); + + shared_ptr instance = Recipes::getInstance()->getItemFor(container, animal->level); + + int color = 0; + if (instance != NULL && instance->getItem()->id == Item::dye_powder_Id) + { + color = instance->getAuxValue(); + } + else + { + color = level->random->nextBoolean() ? parent1DyeColor : parent2DyeColor; + } + return color; +} + +int Sheep::getDyeColor(shared_ptr animal) +{ + return 15 - dynamic_pointer_cast(animal)->getColor(); +} diff --git a/Minecraft.World/Sheep.h b/Minecraft.World/Sheep.h new file mode 100644 index 00000000..666e1d57 --- /dev/null +++ b/Minecraft.World/Sheep.h @@ -0,0 +1,86 @@ +#pragma once + +using namespace std; + +class Random; +class EatTileGoal; +class CraftingContainer; + +#include "Animal.h" +#include "SharedConstants.h" +#include "AbstractContainerMenu.h" + +class Sheep : public Animal +{ +private: + class SheepContainer : public AbstractContainerMenu + { + bool stillValid(shared_ptr player) { return false; } + }; + + shared_ptr container; +public: + eINSTANCEOF GetType() { return eTYPE_SHEEP; } + static Entity *create(Level *level) { return new Sheep(level); } + +private: + static const int EAT_ANIMATION_TICKS = SharedConstants::TICKS_PER_SECOND * 2; + static const int DATA_WOOL_ID = 16; + + int eatAnimationTick; + EatTileGoal *eatTileGoal; + +public: + static const float COLOR[][3]; + +public: + Sheep(Level *level); + +protected: + virtual bool useNewAi(); + virtual void newServerAiStep(); + +public: + void aiStep(); + virtual int getMaxHealth(); + +protected: + virtual void defineSynchedData(); + +public: + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + virtual int getDeathLoot(); + +public: + virtual void handleEntityEvent(byte id); + +public: + float getHeadEatPositionScale(float a); + float getHeadEatAngleScale(float a); + + virtual bool interact(shared_ptr player); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +protected: + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + +public: + int getColor(); + void setColor(int color); + bool isSheared(); + void setSheared(bool value); + + static int getSheepColor(Random *random); + virtual shared_ptr getBreedOffspring(shared_ptr target); + + virtual void ate(); + + void finalizeMobSpawn(); + +private: + int getOffspringColor(shared_ptr animal, shared_ptr partner); + int getDyeColor(shared_ptr animal); +}; diff --git a/Minecraft.World/ShoreLayer.cpp b/Minecraft.World/ShoreLayer.cpp new file mode 100644 index 00000000..297571f5 --- /dev/null +++ b/Minecraft.World/ShoreLayer.cpp @@ -0,0 +1,74 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" +#include "net.minecraft.world.level.biome.h" + +ShoreLayer::ShoreLayer(__int64 seed, shared_ptr parent) : Layer(seed) +{ + this->parent = parent; +} + +intArray ShoreLayer::getArea(int xo, int yo, int w, int h) +{ + intArray b = parent->getArea(xo - 1, yo - 1, w + 2, h + 2); + + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + initRandom(x + xo, y + yo); + int old = b[(x + 1) + (y + 1) * (w + 2)]; + if (old == Biome::mushroomIsland->id) + { + int _n = b[(x + 1) + (y + 1 - 1) * (w + 2)]; + int _e = b[(x + 1 + 1) + (y + 1) * (w + 2)]; + int _w = b[(x + 1 - 1) + (y + 1) * (w + 2)]; + int _s = b[(x + 1) + (y + 1 + 1) * (w + 2)]; + if (_n == Biome::ocean->id || _e == Biome::ocean->id || _w == Biome::ocean->id || _s == Biome::ocean->id) + { + result[x + y * w] = Biome::mushroomIslandShore->id; + } + else + { + result[x + y * w] = old; + } + } + else if (old != Biome::ocean->id && old != Biome::river->id && old != Biome::swampland->id && old != Biome::extremeHills->id) + { + int _n = b[(x + 1) + (y + 1 - 1) * (w + 2)]; + int _e = b[(x + 1 + 1) + (y + 1) * (w + 2)]; + int _w = b[(x + 1 - 1) + (y + 1) * (w + 2)]; + int _s = b[(x + 1) + (y + 1 + 1) * (w + 2)]; + if (_n == Biome::ocean->id || _e == Biome::ocean->id || _w == Biome::ocean->id || _s == Biome::ocean->id) + { + result[x + y * w] = Biome::beaches->id; + } + else + { + result[x + y * w] = old; + } + } + else if (old == Biome::extremeHills->id) + { + int _n = b[(x + 1) + (y + 1 - 1) * (w + 2)]; + int _e = b[(x + 1 + 1) + (y + 1) * (w + 2)]; + int _w = b[(x + 1 - 1) + (y + 1) * (w + 2)]; + int _s = b[(x + 1) + (y + 1 + 1) * (w + 2)]; + if (_n != Biome::extremeHills->id || _e != Biome::extremeHills->id || _w != Biome::extremeHills->id || _s != Biome::extremeHills->id) + { + result[x + y * w] = Biome::smallerExtremeHills->id; + } + else + { + result[x + y * w] = old; + } + } + else + { + result[x + y * w] = old; + } + } + } + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/ShoreLayer.h b/Minecraft.World/ShoreLayer.h new file mode 100644 index 00000000..96aea152 --- /dev/null +++ b/Minecraft.World/ShoreLayer.h @@ -0,0 +1,9 @@ +#pragma once +#include "Layer.h" + +class ShoreLayer : public Layer +{ +public: + ShoreLayer(__int64 seed, shared_ptr parent); + virtual intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/ShortTag.h b/Minecraft.World/ShortTag.h new file mode 100644 index 00000000..bfd7511b --- /dev/null +++ b/Minecraft.World/ShortTag.h @@ -0,0 +1,36 @@ +#pragma once +#include "Tag.h" + +class ShortTag : public Tag +{ +public: + short data; + ShortTag(const wstring &name) : Tag(name) {} + ShortTag(const wstring &name, int data) : Tag(name) {this->data = data; } + + void write(DataOutput *dos) { dos->writeShort(data); } + void load(DataInput *dis) { data = dis->readShort(); } + + byte getId() { return TAG_Short; } + wstring toString() + { + static wchar_t buf[32]; + swprintf(buf,32,L"%d",data); + return wstring( buf ); + } + + Tag *copy() + { + return new ShortTag(getName(), data); + } + + bool equals(Tag *obj) + { + if (Tag::equals(obj)) + { + ShortTag *o = (ShortTag *) obj; + return data == o->data; + } + return false; + } +}; \ No newline at end of file diff --git a/Minecraft.World/ShovelItem.cpp b/Minecraft.World/ShovelItem.cpp new file mode 100644 index 00000000..79981bd1 --- /dev/null +++ b/Minecraft.World/ShovelItem.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" + +#include "net.minecraft.world.level.tile.h" +#include "ShovelItem.h" + +TileArray *ShovelItem::diggables = NULL; + +void ShovelItem::staticCtor() +{ + ShovelItem::diggables = new TileArray( SHOVEL_DIGGABLES); + diggables->data[0] = Tile::grass; + diggables->data[1] = Tile::dirt; + diggables->data[2] = Tile::sand; + diggables->data[3] = Tile::gravel; + diggables->data[4] = Tile::topSnow; + diggables->data[5] = Tile::snow; + diggables->data[6] = Tile::clay; + diggables->data[7] = Tile::farmland; + // 4J - brought forward from 1.2.3 + diggables->data[8] = Tile::hellSand; + diggables->data[9] = Tile::mycel; +} + +ShovelItem::ShovelItem(int id, const Tier *tier) : DiggerItem(id, 1, tier, diggables) +{ +} + +bool ShovelItem::canDestroySpecial(Tile *tile) +{ + if (tile == Tile::topSnow) return true; + if (tile == Tile::snow) return true; + return false; +} \ No newline at end of file diff --git a/Minecraft.World/ShovelItem.h b/Minecraft.World/ShovelItem.h new file mode 100644 index 00000000..c9c42240 --- /dev/null +++ b/Minecraft.World/ShovelItem.h @@ -0,0 +1,15 @@ +#pragma once +#include "DiggerItem.h" + +#define SHOVEL_DIGGABLES 10 +class ShovelItem : public DiggerItem +{ +private: + static TileArray *diggables; + +public: + static void staticCtor(); + ShovelItem(int id, const Tier *tier); + + bool canDestroySpecial(Tile *tile); +}; \ No newline at end of file diff --git a/Minecraft.World/SignItem.cpp b/Minecraft.World/SignItem.cpp new file mode 100644 index 00000000..02ccf83d --- /dev/null +++ b/Minecraft.World/SignItem.cpp @@ -0,0 +1,59 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "ItemInstance.h" +#include "SignItem.h" +#include "GenericStats.h" + +SignItem::SignItem(int id) : Item(id) +{ + // 4J-PB - Changed for TU9 + maxStackSize = 16; +} + +bool SignItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + // 4J-PB - Adding a test only version to allow tooltips to be displayed + if (face == 0) return false; + if (!level->getMaterial(x, y, z)->isSolid()) return false; + + + if (face == 1) y++; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + + if (!player->mayBuild(x, y, z)) return false; + + if (!Tile::sign->mayPlace(level, x, y, z)) return false; + + if(!bTestUseOnOnly) + { + if (face == 1) + { + int rot = Mth::floor(((player->yRot + 180) * 16) / 360 + 0.5) & 15; + level->setTileAndData(x, y, z, Tile::sign_Id, rot); + } + else + { + level->setTileAndData(x, y, z, Tile::wallSign_Id, face); + } + + instance->count--; + shared_ptr ste = dynamic_pointer_cast( level->getTileEntity(x, y, z) ); + if (ste != NULL) player->openTextEdit(ste); + + // 4J-JEV: Hook for durango 'BlockPlaced' event. + player->awardStat( + GenericStats::blocksPlaced((face==1) ? Tile::sign_Id : Tile::wallSign_Id), + GenericStats::param_blocksPlaced( + (face==1) ? Tile::sign_Id : Tile::wallSign_Id, + instance->getAuxValue(), + 1) + ); + } + return true; +} diff --git a/Minecraft.World/SignItem.h b/Minecraft.World/SignItem.h new file mode 100644 index 00000000..6265e759 --- /dev/null +++ b/Minecraft.World/SignItem.h @@ -0,0 +1,12 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class SignItem : public Item +{ +public: + SignItem(int id); + + virtual bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); +}; \ No newline at end of file diff --git a/Minecraft.World/SignTile.cpp b/Minecraft.World/SignTile.cpp new file mode 100644 index 00000000..52dc1260 --- /dev/null +++ b/Minecraft.World/SignTile.cpp @@ -0,0 +1,129 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "Material.h" +#include "SignTileEntity.h" +#include "SignTile.h" + +SignTile::SignTile(int id, eINSTANCEOF clas, bool onGround) : EntityTile(id, Material::wood, isSolidRender()) +{ + this->onGround = onGround; + this->clas = clas; + updateDefaultShape(); +} + +Icon *SignTile::getTexture(int face, int data) +{ + return Tile::wood->getTexture(face); +} + +void SignTile::updateDefaultShape() +{ + float r = 4 / 16.0f; + float h = 16 / 16.0f; + this->setShape(0.5f - r, 0, 0.5f - r, 0.5f + r, h, 0.5f + r); +} + +AABB *SignTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +AABB *SignTile::getTileAABB(Level *level, int x, int y, int z) +{ + updateShape(level, x, y, z); + return EntityTile::getTileAABB(level, x, y, z); +} + +void SignTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + if (onGround) return; + + int face = level->getData(x, y, z); + + float h0 = (4 + 0.5f) / 16.0f; + float h1 = (12 + 0.5f) / 16.0f; + float w0 = 0 / 16.0f; + float w1 = 16 / 16.0f; + + float d0 = 2 / 16.0f; + + setShape(0, 0, 0, 1, 1, 1); + if (face == 2) setShape(w0, h0, 1 - d0, w1, h1, 1); + if (face == 3) setShape(w0, h0, 0, w1, h1, d0); + if (face == 4) setShape(1 - d0, h0, w0, 1, h1, w1); + if (face == 5) setShape(0, h0, w0, d0, h1, w1); +} + +int SignTile::getRenderShape() +{ + return Tile::SHAPE_INVISIBLE; +} + +bool SignTile::isCubeShaped() +{ + return false; +} + +bool SignTile::isPathfindable(LevelSource *level, int x, int y, int z) +{ + return true; +} + +bool SignTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +shared_ptr SignTile::newTileEntity(Level *level) +{ + //try { + // 4J Stu - For some reason the newInstance wasn't working right, but doing it like the other TileEntities is fine + return shared_ptr( new SignTileEntity() ); + //return dynamic_pointer_cast( clas->newInstance() ); + //} catch (Exception e) { + // TODO 4J Stu - Exception handling + // throw new RuntimeException(e); + //} +} + +int SignTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::sign->id; +} + +void SignTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + bool remove = false; + + if (onGround) + { + if (!level->getMaterial(x, y - 1, z)->isSolid()) remove = true; + } + else + { + int face = level->getData(x, y, z); + remove = true; + if (face == 2 && level->getMaterial(x, y, z + 1)->isSolid()) remove = false; + if (face == 3 && level->getMaterial(x, y, z - 1)->isSolid()) remove = false; + if (face == 4 && level->getMaterial(x + 1, y, z)->isSolid()) remove = false; + if (face == 5 && level->getMaterial(x - 1, y, z)->isSolid()) remove = false; + } + if (remove) + { + spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } + + EntityTile::neighborChanged(level, x, y, z, type); +} + +int SignTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Item::sign_Id; +} + +void SignTile::registerIcons(IconRegister *iconRegister) +{ + // None +} \ No newline at end of file diff --git a/Minecraft.World/SignTile.h b/Minecraft.World/SignTile.h new file mode 100644 index 00000000..92163973 --- /dev/null +++ b/Minecraft.World/SignTile.h @@ -0,0 +1,38 @@ +#pragma once + +#include "EntityTile.h" +#include "TileEntity.h" + +#include "stdafx.h" +#include "Material.h" + +class SignTile : public EntityTile +{ + friend class Tile; +private: + eINSTANCEOF clas; + bool onGround; + +protected: + SignTile(int id, eINSTANCEOF clas, bool onGround); + +public: + Icon *getTexture(int face, int data); + virtual void updateDefaultShape(); + AABB *getAABB(Level *level, int x, int y, int z); + AABB *getTileAABB(Level *level, int x, int y, int z); + void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + int getRenderShape(); + bool isCubeShaped(); + virtual bool isPathfindable(LevelSource *level, int x, int y, int z); + bool isSolidRender(bool isServerLevel = false); + +protected: + shared_ptr newTileEntity(Level *level); + +public: + int getResource(int data, Random *random, int playerBonusLevel); + void neighborChanged(Level *level, int x, int y, int z, int type); + int cloneTileId(Level *level, int x, int y, int z); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/SignTileEntity.cpp b/Minecraft.World/SignTileEntity.cpp new file mode 100644 index 00000000..09b8e022 --- /dev/null +++ b/Minecraft.World/SignTileEntity.cpp @@ -0,0 +1,198 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.network.packet.h" +#include "SignTileEntity.h" +#include +#include "..\Minecraft.Client\ClientConnection.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\ServerLevel.h" +#include "..\Minecraft.World\Level.h" + + + +const int SignTileEntity::MAX_LINE_LENGTH = 15; + +SignTileEntity::SignTileEntity() : TileEntity() +{ + m_wsmessages[0] = L""; + m_wsmessages[1] = L""; + m_wsmessages[2] = L""; + m_wsmessages[3] = L""; + m_bVerified=true; + m_bCensored=false; + + m_iSelectedLine = -1; + + _isEditable = true; +} + +SignTileEntity::~SignTileEntity() +{ + // TODO ORBIS_STUBBED; +#ifndef __ORBIS__ + // 4J-PB - we don't need to verify strings anymore - InputManager.CancelQueuedVerifyStrings(&SignTileEntity::StringVerifyCallback,(LPVOID)this); +#endif +} + +void SignTileEntity::save(CompoundTag *tag) +{ + TileEntity::save(tag); + tag->putString(L"Text1", m_wsmessages[0] ); + tag->putString(L"Text2", m_wsmessages[1] ); + tag->putString(L"Text3", m_wsmessages[2] ); + tag->putString(L"Text4", m_wsmessages[3] ); +#ifndef _CONTENT_PACKAGE + OutputDebugStringW(L"### - Saving a sign with text - \n"); + for(int i=0;i<4;i++) + { + OutputDebugStringW(m_wsmessages[i].c_str()); + OutputDebugStringW(L"\n"); + } +#endif +} + +void SignTileEntity::load(CompoundTag *tag) +{ + _isEditable = false; + TileEntity::load(tag); + for (int i = 0; i < MAX_SIGN_LINES; i++) + { + wchar_t *buf = new wchar_t[256]; + swprintf(buf, 256, L"Text%d", (i+1) ); + m_wsmessages[i] = tag->getString( buf ); + if (m_wsmessages[i].length() > MAX_LINE_LENGTH) m_wsmessages[i] = m_wsmessages[i].substr(0, MAX_LINE_LENGTH); + } +#ifndef _CONTENT_PACKAGE + OutputDebugStringW(L"### - Loaded a sign with text - \n"); + for(int i=0;i<4;i++) + { + OutputDebugStringW(m_wsmessages[i].c_str()); + OutputDebugStringW(L"\n"); + } +#endif + + // 4J Stu - Fix for #13531 - Bug: Signs do not Censor after loading a save + // Set verified as false so that it can be re-verified + m_bVerified=false; + + setChanged(); +} + +shared_ptr SignTileEntity::getUpdatePacket() +{ + wstring copy[MAX_SIGN_LINES]; + for (int i = 0; i < MAX_SIGN_LINES; i++) + { + copy[i] = m_wsmessages[i]; + } + return shared_ptr( new SignUpdatePacket(x, y, z, m_bVerified, m_bCensored, copy) ); +} + +bool SignTileEntity::isEditable() +{ + return _isEditable; +} + +void SignTileEntity::setEditable(bool isEditable) +{ + this->_isEditable = isEditable; +} + +void SignTileEntity::setChanged() +{ + Minecraft *pMinecraft=Minecraft::GetInstance(); + + // 4J-PB - For TU14 we are allowed to not verify strings anymore ! + m_bVerified=true; + /* + if(!g_NetworkManager.IsLocalGame() && !m_bVerified) + //if (pMinecraft->level->isClientSide) + { + WCHAR *wcMessages[MAX_SIGN_LINES]; + for (int i = 0; i < MAX_SIGN_LINES; ++i) + { + wcMessages[i]=new WCHAR [MAX_LINE_LENGTH+1]; + ZeroMemory(wcMessages[i],sizeof(WCHAR)*(MAX_LINE_LENGTH+1)); + if(m_wsmessages[i].length()>0) + { + memcpy(wcMessages[i],m_wsmessages[i].c_str(),m_wsmessages[i].length()*sizeof(WCHAR)); + } + } + // at this point, we can ask the online string verifier if our sign text is ok +#ifdef __ORBIS__ + m_bVerified=true; +#else + + if(!InputManager.VerifyStrings((WCHAR**)&wcMessages,MAX_SIGN_LINES,&SignTileEntity::StringVerifyCallback,(LPVOID)this)) + { + // Nothing to verify + m_bVerified=true; + } + for(unsigned int i = 0; i < MAX_SIGN_LINES; ++i) + { + delete [] wcMessages[i]; + } +#endif + } + else + { + // set the sign to allowed (local game) + m_bVerified=true; + } + */ +} + + +void SignTileEntity::SetMessage(int iIndex,wstring &wsText) +{ + m_wsmessages[iIndex]=wsText; + +} + +// 4J-PB - added for string verification +int SignTileEntity::StringVerifyCallback(LPVOID lpParam,STRING_VERIFY_RESPONSE *pResults) +{ + // results will be in m_pStringVerifyResponse + SignTileEntity *pClass=(SignTileEntity *)lpParam; + + pClass->m_bVerified=true; + pClass->m_bCensored=false; + for(int i=0;iwNumStrings;i++) + { + if(pResults->pStringResult[i]!=ERROR_SUCCESS) + { + pClass->m_bCensored=true; + } + } + + if(!pClass->level->isClientSide) + { + ServerLevel *serverLevel = (ServerLevel *)pClass->level; + // 4J Stu - This callback gets called on the main thread, but tried to access things on the server thread. Change to go through the protected method. + //pClass->level->sendTileUpdated(pClass->x, pClass->y, pClass->z); + serverLevel->queueSendTileUpdate(pClass->x, pClass->y, pClass->z); + } + + return 0; +} + +// 4J Added +shared_ptr SignTileEntity::clone() +{ + shared_ptr result = shared_ptr( new SignTileEntity() ); + TileEntity::clone(result); + + result->m_wsmessages[0] = m_wsmessages[0]; + result->m_wsmessages[1] = m_wsmessages[1]; + result->m_wsmessages[2] = m_wsmessages[2]; + result->m_wsmessages[3] = m_wsmessages[3]; + result->m_bVerified = m_bVerified; + result->m_bCensored = m_bCensored; + return result; +} \ No newline at end of file diff --git a/Minecraft.World/SignTileEntity.h b/Minecraft.World/SignTileEntity.h new file mode 100644 index 00000000..b963f12f --- /dev/null +++ b/Minecraft.World/SignTileEntity.h @@ -0,0 +1,49 @@ +#pragma once +using namespace std; + +#include "TileEntity.h" + +#define MAX_SIGN_LINES 4 + +class SignTileEntity : public TileEntity +{ +public: + eINSTANCEOF GetType() { return eTYPE_SIGNTILEENTITY; } + static TileEntity *create() { return new SignTileEntity(); } +public: + static const int MAX_LINE_LENGTH; + +public: + SignTileEntity(); + virtual ~SignTileEntity(); + wstring GetMessage(int i) { return m_wsmessages[i];} + wstring *GetMessages() { return m_wsmessages;} + void SetMessage(int iIndex,wstring &wsText); + int GetSelectedLine() {return m_iSelectedLine;} + void SetSelectedLine(int iLine) {m_iSelectedLine=iLine;} + bool IsVerified() {return m_bVerified;} + void SetVerified(bool bVerified) {m_bVerified=bVerified;} + bool IsCensored() {return m_bCensored;} + void SetCensored(bool bCensored) {m_bCensored=bCensored;} +public: + +private: + bool _isEditable; + bool m_bVerified; + bool m_bCensored; + int m_iSelectedLine; + + wstring m_wsmessages[MAX_SIGN_LINES]; + +public: + virtual void save(CompoundTag *tag); + virtual void load(CompoundTag *tag); + virtual shared_ptr getUpdatePacket(); + bool isEditable(); + void setEditable(bool isEditable); + virtual void setChanged(); + static int StringVerifyCallback(LPVOID lpParam,STRING_VERIFY_RESPONSE *pResults); + + // 4J Added + virtual shared_ptr clone(); +}; \ No newline at end of file diff --git a/Minecraft.World/SignUpdatePacket.cpp b/Minecraft.World/SignUpdatePacket.cpp new file mode 100644 index 00000000..7b3a3217 --- /dev/null +++ b/Minecraft.World/SignUpdatePacket.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "SignUpdatePacket.h" + + + +SignUpdatePacket::SignUpdatePacket() +{ + shouldDelay = true; + m_bVerified=false; + m_bCensored=false; + x = 0; + y = 0; + z = 0; + +} + +SignUpdatePacket::SignUpdatePacket(int x, int y, int z, bool bVerified, bool bCensored, wstring lines[]) +{ + shouldDelay = true; + this->m_bVerified=bVerified; + this->m_bCensored=bCensored; + this->x = x; + this->y = y; + this->z = z; + for( int i = 0; i < MAX_SIGN_LINES; i++ ) this->lines[i] = lines[i]; +} + +void SignUpdatePacket::read(DataInputStream *dis) //throws IOException +{ + x = dis->readInt(); + y = dis->readShort(); + z = dis->readInt(); + this->m_bVerified=dis->readBoolean(); + this->m_bCensored=dis->readBoolean();; + for (int i = 0; i < MAX_SIGN_LINES; i++) + lines[i] = readUtf(dis, SignTileEntity::MAX_LINE_LENGTH); +} + +void SignUpdatePacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(x); + dos->writeShort(y); + dos->writeInt(z); + dos->writeBoolean(m_bVerified); + dos->writeBoolean(m_bCensored); + for (int i = 0; i < MAX_SIGN_LINES; i++) + writeUtf(lines[i], dos); +} + +void SignUpdatePacket::handle(PacketListener *listener) +{ + listener->handleSignUpdate(shared_from_this()); +} + +int SignUpdatePacket::getEstimatedSize() +{ + int l = 0; + l+=sizeof(int); + l+=sizeof(short); + l+=sizeof(int); + l+=sizeof(byte); + l+=sizeof(byte); + + for (int i = 0; i < MAX_SIGN_LINES; i++) + l += (int)lines[i].length(); + return l; +} diff --git a/Minecraft.World/SignUpdatePacket.h b/Minecraft.World/SignUpdatePacket.h new file mode 100644 index 00000000..80cc2968 --- /dev/null +++ b/Minecraft.World/SignUpdatePacket.h @@ -0,0 +1,26 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class SignUpdatePacket : public Packet, public enable_shared_from_this +{ +public: + int x, y, z; + bool m_bVerified; + bool m_bCensored; + wstring lines[4]; + + SignUpdatePacket(); + SignUpdatePacket(int x, int y, int z, bool bVerified, bool bCensored, wstring lines[]); + bool GetVerified() {return m_bVerified;} + bool GetCensored() {return m_bCensored;} + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new SignUpdatePacket()); } + virtual int getId() { return 130; } +}; \ No newline at end of file diff --git a/Minecraft.World/Silverfish.cpp b/Minecraft.World/Silverfish.cpp new file mode 100644 index 00000000..117dc41b --- /dev/null +++ b/Minecraft.World/Silverfish.cpp @@ -0,0 +1,211 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.h" +#include "..\Minecraft.Client\Textures.h" +#include "Silverfish.h" +#include "SoundTypes.h" + + + +Silverfish::Silverfish(Level *level) : Monster( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_SILVERFISH;// 4J was "/mob/silverfish.png"; + this->setSize(0.3f, 0.7f); + runSpeed = 0.6f; + + // 4J - Brought forward damage from 1.2.3 + attackDamage = 1; +} + +int Silverfish::getMaxHealth() +{ + return 8; +} + +bool Silverfish::makeStepSound() +{ + return false; +} + +shared_ptr Silverfish::findAttackTarget() +{ +#ifndef _FINAL_BUILD + if(app.GetMobsDontAttackEnabled()) + { + return shared_ptr(); + } +#endif + + double maxDist = 8; + return level->getNearestAttackablePlayer(shared_from_this(), maxDist); +} + +int Silverfish::getAmbientSound() +{ + // 4J - brought sound change forward from 1.2.3 + return eSoundType_MOB_SILVERFISH_AMBIENT; +} + +int Silverfish::getHurtSound() +{ + // 4J - brought sound change forward from 1.2.3 + return eSoundType_MOB_SILVERFISH_HURT; +} + +int Silverfish::getDeathSound() +{ + // 4J - brought sound change forward from 1.2.3 + return eSoundType_MOB_SILVERFISH_DEATH; +} + + +bool Silverfish::hurt(DamageSource *source, int dmg) +{ + if (lookForFriends <= 0 && (dynamic_cast(source) != NULL || source == DamageSource::magic)) + { + // look for friends + lookForFriends = 20; + } + return Monster::hurt(source, dmg); +} + +void Silverfish::checkHurtTarget(shared_ptr target, float d) +{ + + // super.checkHurtTarget(target, d); + if (attackTime <= 0 && d < 1.2f && target->bb->y1 > bb->y0 && target->bb->y0 < bb->y1) + { + attackTime = 20; + DamageSource *damageSource = DamageSource::mobAttack( dynamic_pointer_cast( shared_from_this() ) ); + target->hurt(damageSource, attackDamage); + delete damageSource; + } + +} + +void Silverfish::playStepSound(int xt, int yt, int zt, int t) +{ + level->playSound(shared_from_this(), eSoundType_MOB_SILVERFISH_STEP, 1, 1); +} + +int Silverfish::getDeathLoot() +{ + return 0; +} + +void Silverfish::tick() +{ + // rotate the whole body to the same angle as the head + yBodyRot = yRot; + + Monster::tick(); +} + +void Silverfish::serverAiStep() +{ + Monster::serverAiStep(); + + if (level->isClientSide) + { + return; + } + + if (lookForFriends > 0) + { + lookForFriends--; + if (lookForFriends == 0) + { + // see if there are any friendly monster eggs nearby + int baseX = Mth::floor(x); + int baseY = Mth::floor(y); + int baseZ = Mth::floor(z); + bool doBreak = false; + + for (int yOff = 0; !doBreak && yOff <= 5 && yOff >= -5; yOff = (yOff <= 0) ? 1 - yOff : 0 - yOff) + { + for (int xOff = 0; !doBreak && xOff <= 10 && xOff >= -10; xOff = (xOff <= 0) ? 1 - xOff : 0 - xOff) + { + for (int zOff = 0; !doBreak && zOff <= 10 && zOff >= -10; zOff = (zOff <= 0) ? 1 - zOff : 0 - zOff) + { + int tile = level->getTile(baseX + xOff, baseY + yOff, baseZ + zOff); + if (tile == Tile::monsterStoneEgg_Id) + { + level->levelEvent(LevelEvent::PARTICLES_DESTROY_BLOCK, baseX + xOff, baseY + yOff, baseZ + zOff, + Tile::monsterStoneEgg_Id + (level->getData(baseX + xOff, baseY + yOff, baseZ + zOff) << Tile::TILE_NUM_SHIFT)); + level->setTile(baseX + xOff, baseY + yOff, baseZ + zOff, 0); + Tile::monsterStoneEgg->destroy(level, baseX + xOff, baseY + yOff, baseZ + zOff, 0); + + if (random->nextBoolean()) + { + doBreak = true; + break; + } + } + } + } + } + } + } + + if (attackTarget == NULL && !isPathFinding()) + { + // if the silverfish isn't doing anything special, it will merge + // with any rock tile it is nearby + int tileX = Mth::floor(x), tileY = Mth::floor(y + .5f), tileZ = Mth::floor(z); + int facing = random->nextInt(6); + + int tile = level->getTile(tileX + Facing::STEP_X[facing], tileY + Facing::STEP_Y[facing], tileZ + Facing::STEP_Z[facing]); + if (StoneMonsterTile::isCompatibleHostBlock(tile)) + { + level->setTileAndData(tileX + Facing::STEP_X[facing], tileY + Facing::STEP_Y[facing], tileZ + Facing::STEP_Z[facing], Tile::monsterStoneEgg_Id, StoneMonsterTile::getDataForHostBlock(tile)); + spawnAnim(); + remove(); + } + else + { + findRandomStrollLocation(); + } + + } + else if (attackTarget != NULL && !isPathFinding()) + { + attackTarget = nullptr; + } +} + +float Silverfish::getWalkTargetValue(int x, int y, int z) +{ + // silverfish LOVES stone =) + if (level->getTile(x, y - 1, z) == Tile::rock_Id) return 10; + return Monster::getWalkTargetValue(x, y, z); +} + +bool Silverfish::isDarkEnoughToSpawn() +{ + return true; +} + +bool Silverfish::canSpawn() +{ + if (Monster::canSpawn()) + { + shared_ptr nearestPlayer = level->getNearestPlayer(shared_from_this(), 5.0); + return nearestPlayer == NULL; + } + return false; +} + +MobType Silverfish::getMobType() +{ + return ARTHROPOD; +} \ No newline at end of file diff --git a/Minecraft.World/Silverfish.h b/Minecraft.World/Silverfish.h new file mode 100644 index 00000000..0fd0cc06 --- /dev/null +++ b/Minecraft.World/Silverfish.h @@ -0,0 +1,49 @@ +#pragma once + +#include "Monster.h" + +class Silverfish : public Monster +{ +public: + eINSTANCEOF GetType() { return eTYPE_SILVERFISH; } + static Entity *create(Level *level) { return new Silverfish(level); } +private: + int lookForFriends; + +public: + Silverfish(Level *level); + + virtual int getMaxHealth(); + +protected: + virtual bool makeStepSound(); + virtual shared_ptr findAttackTarget(); + + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + +public: + virtual bool hurt(DamageSource *source, int dmg); + +protected: + virtual void checkHurtTarget(shared_ptr target, float d); + virtual void playStepSound(int xt, int yt, int zt, int t); + virtual int getDeathLoot(); + +public: + virtual void tick(); + +protected: + virtual void serverAiStep(); + +public: + virtual float getWalkTargetValue(int x, int y, int z); + +protected: + virtual bool isDarkEnoughToSpawn(); + +public: + virtual bool canSpawn(); + virtual MobType getMobType(); +}; \ No newline at end of file diff --git a/Minecraft.World/SimpleContainer.cpp b/Minecraft.World/SimpleContainer.cpp new file mode 100644 index 00000000..e29bc790 --- /dev/null +++ b/Minecraft.World/SimpleContainer.cpp @@ -0,0 +1,110 @@ +#include "stdafx.h" + +#include "net.minecraft.world.ContainerListener.h" +#include "net.minecraft.world.item.h" + +#include "SimpleContainer.h" + +SimpleContainer::SimpleContainer(int name, int size) +{ + this->name = name; + this->size = size; + items = new ItemInstanceArray( size ); + + listeners = NULL; +} + +void SimpleContainer::addListener(net_minecraft_world::ContainerListener *listener) +{ + if (listeners == NULL) listeners = new vector(); + listeners->push_back(listener); +} + +void SimpleContainer::removeListener(net_minecraft_world::ContainerListener *listener) +{ + // 4J Java has a remove function on lists that will find the first occurence of + // an object and remove it. We need to replicate that ourselves + + vector::iterator it = listeners->begin(); + vector::iterator itEnd = listeners->end(); + while( it != itEnd && *it != listener ) + it++; + + if( it != itEnd ) + listeners->erase( it ); +} + +shared_ptr SimpleContainer::getItem(unsigned int slot) +{ + return (*items)[slot]; +} + +shared_ptr SimpleContainer::removeItem(unsigned int slot, int count) +{ + if ((*items)[slot] != NULL) { + if ((*items)[slot]->count <= count) + { + shared_ptr item = (*items)[slot]; + (*items)[slot] = nullptr; + this->setChanged(); + return item; + } + else + { + shared_ptr i = (*items)[slot]->remove(count); + if ((*items)[slot]->count == 0) (*items)[slot] = nullptr; + this->setChanged(); + return i; + } + } + return nullptr; +} + +shared_ptr SimpleContainer::removeItemNoUpdate(int slot) +{ + if ((*items)[slot] != NULL) + { + shared_ptr item = (*items)[slot]; + (*items)[slot] = nullptr; + return item; + } + return nullptr; +} + +void SimpleContainer::setItem(unsigned int slot, shared_ptr item) +{ + (*items)[slot] = item; + if (item != NULL && item->count > getMaxStackSize()) item->count = getMaxStackSize(); + this->setChanged(); +} + +unsigned int SimpleContainer::getContainerSize() +{ + return size; +} + +int SimpleContainer::getName() +{ + return name; +} + +int SimpleContainer::getMaxStackSize() +{ + return Container::LARGE_MAX_STACK_SIZE; +} + +void SimpleContainer::setChanged() +{ + // 4J - removing this as we don't seem to have any implementation of a listener containerChanged function, and shared_from_this is proving tricky to add to containers +#if 0 + if (listeners != NULL) for (unsigned int i = 0; i < listeners->size(); i++) + { + listeners->at(i)->containerChanged(shared_from_this()); + } +#endif +} + +bool SimpleContainer::stillValid(shared_ptr player) +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/SimpleContainer.h b/Minecraft.World/SimpleContainer.h new file mode 100644 index 00000000..98c193d3 --- /dev/null +++ b/Minecraft.World/SimpleContainer.h @@ -0,0 +1,42 @@ +#pragma once +using namespace std; + +#include "Container.h" +#include "net.minecraft.world.ContainerListener.h" + +class SimpleContainer : public Container +{ +private: + int name; + int size; + ItemInstanceArray *items; + vector *listeners; + +public: + SimpleContainer(int name, int size); + + void addListener(net_minecraft_world::ContainerListener *listener); + + void removeListener(net_minecraft_world::ContainerListener *listener); + + shared_ptr getItem(unsigned int slot); + + shared_ptr removeItem(unsigned int slot, int count); + shared_ptr removeItemNoUpdate(int slot); + + void setItem(unsigned int slot, shared_ptr item); + + unsigned int getContainerSize(); + + int getName(); + + int getMaxStackSize(); + + void setChanged(); + + bool stillValid(shared_ptr player); + + void startOpen() { } // TODO Auto-generated method stub + void stopOpen() { } // TODO Auto-generated method stub + +}; \ No newline at end of file diff --git a/Minecraft.World/SimplexNoise.cpp b/Minecraft.World/SimplexNoise.cpp new file mode 100644 index 00000000..469e0b41 --- /dev/null +++ b/Minecraft.World/SimplexNoise.cpp @@ -0,0 +1,476 @@ +#include "stdafx.h" +#include "SimplexNoise.h" + +int SimplexNoise::grad3[12][3] = { { 1, 1, 0 }, { -1, 1, 0 }, { 1, -1, 0 }, { -1, -1, 0 }, { 1, 0, 1 }, { -1, 0, 1 }, { 1, 0, -1 }, { -1, 0, -1 }, { 0, 1, 1 }, { 0, -1, 1 }, { 0, 1, -1 }, { 0, -1, -1 } }; + +double SimplexNoise::F2 = 0.5 * (sqrt(3.0) - 1.0); +double SimplexNoise::G2 = (3.0 - sqrt(3.0)) / 6.0; +double SimplexNoise::F3 = 1.0 / 3.0; +double SimplexNoise::G3 = 1.0 / 6.0; + +SimplexNoise::SimplexNoise() +{ + Random random; + init(&random); +} + +SimplexNoise::SimplexNoise(Random *random) +{ + init(random); +} + +void SimplexNoise::init(Random *random) +{ + p = new int[512]; + + xo = random->nextDouble() * 256; + yo = random->nextDouble() * 256; + zo = random->nextDouble() * 256; + for (int i = 0; i < 256; i++) + { + p[i] = i; + } + + for (int i = 0; i < 256; i++) + { + int j = random->nextInt(256 - i) + i; + int tmp = p[i]; + p[i] = p[j]; + p[j] = tmp; + + p[i + 256] = p[i]; + } +} + +SimplexNoise::~SimplexNoise() +{ + delete [] p; +} + +int SimplexNoise::fastfloor(double x) +{ + return x > 0 ? (int) x : (int) x - 1; +} + +double SimplexNoise::dot(int *g, double x, double y) +{ + return g[0] * x + g[1] * y; +} + +double SimplexNoise::dot(int *g, double x, double y, double z) +{ + return g[0] * x + g[1] * y + g[2] * z; +} + +double SimplexNoise::getValue(double xin, double yin) +{ + double n0, n1, n2; // Noise contributions from the three corners + // Skew the input space to determine which simplex cell we're in + double s = (xin + yin) * F2; // Hairy factor for 2D + int i = fastfloor(xin + s); + int j = fastfloor(yin + s); + double t = (i + j) * G2; + double X0 = i - t; // Unskew the cell origin back to (x,y) space + double Y0 = j - t; + double x0 = xin - X0; // The x,y distances from the cell origin + double y0 = yin - Y0; + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords + if (x0 > y0) { + i1 = 1; + j1 = 0; + } // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else { + i1 = 0; + j1 = 1; + } // upper triangle, YX order: (0,0)->(0,1)->(1,1) + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + double y1 = y0 - j1 + G2; + double x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords + double y2 = y0 - 1.0 + 2.0 * G2; + // Work out the hashed gradient indices of the three simplex corners + int ii = i & 255; + int jj = j & 255; + int gi0 = p[ii + p[jj]] % 12; + int gi1 = p[ii + i1 + p[jj + j1]] % 12; + int gi2 = p[ii + 1 + p[jj + 1]] % 12; + // Calculate the contribution from the three corners + double t0 = 0.5 - x0 * x0 - y0 * y0; + if (t0 < 0) n0 = 0.0; + else { + t0 *= t0; + n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient + } + double t1 = 0.5 - x1 * x1 - y1 * y1; + if (t1 < 0) n1 = 0.0; + else { + t1 *= t1; + n1 = t1 * t1 * dot(grad3[gi1], x1, y1); + } + double t2 = 0.5 - x2 * x2 - y2 * y2; + if (t2 < 0) n2 = 0.0; + else { + t2 *= t2; + n2 = t2 * t2 * dot(grad3[gi2], x2, y2); + } + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 70.0 * (n0 + n1 + n2); +} + +double SimplexNoise::getValue(double xin, double yin, double zin) +{ + double n0, n1, n2, n3; + double s = (xin + yin + zin) * F3; + int i = fastfloor(xin + s); + int j = fastfloor(yin + s); + int k = fastfloor(zin + s); + + double t = (i + j + k) * G3; + double X0 = i - t; + double Y0 = j - t; + double Z0 = k - t; + double x0 = xin - X0; + double y0 = yin - Y0; + double z0 = zin - Z0; + int i1, j1, k1; + int i2, j2, k2; + if (x0 >= y0) + { + if (y0 >= z0) + { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 1; + k2 = 0; + } // X Y Z order + else if (x0 >= z0) + { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 0; + k2 = 1; + } // X Z Y order + else + { + i1 = 0; + j1 = 0; + k1 = 1; + i2 = 1; + j2 = 0; + k2 = 1; + } // Z X Y order + } + else + { // x0 y0) + { + i1 = 1; + j1 = 0; + } // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else + { + i1 = 0; + j1 = 1; + } // upper triangle, YX order: (0,0)->(0,1)->(1,1) + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + double y1 = y0 - j1 + G2; + double x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords + double y2 = y0 - 1.0 + 2.0 * G2; + // Work out the hashed gradient indices of the three simplex corners + int ii = i & 255; + int jj = j & 255; + int gi0 = p[ii + p[jj]] % 12; + int gi1 = p[ii + i1 + p[jj + j1]] % 12; + int gi2 = p[ii + 1 + p[jj + 1]] % 12; + // Calculate the contribution from the three corners + double t0 = 0.5 - x0 * x0 - y0 * y0; + if (t0 < 0) n0 = 0.0; + else + { + t0 *= t0; + n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient + } + double t1 = 0.5 - x1 * x1 - y1 * y1; + if (t1 < 0) n1 = 0.0; + else + { + t1 *= t1; + n1 = t1 * t1 * dot(grad3[gi1], x1, y1); + } + double t2 = 0.5 - x2 * x2 - y2 * y2; + if (t2 < 0) n2 = 0.0; + else + { + t2 *= t2; + n2 = t2 * t2 * dot(grad3[gi2], x2, y2); + } + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + buffer[pp++] += (70.0 * (n0 + n1 + n2))*pow; + } + } + +} +void SimplexNoise::add(doubleArray buffer, double _x, double _y, double _z, int xSize, int ySize, int zSize, double xs, double ys, double zs, double pow) +{ + int pp = 0; + for (int xx = 0; xx < xSize; xx++) + { + double xin = (_x + xx) * xs + xo; + for (int zz = 0; zz < zSize; zz++) + { + double zin = (_z + zz) * zs + zo; + for (int yy = 0; yy < ySize; yy++) + { + double yin = (_y + yy) * ys + yo; + + double n0, n1, n2, n3; + double s = (xin + yin + zin) * F3; + int i = fastfloor(xin + s); + int j = fastfloor(yin + s); + int k = fastfloor(zin + s); + double t = (i + j + k) * G3; + double X0 = i - t; + double Y0 = j - t; + double Z0 = k - t; + double x0 = xin - X0; + double y0 = yin - Y0; + double z0 = zin - Z0; + int i1, j1, k1; + int i2, j2, k2; + if (x0 >= y0) + { + if (y0 >= z0) + { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 1; + k2 = 0; + } // X Y Z order + else if (x0 >= z0) + { + i1 = 1; + j1 = 0; + k1 = 0; + i2 = 1; + j2 = 0; + k2 = 1; + } // X Z Y order + else + { + i1 = 0; + j1 = 0; + k1 = 1; + i2 = 1; + j2 = 0; + k2 = 1; + } // Z X Y order + } + else + { // x0mob = mob; + setRequiredControlFlags(Control::JumpControlFlag | Control::MoveControlFlag); +} + +bool SitGoal::canUse() +{ + if (!mob->isTame()) return false; + if (mob->isInWater()) return false; + if (!mob->onGround) return false; + + shared_ptr owner = mob->getOwner(); + if (owner == NULL) return true; // owner not on level + + if (mob->distanceToSqr(owner) < FollowOwnerGoal::TeleportDistance * FollowOwnerGoal::TeleportDistance && owner->getLastHurtByMob() != NULL) return false; + + return _wantToSit; +} + +void SitGoal::start() +{ + mob->getNavigation()->stop(); + mob->setSitting(true); +} + +void SitGoal::stop() +{ + mob->setSitting(false); +} + +void SitGoal::wantToSit(bool _wantToSit) +{ + this->_wantToSit = _wantToSit; +} \ No newline at end of file diff --git a/Minecraft.World/SitGoal.h b/Minecraft.World/SitGoal.h new file mode 100644 index 00000000..542ab18c --- /dev/null +++ b/Minecraft.World/SitGoal.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Goal.h" + +class SitGoal : public Goal +{ +private: + TamableAnimal *mob; + bool _wantToSit; + +public: + SitGoal(TamableAnimal *mob); + + bool canUse(); + void start(); + void stop(); + void wantToSit(bool _wantToSit); +}; \ No newline at end of file diff --git a/Minecraft.World/Skeleton.cpp b/Minecraft.World/Skeleton.cpp new file mode 100644 index 00000000..b6dbab1f --- /dev/null +++ b/Minecraft.World/Skeleton.cpp @@ -0,0 +1,154 @@ +#include "stdafx.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.ai.goal.target.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.damagesource.h" +#include "SharedConstants.h" +#include "Skeleton.h" +#include "..\Minecraft.Client\Textures.h" +#include "SoundTypes.h" + + + +Skeleton::Skeleton(Level *level) : Monster( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_SKELETON; // 4J was L"/mob/skeleton.png"; + + runSpeed = 0.25f; + + goalSelector.addGoal(1, new FloatGoal(this)); + goalSelector.addGoal(2, new RestrictSunGoal(this)); + goalSelector.addGoal(3, new FleeSunGoal(this, runSpeed)); + goalSelector.addGoal(4, new ArrowAttackGoal(this, runSpeed, ArrowAttackGoal::ArrowType, SharedConstants::TICKS_PER_SECOND * 3)); + goalSelector.addGoal(5, new RandomStrollGoal(this, runSpeed)); + goalSelector.addGoal(6, new LookAtPlayerGoal(this, typeid(Player), 8)); + goalSelector.addGoal(6, new RandomLookAroundGoal(this)); + + targetSelector.addGoal(1, new HurtByTargetGoal(this, false)); + targetSelector.addGoal(2, new NearestAttackableTargetGoal(this, typeid(Player), 16, 0, true)); +} + +bool Skeleton::useNewAi() +{ + return true; +} + +int Skeleton::getMaxHealth() +{ + return 20; +} + +int Skeleton::getAmbientSound() +{ + return eSoundType_MOB_SKELETON_AMBIENT; +} + +int Skeleton::getHurtSound() +{ + return eSoundType_MOB_SKELETON_HURT; +} + +int Skeleton::getDeathSound() +{ + return eSoundType_MOB_SKELETON_HURT; +} + +shared_ptr Skeleton::bow; + +shared_ptr Skeleton::getCarriedItem() +{ + return bow; +} + +MobType Skeleton::getMobType() +{ + return UNDEAD; +} + +void Skeleton::aiStep() +{ + // isClientSide check brought forward from 1.8 (I assume it's related to the lighting changes) + if (level->isDay() && !level->isClientSide) + { + float br = getBrightness(1); + if (br > 0.5f) + { + if (level->canSeeSky( Mth::floor(x), Mth::floor(y), Mth::floor(z)) && random->nextFloat() * 30 < (br - 0.4f) * 2) + { + setOnFire(8); + } + } + } + + Monster::aiStep(); +} + +void Skeleton::die(DamageSource *source) +{ + Monster::die(source); + shared_ptr player = dynamic_pointer_cast( source->getEntity() ); + if ( dynamic_pointer_cast( source->getDirectEntity() ) != NULL && player != NULL) + { + double xd = player->x - x; + double zd = player->z - z; + if (xd * xd + zd * zd >= 50 * 50) + { + player->awardStat(GenericStats::snipeSkeleton(), GenericStats::param_snipeSkeleton()); + } + } +} + +int Skeleton::getDeathLoot() +{ + return Item::arrow->id; +} + +void Skeleton::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + // drop some arrows + int count = random->nextInt(3 + playerBonusLevel); + for (int i = 0; i < count; i++) + { + spawnAtLocation(Item::arrow->id, 1); + } + // and some bones + count = random->nextInt(3 + playerBonusLevel); + for (int i = 0; i < count; i++) + { + spawnAtLocation(Item::bone->id, 1); + } +} + +void Skeleton::dropRareDeathLoot(int rareLootLevel) +{ + if (rareLootLevel > 0) + { + shared_ptr bow = shared_ptr( new ItemInstance(Item::bow) ); + EnchantmentHelper::enchantItem(random, bow, 5); + spawnAtLocation(bow, 0); + } + else + { + spawnAtLocation(Item::bow_Id, 1); + } +} + +void Skeleton::staticCtor() +{ + Skeleton::bow = shared_ptr( new ItemInstance(Item::bow, 1) ); +} diff --git a/Minecraft.World/Skeleton.h b/Minecraft.World/Skeleton.h new file mode 100644 index 00000000..b85d9afe --- /dev/null +++ b/Minecraft.World/Skeleton.h @@ -0,0 +1,39 @@ +#pragma once +using namespace std; + +#include "Monster.h" + +class Skeleton : public Monster +{ +public: + eINSTANCEOF GetType() { return eTYPE_SKELETON; } + static Entity *create(Level *level) { return new Skeleton(level); } + + Skeleton(Level *level); + + virtual bool useNewAi(); + virtual int getMaxHealth(); + +protected: + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + +public: + virtual shared_ptr getCarriedItem(); + virtual MobType getMobType(); + virtual void aiStep(); + virtual void die(DamageSource *source); + +protected: + virtual int getDeathLoot(); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + virtual void dropRareDeathLoot(int rareLootLevel); + +private: + static shared_ptr bow; + +public: + + static void staticCtor(); +}; diff --git a/Minecraft.World/SkullItem.cpp b/Minecraft.World/SkullItem.cpp new file mode 100644 index 00000000..643021dc --- /dev/null +++ b/Minecraft.World/SkullItem.cpp @@ -0,0 +1,141 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.h" +#include "net.minecraft.world.h" +#include "SkullItem.h" + +const unsigned int SkullItem::NAMES[SKULL_COUNT] = {IDS_ITEM_SKULL_SKELETON, IDS_ITEM_SKULL_WITHER, IDS_ITEM_SKULL_ZOMBIE, IDS_ITEM_SKULL_CHARACTER, IDS_ITEM_SKULL_CREEPER}; + +wstring SkullItem::ICON_NAMES[SKULL_COUNT] = {L"skull_skeleton", L"skull_wither", L"skull_zombie", L"skull_char", L"skull_creeper"}; + +SkullItem::SkullItem(int id) : Item(id) +{ + //setItemCategory(CreativeModeTab.TAB_DECORATIONS); + setMaxDamage(0); + setStackedByData(true); +} + +bool SkullItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) //float clickX, float clickY, float clickZ) +{ + if (face == 0) return false; + if (!level->getMaterial(x, y, z)->isSolid()) return false; + + if (face == 1) y++; + + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + + //if (!player->mayUseItemAt(x, y, z, face, instance)) return false; + if (!player->mayBuild(x, y, z)) return false; + + if (!Tile::skull->mayPlace(level, x, y, z)) return false; + + if(!bTestUseOnOnly) + { + level->setTileAndData(x, y, z, Tile::skull_Id, face) ;//, Tile.UPDATE_CLIENTS); + + int rot = 0; + if (face == Facing::UP) + { + rot = Mth::floor(((player->yRot) * 16) / 360 + 0.5) & 15; + } + + shared_ptr skullTE = level->getTileEntity(x, y, z); + shared_ptr skull = dynamic_pointer_cast(skullTE); + + if (skull != NULL) + { + wstring extra = L""; + if (instance->hasTag() && instance->getTag()->contains(L"SkullOwner")) + { + extra = instance->getTag()->getString(L"SkullOwner"); + } + skull->setSkullType(instance->getAuxValue(), extra); + skull->setRotation(rot); + ((SkullTile *) Tile::skull)->checkMobSpawn(level, x, y, z, skull); + } + + instance->count--; + } + return true; +} + +bool SkullItem::mayPlace(Level *level, int x, int y, int z, int face, shared_ptr player, shared_ptr item) +{ + int currentTile = level->getTile(x, y, z); + if (currentTile == Tile::topSnow_Id) + { + face = Facing::UP; + } + else if (currentTile != Tile::vine_Id && currentTile != Tile::tallgrass_Id && currentTile != Tile::deadBush_Id) + { + if (face == 0) y--; + if (face == 1) y++; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + } + + return level->mayPlace(Tile::skull_Id, x, y, z, false, face, nullptr) ;//, item); +} + +Icon *SkullItem::getIcon(int itemAuxValue) +{ + if (itemAuxValue < 0 || itemAuxValue >= SKULL_COUNT) + { + itemAuxValue = 0; + } + return icons[itemAuxValue]; +} + +int SkullItem::getLevelDataForAuxValue(int auxValue) +{ + return auxValue; +} + +unsigned int SkullItem::getDescriptionId(int iData) +{ + if (iData < 0 || iData >= SKULL_COUNT) + { + iData = 0; + } + return NAMES[iData]; +} + +unsigned int SkullItem::getDescriptionId(shared_ptr instance) +{ + int auxValue = instance->getAuxValue(); + if (auxValue < 0 || auxValue >= SKULL_COUNT) + { + auxValue = 0; + } + return NAMES[auxValue]; +} + +wstring SkullItem::getHoverName(shared_ptr itemInstance) +{ +#if 0 + if (itemInstance->getAuxValue() == SkullTileEntity::TYPE_CHAR && itemInstance->hasTag() && itemInstance->getTag()->contains(L"SkullOwner")) + { + return I18n.get("item.skull.player.name", itemInstance->getTag()->getString(L"SkullOwner")); + } + else +#endif + { + return Item::getHoverName(itemInstance); + } +} + +void SkullItem::registerIcons(IconRegister *iconRegister) +{ + for (int i = 0; i < SKULL_COUNT; i++) + { + icons[i] = iconRegister->registerIcon(ICON_NAMES[i]); + } +} \ No newline at end of file diff --git a/Minecraft.World/SkullItem.h b/Minecraft.World/SkullItem.h new file mode 100644 index 00000000..fc23be2f --- /dev/null +++ b/Minecraft.World/SkullItem.h @@ -0,0 +1,29 @@ +#pragma once + +#include "Item.h" + +class SkullItem : public Item +{ +private: + static const int SKULL_COUNT = 5; + + static const unsigned int NAMES[SKULL_COUNT]; + +public: + static wstring ICON_NAMES[SKULL_COUNT]; + +private: + Icon *icons[SKULL_COUNT]; + +public: + SkullItem(int id); + + bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + bool mayPlace(Level *level, int x, int y, int z, int face, shared_ptr player, shared_ptr item); + Icon *getIcon(int itemAuxValue); + int getLevelDataForAuxValue(int auxValue); + virtual unsigned int getDescriptionId(int iData = -1); + unsigned int getDescriptionId(shared_ptr instance); + wstring getHoverName(shared_ptr itemInstance); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/SkullTile.cpp b/Minecraft.World/SkullTile.cpp new file mode 100644 index 00000000..73aef0e1 --- /dev/null +++ b/Minecraft.World/SkullTile.cpp @@ -0,0 +1,270 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.h" +#include "SkullTile.h" + +SkullTile::SkullTile(int id) : EntityTile(id, Material::decoration, isSolidRender() ) +{ + setShape(4.0f / 16.0f, 0, 4.0f / 16.0f, 12.0f / 16.0f, .5f, 12.0f / 16.0f); +} + +int SkullTile::getRenderShape() +{ + return SHAPE_INVISIBLE; +} + +bool SkullTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool SkullTile::isCubeShaped() +{ + return false; +} + +void SkullTile::updateShape(LevelSource *level, int x, int y, int z, int forceData , shared_ptr forceEntity) +{ + int data = level->getData(x, y, z) & PLACEMENT_MASK; + + switch (data) + { + default: + case Facing::UP: + setShape(4.0f / 16.0f, 0, 4.0f / 16.0f, 12.0f / 16.0f, .5f, 12.0f / 16.0f); + break; + case Facing::NORTH: + setShape(4.0f / 16.0f, 4.0f / 16.0f, .5f, 12.0f / 16.0f, 12.0f / 16.0f, 1); + break; + case Facing::SOUTH: + setShape(4.0f / 16.0f, 4.0f / 16.0f, 0, 12.0f / 16.0f, 12.0f / 16.0f, .5f); + break; + case Facing::WEST: + setShape(.5f, 4.0f / 16.0f, 4.0f / 16.0f, 1, 12.0f / 16.0f, 12.0f / 16.0f); + break; + case Facing::EAST: + setShape(0, 4.0f / 16.0f, 4.0f / 16.0f, .5f, 12.0f / 16.0f, 12.0f / 16.0f); + break; + } +} + +AABB *SkullTile::getAABB(Level *level, int x, int y, int z) +{ + updateShape(level, x, y, z); + return EntityTile::getAABB(level, x, y, z); +} + +void SkullTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int dir = Mth::floor(by->yRot * 4 / (360) + 2.5) & 3; + level->setData(x, y, z, dir); +} + +shared_ptr SkullTile::newTileEntity(Level *level) +{ + return shared_ptr(new SkullTileEntity()); +} + +int SkullTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Item::skull_Id; +} + +int SkullTile::cloneTileData(Level *level, int x, int y, int z) +{ + shared_ptr tileEntity = level->getTileEntity(x, y, z); + shared_ptr skull = dynamic_pointer_cast(tileEntity); + if (skull != NULL) + { + return skull->getSkullType(); + } + return 0; + // 4J Stu - Not added yet + //return EntityTile::cloneTileData(level, x, y, z); +} + +int SkullTile::getSpawnResourcesAuxValue(int data) +{ + return data; +} + +void SkullTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel) +{ + // do nothing, resource is popped by onRemove + // ... because the tile entity is removed prior to spawnResources +} + +void SkullTile::playerWillDestroy(Level *level, int x, int y, int z, int data, shared_ptr player) +{ + // 4J Stu - Not implemented +#if 0 + if (player->abilities.instabuild) + { + // prevent resource drop + data |= NO_DROP_BIT; + level.setData(x, y, z, data); + } + EntityTile::playerWillDestroy(level, x, y, z, data, player); +#endif +} + +void SkullTile::onRemove(Level *level, int x, int y, int z)//, int id, int data) +{ + if (level->isClientSide) return; + int data = level->getData(x, y, z); + if ((data & NO_DROP_BIT) == 0) + { + shared_ptr item = shared_ptr(new ItemInstance(Item::skull_Id, 1, cloneTileData(level, x, y, z))); + shared_ptr entity = dynamic_pointer_cast(level->getTileEntity(x, y, z)); + + if (entity->getSkullType() == SkullTileEntity::TYPE_CHAR && !entity->getExtraType().empty()) + { + item->setTag(new CompoundTag()); + item->getTag()->putString(L"SkullOwner", entity->getExtraType()); + } + + popResource(level, x, y, z, item); + } + EntityTile::onRemove(level, x, y, z, id, data); +} + +int SkullTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::skull_Id; +} + +void SkullTile::checkMobSpawn(Level *level, int x, int y, int z, shared_ptr placedSkull) +{ + // 4J Stu - Don't have Withers yet, so don't need this +#if 0 + if (placedSkull.getSkullType() == SkullTileEntity.TYPE_WITHER && y >= 2 && level.difficulty > Difficulty.PEACEFUL) { + + // check wither boss spawn + + final int ss = Tile.hellSand.id; + + // north-south alignment + for (int zo = -2; zo <= 0; zo++) { + if ( // + level.getTile(x, y - 1, z + zo) == ss && // + level.getTile(x, y - 1, z + zo + 1) == ss && // + level.getTile(x, y - 2, z + zo + 1) == ss && // + level.getTile(x, y - 1, z + zo + 2) == ss && // + isSkullAt(level, x, y, z + zo, SkullTileEntity.TYPE_WITHER) && // + isSkullAt(level, x, y, z + zo + 1, SkullTileEntity.TYPE_WITHER) && // + isSkullAt(level, x, y, z + zo + 2, SkullTileEntity.TYPE_WITHER)) { + + level.setDataNoUpdate(x, y, z + zo, NO_DROP_BIT); + level.setDataNoUpdate(x, y, z + zo + 1, NO_DROP_BIT); + level.setDataNoUpdate(x, y, z + zo + 2, NO_DROP_BIT); + level.setTileNoUpdate(x, y, z + zo, 0); + level.setTileNoUpdate(x, y, z + zo + 1, 0); + level.setTileNoUpdate(x, y, z + zo + 2, 0); + level.setTileNoUpdate(x, y - 1, z + zo, 0); + level.setTileNoUpdate(x, y - 1, z + zo + 1, 0); + level.setTileNoUpdate(x, y - 1, z + zo + 2, 0); + level.setTileNoUpdate(x, y - 2, z + zo + 1, 0); + + if (!level.isClientSide) { + WitherBoss witherBoss = new WitherBoss(level); + witherBoss.moveTo(x + 0.5, y - 1.45, z + zo + 1.5, 90, 0); + witherBoss.yBodyRot = 90; + witherBoss.makeInvulnerable(); + level.addEntity(witherBoss); + } + + for (int i = 0; i < 120; i++) { + level.addParticle("snowballpoof", x + level.random.nextDouble(), y - 2 + level.random.nextDouble() * 3.9, z + zo + 1 + level.random.nextDouble(), 0, 0, 0); + } + + level.tileUpdated(x, y, z + zo, 0); + level.tileUpdated(x, y, z + zo + 1, 0); + level.tileUpdated(x, y, z + zo + 2, 0); + level.tileUpdated(x, y - 1, z + zo, 0); + level.tileUpdated(x, y - 1, z + zo + 1, 0); + level.tileUpdated(x, y - 1, z + zo + 2, 0); + level.tileUpdated(x, y - 2, z + zo + 1, 0); + + return; + } + } + // west-east alignment + for (int xo = -2; xo <= 0; xo++) { + if ( // + level.getTile(x + xo, y - 1, z) == ss && // + level.getTile(x + xo + 1, y - 1, z) == ss && // + level.getTile(x + xo + 1, y - 2, z) == ss && // + level.getTile(x + xo + 2, y - 1, z) == ss && // + isSkullAt(level, x + xo, y, z, SkullTileEntity.TYPE_WITHER) && // + isSkullAt(level, x + xo + 1, y, z, SkullTileEntity.TYPE_WITHER) && // + isSkullAt(level, x + xo + 2, y, z, SkullTileEntity.TYPE_WITHER)) { + + level.setDataNoUpdate(x + xo, y, z, NO_DROP_BIT); + level.setDataNoUpdate(x + xo + 1, y, z, NO_DROP_BIT); + level.setDataNoUpdate(x + xo + 2, y, z, NO_DROP_BIT); + level.setTileNoUpdate(x + xo, y, z, 0); + level.setTileNoUpdate(x + xo + 1, y, z, 0); + level.setTileNoUpdate(x + xo + 2, y, z, 0); + level.setTileNoUpdate(x + xo, y - 1, z, 0); + level.setTileNoUpdate(x + xo + 1, y - 1, z, 0); + level.setTileNoUpdate(x + xo + 2, y - 1, z, 0); + level.setTileNoUpdate(x + xo + 1, y - 2, z, 0); + + if (!level.isClientSide) { + WitherBoss witherBoss = new WitherBoss(level); + witherBoss.moveTo(x + xo + 1.5, y - 1.45, z + .5, 0, 0); + witherBoss.makeInvulnerable(); + level.addEntity(witherBoss); + } + + for (int i = 0; i < 120; i++) { + level.addParticle("snowballpoof", x + xo + 1 + level.random.nextDouble(), y - 2 + level.random.nextDouble() * 3.9, z + level.random.nextDouble(), 0, 0, 0); + } + + level.tileUpdated(x + xo, y, z, 0); + level.tileUpdated(x + xo + 1, y, z, 0); + level.tileUpdated(x + xo + 2, y, z, 0); + level.tileUpdated(x + xo, y - 1, z, 0); + level.tileUpdated(x + xo + 1, y - 1, z, 0); + level.tileUpdated(x + xo + 2, y - 1, z, 0); + level.tileUpdated(x + xo + 1, y - 2, z, 0); + + return; + } + } + } +#endif +} + +bool SkullTile::isSkullAt(Level *level, int x, int y, int z, int skullType) +{ + if (level->getTile(x, y, z) != id) + { + return false; + } + shared_ptr te = level->getTileEntity(x, y, z); + shared_ptr skull = dynamic_pointer_cast(te); + if (skull == NULL) + { + return false; + } + return skull->getSkullType() == skullType; +} + +void SkullTile::registerIcons(IconRegister *iconRegister) +{ + // None +} + +Icon *SkullTile::getTexture(int face, int data) +{ + return Tile::hellSand->getTexture(face); +} + +wstring SkullTile::getTileItemIconName() +{ + return L""; + //return SkullItem::ICON_NAMES[0]; +} \ No newline at end of file diff --git a/Minecraft.World/SkullTile.h b/Minecraft.World/SkullTile.h new file mode 100644 index 00000000..1d514acd --- /dev/null +++ b/Minecraft.World/SkullTile.h @@ -0,0 +1,44 @@ +#pragma once + +#include "EntityTile.h" + +class SkullTileEntity; + +class SkullTile : public EntityTile +{ + friend class Tile; +public: + static const int MAX_SKULL_TILES = 40; +public: + static const int PLACEMENT_MASK = 0x7; + static const int NO_DROP_BIT = 0x8; + + SkullTile(int id); + +public: + using EntityTile::onRemove; + + int getRenderShape(); + bool isSolidRender(bool isServerLevel = false); + bool isCubeShaped(); + void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); + AABB *getAABB(Level *level, int x, int y, int z); + void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + shared_ptr newTileEntity(Level *level); + int cloneTileId(Level *level, int x, int y, int z); + int cloneTileData(Level *level, int x, int y, int z); + int getSpawnResourcesAuxValue(int data); + void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel); + void playerWillDestroy(Level *level, int x, int y, int z, int data, shared_ptr player); + void onRemove(Level *level, int x, int y, int z); //, int id, int data); + int getResource(int data, Random *random, int playerBonusLevel); + void checkMobSpawn(Level *level, int x, int y, int z, shared_ptr placedSkull); + +private: + bool isSkullAt(Level *level, int x, int y, int z, int skullType); + +public: + void registerIcons(IconRegister *iconRegister); + Icon *getTexture(int face, int data); + wstring getTileItemIconName(); +}; diff --git a/Minecraft.World/SkullTileEntity.cpp b/Minecraft.World/SkullTileEntity.cpp new file mode 100644 index 00000000..ea19b446 --- /dev/null +++ b/Minecraft.World/SkullTileEntity.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.network.packet.h" +#include "SkullTileEntity.h" + +SkullTileEntity::SkullTileEntity() +{ + skullType = 0; + rotation = 0; + extraType = L""; +} + +void SkullTileEntity::save(CompoundTag *tag) +{ + TileEntity::save(tag); + tag->putByte(L"SkullType", (BYTE) (skullType & 0xff)); + tag->putByte(L"Rot", (BYTE) (rotation & 0xff)); + tag->putString(L"ExtraType", extraType); +} + +void SkullTileEntity::load(CompoundTag *tag) +{ + TileEntity::load(tag); + skullType = tag->getByte(L"SkullType"); + rotation = tag->getByte(L"Rot"); + if (tag->contains(L"ExtraType")) extraType = tag->getString(L"ExtraType"); +} + +shared_ptr SkullTileEntity::getUpdatePacket() +{ + CompoundTag *tag = new CompoundTag(); + save(tag); + return shared_ptr(new TileEntityDataPacket(x, y, z, TileEntityDataPacket::TYPE_SKULL, tag)); +} + +void SkullTileEntity::setSkullType(int skullType, const wstring &extra) +{ + this->skullType = skullType; + this->extraType = extra; +} + +int SkullTileEntity::getSkullType() +{ + return skullType; +} + +int SkullTileEntity::getRotation() +{ + return rotation; +} + +void SkullTileEntity::setRotation(int rot) +{ + rotation = rot; +} + +wstring SkullTileEntity::getExtraType() +{ + return extraType; +} + +// 4J Added +shared_ptr SkullTileEntity::clone() +{ + shared_ptr result = shared_ptr( new SkullTileEntity() ); + TileEntity::clone(result); + + result->skullType = skullType; + result->rotation = rotation; + result->extraType = extraType; + return result; +} \ No newline at end of file diff --git a/Minecraft.World/SkullTileEntity.h b/Minecraft.World/SkullTileEntity.h new file mode 100644 index 00000000..4b24457b --- /dev/null +++ b/Minecraft.World/SkullTileEntity.h @@ -0,0 +1,36 @@ +#pragma once + +#include "TileEntity.h" + +class SkullTileEntity : public TileEntity +{ +public: + eINSTANCEOF GetType() { return eTYPE_SKULLTILEENTITY; } + static TileEntity *create() { return new SkullTileEntity(); } +public: + static const int TYPE_SKELETON = 0; + static const int TYPE_WITHER = 1; + static const int TYPE_ZOMBIE = 2; + static const int TYPE_CHAR = 3; + static const int TYPE_CREEPER = 4; + +private: + int skullType; + int rotation; + wstring extraType; + +public: + SkullTileEntity(); + + void save(CompoundTag *tag); + void load(CompoundTag *tag); + shared_ptr getUpdatePacket(); + void setSkullType(int skullType, const wstring &extra); + int getSkullType(); + int getRotation(); + void setRotation(int rot); + wstring getExtraType(); + + // 4J Added + virtual shared_ptr clone(); +}; \ No newline at end of file diff --git a/Minecraft.World/SkyIslandDimension.cpp b/Minecraft.World/SkyIslandDimension.cpp new file mode 100644 index 00000000..0746c92c --- /dev/null +++ b/Minecraft.World/SkyIslandDimension.cpp @@ -0,0 +1,63 @@ +#include "stdafx.h" +#include "SkyIslandDimension.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.levelgen.h" + +void SkyIslandDimension::init() +{ + biomeSource = new FixedBiomeSource(Biome::sky, 0.5f, 0); + id = 1; +} + +ChunkSource *SkyIslandDimension::createRandomLevelSource() const +{ + return new SkyIslandRandomLevelSource(level, level->getSeed()); +} + +float SkyIslandDimension::getTimeOfDay(__int64 time, float a) const +{ + return 0.0f; +} + +float *SkyIslandDimension::getSunriseColor(float td, float a) +{ + return NULL; +} + +Vec3 *SkyIslandDimension::getFogColor(float td, float a) const +{ + int fogColor = 0x8080a0; + float br = Mth::cos(td * PI * 2) * 2 + 0.5f; + if (br < 0.0f) br = 0.0f; + if (br > 1.0f) br = 1.0f; + + float r = ((fogColor >> 16) & 0xff) / 255.0f; + float g = ((fogColor >> 8) & 0xff) / 255.0f; + float b = ((fogColor) & 0xff) / 255.0f; + r *= br * 0.94f + 0.06f; + g *= br * 0.94f + 0.06f; + b *= br * 0.91f + 0.09f; + + return Vec3::newTemp(r, g, b); +} + +bool SkyIslandDimension::hasGround() +{ + return false; +} + +float SkyIslandDimension::getCloudHeight() +{ + return 8; +} + +bool SkyIslandDimension::isValidSpawn(int x, int z) const +{ + int topTile = level->getTopTile(x, z); + + if (topTile == 0) return false; + + return Tile::tiles[topTile]->material->blocksMotion(); +} \ No newline at end of file diff --git a/Minecraft.World/Slime.cpp b/Minecraft.World/Slime.cpp new file mode 100644 index 00000000..7941fbe0 --- /dev/null +++ b/Minecraft.World/Slime.cpp @@ -0,0 +1,287 @@ +#include "stdafx.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.storage.h" +#include "net.minecraft.world.level.chunk.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.damagesource.h" +#include "com.mojang.nbt.h" +#include "Slime.h" +#include "..\Minecraft.Client\Textures.h" +#include "SoundTypes.h" + + + +void Slime::_init() +{ + jumpDelay = 0; + + targetSquish = 0; + squish = 0; + oSquish = 0; +} + +Slime::Slime(Level *level) : Mob( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + _init(); + + this->textureIdx = TN_MOB_SLIME; // 4J was L"/mob/slime.png"; + int size = 1 << (random->nextInt(3)); + this->heightOffset = 0; + jumpDelay = random->nextInt(20) + 10; + setSize(size); +} + +void Slime::defineSynchedData() +{ + Mob::defineSynchedData(); + + entityData->define(ID_SIZE, (byte) 1); +} + +void Slime::setSize(int size) +{ + entityData->set(ID_SIZE, (byte) size); + Mob::setSize(0.6f * size, 0.6f * size); + this->setPos(x, y, z); + setHealth(getMaxHealth()); + xpReward = size; +} + +int Slime::getMaxHealth() +{ + int size = getSize(); + return size * size; +} + +int Slime::getSize() +{ + return entityData->getByte(ID_SIZE); +} + +void Slime::addAdditonalSaveData(CompoundTag *tag) +{ + Mob::addAdditonalSaveData(tag); + tag->putInt(L"Size", getSize() - 1); +} + +void Slime::readAdditionalSaveData(CompoundTag *tag) +{ + Mob::readAdditionalSaveData(tag); + setSize(tag->getInt(L"Size") + 1); +} + +ePARTICLE_TYPE Slime::getParticleName() +{ + return eParticleType_slime; +} + +int Slime::getSquishSound() +{ + return eSoundType_MOB_SLIME; +} + +void Slime::tick() +{ + if (!level->isClientSide && level->difficulty == Difficulty::PEACEFUL && getSize() > 0) + { + removed = true; + } + + squish = squish + (targetSquish - squish) * .5f; + + oSquish = squish; + bool wasOnGround = this->onGround; + Mob::tick(); + if (onGround && !wasOnGround) + { + int size = getSize(); + for (int i = 0; i < size * 8; i++) + { + float dir = random->nextFloat() * PI * 2; + float d = random->nextFloat() * 0.5f + 0.5f; + float xd = Mth::sin(dir) * size * 0.5f * d; + float zd = Mth::cos(dir) * size * 0.5f * d; + level->addParticle(getParticleName(), x + xd, bb->y0, z + zd, 0, 0, 0); + } + + if (doPlayLandSound()) + { + level->playSound(shared_from_this(), getSquishSound(), getSoundVolume(), ((random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f) / 0.8f); + } + targetSquish = -0.5f; + } + // 4J Stu - Brought forward from 1.3 in TU7 to fix lava slime render + else if (!onGround && wasOnGround) + { + targetSquish = 1; + } + decreaseSquish(); +} + +void Slime::serverAiStep() +{ + checkDespawn(); + shared_ptr player = level->getNearestAttackablePlayer(shared_from_this(), 16); + if (player != NULL) + { + lookAt(player, 10, 20); + } + if (onGround && jumpDelay-- <= 0) + { + jumpDelay = getJumpDelay(); + if (player != NULL) + { + jumpDelay /= 3; + } + jumping = true; + if (doPlayJumpSound()) + { + level->playSound(shared_from_this(), getSquishSound(), getSoundVolume(), ((random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f) * 0.8f); + } + + // 4J Removed TU7 to bring forward change to fix lava slime render in MP + //targetSquish = 1; + xxa = 1 - random->nextFloat() * 2; + yya = (float) 1 * getSize(); + } + else + { + jumping = false; + if (onGround) + { + xxa = yya = 0; + } + } +} + +void Slime::decreaseSquish() +{ + targetSquish = targetSquish * 0.6f; +} + +int Slime::getJumpDelay() +{ + return random->nextInt(20) + 10; +} + +shared_ptr Slime::createChild() +{ + return shared_ptr( new Slime(level) ); +} + +void Slime::remove() +{ + int size = getSize(); + if (!level->isClientSide && size > 1 && getHealth() <= 0) + { + int count = 2 + random->nextInt(3); + for (int i = 0; i < count; i++) + { + // The mob spawner can currently make a maximum of 25 slimes (limited to 50% of the total amount of monsters which is 50) + // and so limit to slightly more than this so we have some head room to make a few spawned children. Also always create at least one + // new slime since we are getting rid of this one anyway. + if( i == 0 || level->countInstanceOf( eTYPE_SLIME, true) < 35 ) + { + float xd = (i % 2 - 0.5f) * size / 4.0f; + float zd = (i / 2 - 0.5f) * size / 4.0f; + shared_ptr slime = createChild(); + slime->setSize(size / 2); + slime->moveTo(x + xd, y + 0.5, z + zd, random->nextFloat() * 360, 0); + level->addEntity(slime); + } + } + } + Mob::remove(); +} + +void Slime::playerTouch(shared_ptr player) +{ + if (isDealsDamage()) + { + int size = getSize(); + if (canSee(player) && this->distanceToSqr(player) < (0.6 * size) * (0.6 * size)) + { + DamageSource *damageSource = DamageSource::mobAttack( dynamic_pointer_cast( shared_from_this() ) ); + if (player->hurt(damageSource, getAttackDamage())) + { + level->playSound(shared_from_this(), eSoundType_MOB_SLIME_ATTACK, 1, (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); + } + delete damageSource; + } + } +} + +bool Slime::isDealsDamage() +{ + return getSize() > 1; +} + +int Slime::getAttackDamage() +{ + return getSize(); +} + +int Slime::getHurtSound() +{ + return eSoundType_MOB_SLIME; +} + +int Slime::getDeathSound() +{ + return eSoundType_MOB_SLIME; +} + +int Slime::getDeathLoot() +{ + if (getSize() == 1) return Item::slimeBall->id; + return 0; +} + +bool Slime::canSpawn() +{ + LevelChunk *lc = level->getChunkAt( Mth::floor(x), Mth::floor(z)); + if (level->getLevelData()->getGenerator() == LevelType::lvl_flat && random->nextInt(4) != 1) + { + return false; + } + Random *lcr = lc->getRandom(987234911l); // 4J - separated out so we can delete + if ((getSize() == 1 || level->difficulty > Difficulty::PEACEFUL) && random->nextInt(10) == 0 && lcr->nextInt(10) == 0 && y < 40) + { + delete lcr; + return Mob::canSpawn(); + } + delete lcr; + return false; +} + +float Slime::getSoundVolume() +{ + return 0.4f * getSize(); +} + +int Slime::getMaxHeadXRot() +{ + return 0; +} + +bool Slime::doPlayJumpSound() +{ + return getSize() > 1; +} + +bool Slime::doPlayLandSound() +{ + return getSize() > 2; +} \ No newline at end of file diff --git a/Minecraft.World/Slime.h b/Minecraft.World/Slime.h new file mode 100644 index 00000000..6fa56056 --- /dev/null +++ b/Minecraft.World/Slime.h @@ -0,0 +1,78 @@ +#pragma once +using namespace std; + +#include "Mob.h" +#include "Enemy.h" +#include "ParticleTypes.h" + +class Slime : public Mob, public Enemy +{ +public: + eINSTANCEOF GetType() { return eTYPE_SLIME; } + static Entity *create(Level *level) { return new Slime(level); } + +private: + static const int ID_SIZE = 16; + +public: + float targetSquish; + float squish; + float oSquish; + +private: + int jumpDelay; + + void _init(); + +public: + Slime(Level *level); + +protected: + virtual void defineSynchedData(); + +public: + using Entity::setSize; + + virtual void setSize(int size); + virtual int getMaxHealth(); + virtual int getSize(); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +protected: + virtual ePARTICLE_TYPE getParticleName(); + virtual int getSquishSound(); + +public: + virtual void tick(); + +protected: + virtual void serverAiStep(); + virtual void decreaseSquish(); + virtual int getJumpDelay(); + virtual shared_ptr createChild(); + +public: + virtual void remove(); + virtual void playerTouch(shared_ptr player); + +protected: + virtual bool isDealsDamage(); + virtual int getAttackDamage(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual int getDeathLoot(); + +public: + virtual bool canSpawn(); + +protected: + virtual float getSoundVolume(); + +public: + virtual int getMaxHeadXRot(); + +protected: + virtual bool doPlayJumpSound(); + virtual bool doPlayLandSound(); +}; diff --git a/Minecraft.World/Slot.cpp b/Minecraft.World/Slot.cpp new file mode 100644 index 00000000..30fd9125 --- /dev/null +++ b/Minecraft.World/Slot.cpp @@ -0,0 +1,163 @@ +#include "stdafx.h" + +#include "Container.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.crafting.h" +#include "Slot.h" + +Slot::Slot(shared_ptr container, int slot, int x, int y) : container( container ), slot( slot ) +{ + this->x = x; + this->y = y; + + this->index = 0; +} + +void Slot::onQuickCraft(shared_ptr picked, shared_ptr original) +{ + if (picked == NULL || original == NULL) + { + return; + } + if (picked->id != original->id) + { + return; + } + int count = original->count - picked->count; + if (count > 0) + { + onQuickCraft(picked, count); + } +} + + +void Slot::onQuickCraft(shared_ptr picked, int count) +{ +} + +void Slot::checkTakeAchievements(shared_ptr picked) +{ +} + +void Slot::swap(Slot *other) +{ + shared_ptr item1 = container->getItem(slot); + shared_ptr item2 = other->container->getItem(other->slot); + + if (item1 != NULL && item1->count > other->getMaxStackSize()) + { + if (item2 != NULL) return; + item2 = item1->remove(item1->count - other->getMaxStackSize()); + } + if (item2 != NULL && item2->count > getMaxStackSize()) + { + if (item1 != NULL) return; + item1 = item2->remove(item2->count - getMaxStackSize()); + } + other->container->setItem(other->slot, item1); + + container->setItem(slot, item2); + setChanged(); +} + +void Slot::onTake(shared_ptr player, shared_ptr carried) +{ + setChanged(); +} + +bool Slot::mayPlace(shared_ptr item) +{ + return true; +} + +shared_ptr Slot::getItem() +{ + return container->getItem(slot); +} + +bool Slot::hasItem() +{ + return getItem() != NULL; +} + +void Slot::set(shared_ptr item) +{ + container->setItem(slot, item); + setChanged(); +} + +void Slot::setChanged() +{ + container->setChanged(); +} + +int Slot::getMaxStackSize() +{ + return container->getMaxStackSize(); +} + +Icon *Slot::getNoItemIcon() +{ + return NULL; +} + +shared_ptr Slot::remove(int c) +{ + return container->removeItem(slot, c); +} + +bool Slot::isAt(shared_ptr c, int s) +{ + return c == container && s == slot; +} + +bool Slot::mayPickup(shared_ptr player) +{ + return true; +} + +bool Slot::mayCombine(shared_ptr second) +{ + shared_ptr first = getItem(); + + if(first == NULL || second == NULL) return false; + + ArmorItem *thisItem = dynamic_cast(first->getItem()); + if(thisItem) + { + bool thisIsDyableArmor = thisItem->getMaterial() == ArmorItem::ArmorMaterial::CLOTH; + bool itemIsDye = second->id == Item::dye_powder_Id; + return thisIsDyableArmor && itemIsDye; + } + // 4J Stu - This condition taken from Recipes::getItemFor to repair items, but added the damaged check to skip when the result is pointless + else if (first != NULL && second != NULL && first->id == second->id && first->count == 1 && second->count == 1 && Item::items[first->id]->canBeDepleted() && (first->isDamaged() || second->isDamaged()) ) + { + // 4J Stu - Don't allow combinining enchanted items, the enchantment will be lost. They can use the anvil for this + return !first->isEnchanted() && !second->isEnchanted(); + } + return false; +} + +shared_ptr Slot::combine(shared_ptr item) +{ + shared_ptr result = nullptr; + shared_ptr first = getItem(); + + shared_ptr craftSlots = shared_ptr( new CraftingContainer(NULL, 2, 2) ); + craftSlots->setItem(0, item); + craftSlots->setItem(1, first); + + ArmorItem *thisItem = dynamic_cast(first->getItem()); + if(thisItem) + { + result = ArmorDyeRecipe::assembleDyedArmor(craftSlots); + } + else + { + result = Recipes::getInstance()->getItemFor(craftSlots, NULL); + } + + craftSlots->setItem(0, nullptr); + craftSlots->setItem(1, nullptr); + return result; +} diff --git a/Minecraft.World/Slot.h b/Minecraft.World/Slot.h new file mode 100644 index 00000000..6289ec0e --- /dev/null +++ b/Minecraft.World/Slot.h @@ -0,0 +1,41 @@ +#pragma once + +class Container; + +class Slot +{ +private: + int slot; + +public: + shared_ptr container; + +public: + int index; + int x, y; + + Slot(shared_ptr container, int slot, int x, int y); + virtual ~Slot() {} + + void onQuickCraft(shared_ptr picked, shared_ptr original); + +protected: + virtual void onQuickCraft(shared_ptr picked, int count); + virtual void checkTakeAchievements(shared_ptr picked); + +public: + void swap(Slot *other); + virtual void onTake(shared_ptr player, shared_ptr carried); + virtual bool mayPlace(shared_ptr item); + virtual shared_ptr getItem(); + virtual bool hasItem(); + virtual void set(shared_ptr item); + virtual void setChanged(); + virtual int getMaxStackSize(); + virtual Icon *getNoItemIcon(); + virtual shared_ptr remove(int c); + virtual bool isAt(shared_ptr c, int s); + virtual bool mayPickup(shared_ptr player); + virtual bool mayCombine(shared_ptr item); // 4J Added + virtual shared_ptr combine(shared_ptr item); // 4J Added +}; \ No newline at end of file diff --git a/Minecraft.World/SmallFireball.cpp b/Minecraft.World/SmallFireball.cpp new file mode 100644 index 00000000..a1fa5cb1 --- /dev/null +++ b/Minecraft.World/SmallFireball.cpp @@ -0,0 +1,80 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.h" +#include "SmallFireball.h" + +SmallFireball::SmallFireball(Level *level) : Fireball(level) +{ + setSize(5 / 16.0f, 5 / 16.0f); +} + +SmallFireball::SmallFireball(Level *level, shared_ptr mob, double xa, double ya, double za) : Fireball(level, mob, xa, ya, za) +{ + setSize(5 / 16.0f, 5 / 16.0f); +} + +SmallFireball::SmallFireball(Level *level, double x, double y, double z, double xa, double ya, double za) : Fireball(level, x, y, z, xa, ya, za) +{ + setSize(5 / 16.0f, 5 / 16.0f); +} + +void SmallFireball::onHit(HitResult *res) +{ + if (!level->isClientSide) + { + if (res->entity != NULL) + { + DamageSource *damageSource = DamageSource::fireball(dynamic_pointer_cast(shared_from_this()),owner); + if (!res->entity->isFireImmune() && res->entity->hurt(damageSource, 5)) + { + res->entity->setOnFire(5); + } + delete damageSource; + } + else + { + int tileX = res->x; + int tileY = res->y; + int tileZ = res->z; + switch (res->f) + { + case Facing::UP: + tileY++; + break; + case Facing::DOWN: + tileY--; + break; + case Facing::NORTH: + tileZ--; + break; + case Facing::SOUTH: + tileZ++; + break; + case Facing::EAST: + tileX++; + break; + case Facing::WEST: + tileX--; + break; + }; + if (level->isEmptyTile(tileX, tileY, tileZ)) + { + level->setTile(tileX, tileY, tileZ, Tile::fire_Id); + } + } + remove(); + } +} + +bool SmallFireball::isPickable() +{ + return false; +} + +bool SmallFireball::hurt(DamageSource *source, int damage) +{ + return false; +} \ No newline at end of file diff --git a/Minecraft.World/SmallFireball.h b/Minecraft.World/SmallFireball.h new file mode 100644 index 00000000..38ad8efd --- /dev/null +++ b/Minecraft.World/SmallFireball.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Fireball.h" + +class HitResult; + +class SmallFireball : public Fireball +{ +public: + eINSTANCEOF GetType() { return eTYPE_SMALL_FIREBALL; } + static Entity *create(Level *level) { return new SmallFireball(level); } + +public: + SmallFireball(Level *level); + SmallFireball(Level *level, shared_ptr mob, double xa, double ya, double za); + SmallFireball(Level *level, double x, double y, double z, double xa, double ya, double za); + +protected: + virtual void onHit(HitResult *res); + +public: + virtual bool isPickable(); + virtual bool hurt(DamageSource *source, int damage); +}; \ No newline at end of file diff --git a/Minecraft.World/SmoothFloat.cpp b/Minecraft.World/SmoothFloat.cpp new file mode 100644 index 00000000..116a6d7d --- /dev/null +++ b/Minecraft.World/SmoothFloat.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" +#include "SmoothFloat.h" + +SmoothFloat::SmoothFloat() +{ + targetValue = 0.0f; + remainingValue = 0.0f; + lastAmount = 0.0f; +} + +float SmoothFloat::getNewDeltaValue(float deltaValue, float accelerationAmount) +{ + targetValue += deltaValue; + + deltaValue = (targetValue - remainingValue) * accelerationAmount; + lastAmount = lastAmount + (deltaValue - lastAmount) * 0.5f; + if ((deltaValue > 0 && deltaValue > lastAmount) || (deltaValue < 0 && deltaValue < lastAmount)) + { + deltaValue = lastAmount; + } + remainingValue += deltaValue; + + return deltaValue; +} + +float SmoothFloat::getTargetValue() +{ + return targetValue; +} \ No newline at end of file diff --git a/Minecraft.World/SmoothFloat.h b/Minecraft.World/SmoothFloat.h new file mode 100644 index 00000000..5b25302a --- /dev/null +++ b/Minecraft.World/SmoothFloat.h @@ -0,0 +1,13 @@ +#pragma once + +class SmoothFloat +{ +private: + float targetValue; + float remainingValue; + float lastAmount; +public: + SmoothFloat(); // 4J added + float getNewDeltaValue(float deltaValue, float accelerationAmount); + float getTargetValue(); +}; \ No newline at end of file diff --git a/Minecraft.World/SmoothLayer.cpp b/Minecraft.World/SmoothLayer.cpp new file mode 100644 index 00000000..a5545cc5 --- /dev/null +++ b/Minecraft.World/SmoothLayer.cpp @@ -0,0 +1,45 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" + +SmoothLayer::SmoothLayer(__int64 seedMixup, shared_ptrparent) : Layer(seedMixup) +{ + this->parent = parent; +} + +intArray SmoothLayer::getArea(int xo, int yo, int w, int h) +{ + int px = xo - 1; + int py = yo - 1; + int pw = w + 2; + int ph = h + 2; + intArray p = parent->getArea(px, py, pw, ph); + + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + int l = p[(x + 0) + (y + 1) * pw]; + int r = p[(x + 2) + (y + 1) * pw]; + int u = p[(x + 1) + (y + 0) * pw]; + int d = p[(x + 1) + (y + 2) * pw]; + int c = p[(x + 1) + (y + 1) * pw]; + if (l == r && u == d) + { + initRandom((x + xo), (y + yo)); + if (nextRandom(2) == 0) c = l; + else c = u; + + } + else + { + if (l == r) c = l; + if (u == d) c = u; + } + result[x + y * w] = c; + + } + } + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/SmoothLayer.h b/Minecraft.World/SmoothLayer.h new file mode 100644 index 00000000..eec3f19e --- /dev/null +++ b/Minecraft.World/SmoothLayer.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Layer.h" + +class SmoothLayer : public Layer +{ +public: + SmoothLayer(__int64 seedMixup, shared_ptrparent); + + virtual intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/SmoothStoneBrickTile.cpp b/Minecraft.World/SmoothStoneBrickTile.cpp new file mode 100644 index 00000000..372901ec --- /dev/null +++ b/Minecraft.World/SmoothStoneBrickTile.cpp @@ -0,0 +1,43 @@ +#include "stdafx.h" +#include "SmoothStoneBrickTile.h" +#include "net.minecraft.world.h" + +const wstring SmoothStoneBrickTile::TEXTURE_NAMES[] = {L"stonebricksmooth", L"stonebricksmooth_mossy", L"stonebricksmooth_cracked", L"stonebricksmooth_carved"}; + +const unsigned int SmoothStoneBrickTile::SMOOTH_STONE_BRICK_NAMES[SMOOTH_STONE_BRICK_NAMES_LENGTH] = { IDS_TILE_STONE_BRICK_SMOOTH, + IDS_TILE_STONE_BRICK_SMOOTH_MOSSY, + IDS_TILE_STONE_BRICK_SMOOTH_CRACKED, + IDS_TILE_STONE_BRICK_SMOOTH_CHISELED + }; + +SmoothStoneBrickTile::SmoothStoneBrickTile(int id) : Tile(id, Material::stone) +{ + icons = NULL; +} + +Icon *SmoothStoneBrickTile::getTexture(int face, int data) +{ + if (data < 0 || data >= SMOOTH_STONE_BRICK_NAMES_LENGTH) data = 0; + return icons[data]; +} + +int SmoothStoneBrickTile::getSpawnResourcesAuxValue(int data) +{ + return data; +} + +unsigned int SmoothStoneBrickTile::getDescriptionId(int iData /*= -1*/) +{ + if(iData < 0 ) iData = 0; + return SmoothStoneBrickTile::SMOOTH_STONE_BRICK_NAMES[iData]; +} + +void SmoothStoneBrickTile::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[SMOOTH_STONE_BRICK_NAMES_LENGTH]; + + for (int i = 0; i < SMOOTH_STONE_BRICK_NAMES_LENGTH; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURE_NAMES[i]); + } +} \ No newline at end of file diff --git a/Minecraft.World/SmoothStoneBrickTile.h b/Minecraft.World/SmoothStoneBrickTile.h new file mode 100644 index 00000000..1464c6da --- /dev/null +++ b/Minecraft.World/SmoothStoneBrickTile.h @@ -0,0 +1,33 @@ +#pragma once +#include "Tile.h" + +class ChunkRebuildData; +class SmoothStoneBrickTile : public Tile +{ + friend class ChunkRebuildData; +public: + static const int TYPE_DEFAULT = 0; + static const int TYPE_MOSSY = 1; + static const int TYPE_CRACKED = 2; + static const int TYPE_DETAIL = 3; + + static const wstring TEXTURE_NAMES[]; + + static const int SMOOTH_STONE_BRICK_NAMES_LENGTH = 4; + + static const unsigned int SMOOTH_STONE_BRICK_NAMES[SMOOTH_STONE_BRICK_NAMES_LENGTH]; + +private: + Icon **icons; + +public: + + SmoothStoneBrickTile(int id); + +public: + virtual Icon *getTexture(int face, int data); + + virtual unsigned int getDescriptionId(int iData = -1); + virtual int getSpawnResourcesAuxValue(int data); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/SmoothStoneBrickTileItem.cpp b/Minecraft.World/SmoothStoneBrickTileItem.cpp new file mode 100644 index 00000000..e0326130 --- /dev/null +++ b/Minecraft.World/SmoothStoneBrickTileItem.cpp @@ -0,0 +1,32 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.h" +#include "SmoothStoneBrickTileItem.h" + +SmoothStoneBrickTileItem::SmoothStoneBrickTileItem(int id, Tile *parentTile) : TileItem(id) +{ + this->parentTile = parentTile; + + setMaxDamage(0); + setStackedByData(true); +} + +Icon *SmoothStoneBrickTileItem::getIcon(int itemAuxValue) +{ + return parentTile->getTexture(2, itemAuxValue); +} + +int SmoothStoneBrickTileItem::getLevelDataForAuxValue(int auxValue) +{ + return auxValue; +} + +unsigned int SmoothStoneBrickTileItem::getDescriptionId(shared_ptr instance) +{ + int auxValue = instance->getAuxValue(); + if (auxValue < 0 || auxValue >= SmoothStoneBrickTile::SMOOTH_STONE_BRICK_NAMES_LENGTH) + { + auxValue = 0; + } + return SmoothStoneBrickTile::SMOOTH_STONE_BRICK_NAMES[auxValue]; +} \ No newline at end of file diff --git a/Minecraft.World/SmoothStoneBrickTileItem.h b/Minecraft.World/SmoothStoneBrickTileItem.h new file mode 100644 index 00000000..18b640a3 --- /dev/null +++ b/Minecraft.World/SmoothStoneBrickTileItem.h @@ -0,0 +1,17 @@ +#pragma once +using namespace std; + +#include "TileItem.h" + +class SmoothStoneBrickTileItem : public TileItem +{ +private: + Tile *parentTile; + +public: + SmoothStoneBrickTileItem(int id, Tile *parentTile); + + virtual Icon *getIcon(int itemAuxValue); + virtual int getLevelDataForAuxValue(int auxValue); + virtual unsigned int getDescriptionId(shared_ptr instance); +}; \ No newline at end of file diff --git a/Minecraft.World/SmoothZoomLayer.cpp b/Minecraft.World/SmoothZoomLayer.cpp new file mode 100644 index 00000000..3e1c55a1 --- /dev/null +++ b/Minecraft.World/SmoothZoomLayer.cpp @@ -0,0 +1,61 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" +#include "System.h" + +SmoothZoomLayer::SmoothZoomLayer(__int64 seedMixup, shared_ptrparent) : Layer(seedMixup) +{ + this->parent = parent; +} + +intArray SmoothZoomLayer::getArea(int xo, int yo, int w, int h) +{ + int px = xo >> 1; + int py = yo >> 1; + int pw = (w >> 1) + 3; + int ph = (h >> 1) + 3; + intArray p = parent->getArea(px, py, pw, ph); + + intArray tmp = IntCache::allocate((pw * 2) * (ph * 2)); + int ww = (pw << 1); + for (int y = 0; y < ph - 1; y++) + { + int ry = y << 1; + int pp = ry * ww; + int ul = p[(0 + 0) + (y + 0) * pw]; + int dl = p[(0 + 0) + (y + 1) * pw]; + for (int x = 0; x < pw - 1; x++) + { + initRandom((x + px) << 1, (y + py) << 1); + + int ur = p[(x + 1) + (y + 0) * pw]; + int dr = p[(x + 1) + (y + 1) * pw]; + + tmp[pp] = ul; + tmp[pp++ + ww] = ul + (dl - ul) * (nextRandom(256)) / 256; + tmp[pp] = ul + (ur - ul) * (nextRandom(256)) / 256; + + int a = ul + (ur - ul) * (nextRandom(256)) / 256; + int b = dl + (dr - dl) * (nextRandom(256)) / 256; + tmp[pp++ + ww] = a + (b - a) * (nextRandom(256)) / 256; + + ul = ur; + dl = dr; + } + } + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + System::arraycopy(tmp, (y + (yo & 1)) * (pw << 1) + (xo & 1), &result, y * w, w); + } + return result; +} + +shared_ptrSmoothZoomLayer::zoom(__int64 seed, shared_ptrsup, int count) +{ + shared_ptrresult = sup; + for (int i = 0; i < count; i++) + { + result = shared_ptr(new SmoothZoomLayer(seed + i, result)); + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/SmoothZoomLayer.h b/Minecraft.World/SmoothZoomLayer.h new file mode 100644 index 00000000..b2ac4e78 --- /dev/null +++ b/Minecraft.World/SmoothZoomLayer.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Layer.h" + +class SmoothZoomLayer : public Layer +{ +public: + SmoothZoomLayer(__int64 seedMixup, shared_ptrparent); + + virtual intArray getArea(int xo, int yo, int w, int h); + static shared_ptrzoom(__int64 seed, shared_ptrsup, int count); +}; \ No newline at end of file diff --git a/Minecraft.World/SnowMan.cpp b/Minecraft.World/SnowMan.cpp new file mode 100644 index 00000000..958b8126 --- /dev/null +++ b/Minecraft.World/SnowMan.cpp @@ -0,0 +1,97 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.ai.goal.target.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.item.h" +#include "SharedConstants.h" +#include "..\Minecraft.Client\Textures.h" +#include "SnowMan.h" +#include "SoundTypes.h" + + + +SnowMan::SnowMan(Level *level) : Golem(level) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_SNOWMAN;// 4J was "/mob/snowman.png"; + this->setSize(0.4f, 1.8f); + + getNavigation()->setAvoidWater(true); + goalSelector.addGoal(1, new ArrowAttackGoal(this, 0.25f, ArrowAttackGoal::SnowballType, SharedConstants::TICKS_PER_SECOND * 1)); + goalSelector.addGoal(2, new RandomStrollGoal(this, 0.2f)); + goalSelector.addGoal(3, new LookAtPlayerGoal(this, typeid(Player), 6)); + goalSelector.addGoal(4, new RandomLookAroundGoal(this)); + + targetSelector.addGoal(1, new NearestAttackableTargetGoal(this, typeid(Monster), 16, 0, true)); +} + +bool SnowMan::useNewAi() +{ + return true; +} + +int SnowMan::getMaxHealth() +{ + return 4; +} + +void SnowMan::aiStep() +{ + Golem::aiStep(); + + if (this->isInWaterOrRain()) hurt(DamageSource::drown, 1); + + { + int xx = Mth::floor(x); + int zz = Mth::floor(z); + if (level->getBiome(xx, zz)->getTemperature() > 1) + { + hurt(DamageSource::onFire, 1); + } + } + + for (int i = 0; i < 4; i++) + { + int xx = Mth::floor(x + (i % 2 * 2 - 1) * 0.25f); + int yy = Mth::floor(y); + int zz = Mth::floor(z + ((i / 2) % 2 * 2 - 1) * 0.25f); + if (level->getTile(xx, yy, zz) == 0) + { + if (level->getBiome(xx, zz)->getTemperature() < 0.8f) + { + if (Tile::topSnow->mayPlace(level, xx, yy, zz)) + { + level->setTile(xx, yy, zz, Tile::topSnow_Id); + } + } + } + } +} + +int SnowMan::getDeathLoot() +{ + return Item::snowBall_Id; +} + + +void SnowMan::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + // drop some feathers + int count = random->nextInt(16); + for (int i = 0; i < count; i++) { + spawnAtLocation(Item::snowBall_Id, 1); + } +} \ No newline at end of file diff --git a/Minecraft.World/SnowMan.h b/Minecraft.World/SnowMan.h new file mode 100644 index 00000000..cf6f094a --- /dev/null +++ b/Minecraft.World/SnowMan.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Golem.h" + +class SnowMan : public Golem +{ +public: + eINSTANCEOF GetType() { return eTYPE_SNOWMAN; } + static Entity *create(Level *level) { return new SnowMan(level); } + +public: + SnowMan(Level *level); + virtual bool useNewAi(); + + virtual int getMaxHealth(); + virtual void aiStep(); + +protected: + virtual int getDeathLoot(); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); +}; \ No newline at end of file diff --git a/Minecraft.World/SnowTile.cpp b/Minecraft.World/SnowTile.cpp new file mode 100644 index 00000000..dde16379 --- /dev/null +++ b/Minecraft.World/SnowTile.cpp @@ -0,0 +1,35 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" + +#include "net.minecraft.world.item.h" +#include "SnowTile.h" + +SnowTile::SnowTile(int id) : Tile(id, Material::snow) +{ + setTicking(true); +} + + +int SnowTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::snowBall->id; +} + +int SnowTile::getResourceCount(Random *random) +{ + return 4; +} + +void SnowTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (level->getBrightness(LightLayer::Block, x, y, z) > 11) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } +} + +bool SnowTile::shouldTileTick(Level *level, int x,int y,int z) +{ + return level->getBrightness(LightLayer::Block, x, y, z) > 11; +} \ No newline at end of file diff --git a/Minecraft.World/SnowTile.h b/Minecraft.World/SnowTile.h new file mode 100644 index 00000000..83b1f614 --- /dev/null +++ b/Minecraft.World/SnowTile.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Tile.h" + +class SnowTile : public Tile +{ + friend class Tile; +protected: + SnowTile(int id); + +public: + int getResource(int data, Random *random, int playerBonusLevel); + + int getResourceCount(Random *random); + + void tick(Level *level, int x, int y, int z, Random *random); + + // 4J Added so we can check before we try to add a tile to the tick list if it's actually going to do seomthing + virtual bool shouldTileTick(Level *level, int x,int y,int z); +}; \ No newline at end of file diff --git a/Minecraft.World/Snowball.cpp b/Minecraft.World/Snowball.cpp new file mode 100644 index 00000000..cdcefb1b --- /dev/null +++ b/Minecraft.World/Snowball.cpp @@ -0,0 +1,52 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.entity.monster.h" +#include "Snowball.h" + + + +void Snowball::_init() +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); +} + +Snowball::Snowball(Level *level) : Throwable(level) +{ + _init(); +} + +Snowball::Snowball(Level *level, shared_ptr mob) : Throwable(level,mob) +{ + _init(); +} + +Snowball::Snowball(Level *level, double x, double y, double z) : Throwable(level,x,y,z) +{ + _init(); +} + +void Snowball::onHit(HitResult *res) +{ + if (res->entity != NULL) + { + int damage = 0; + if (dynamic_pointer_cast(res->entity) != NULL) + { + damage = 3; + } + + DamageSource *damageSource = DamageSource::thrown(shared_from_this(), owner); + res->entity->hurt(damageSource, damage); + delete damageSource; + } + for (int i = 0; i < 8; i++) + level->addParticle(eParticleType_snowballpoof, x, y, z, 0, 0, 0); + if (!level->isClientSide) + { + remove(); + } +} \ No newline at end of file diff --git a/Minecraft.World/Snowball.h b/Minecraft.World/Snowball.h new file mode 100644 index 00000000..82971726 --- /dev/null +++ b/Minecraft.World/Snowball.h @@ -0,0 +1,24 @@ +#pragma once +using namespace std; + +#include "Throwable.h" + +class HitResult; + +class Snowball : public Throwable +{ +public: + eINSTANCEOF GetType() { return eTYPE_SNOWBALL; } + static Entity *create(Level *level) { return new Snowball(level); } + +private: + void _init(); + +public: + Snowball(Level *level); + Snowball(Level *level, shared_ptr mob); + Snowball(Level *level, double x, double y, double z); + +protected: + virtual void onHit(HitResult *res); +}; \ No newline at end of file diff --git a/Minecraft.World/SnowballItem.cpp b/Minecraft.World/SnowballItem.cpp new file mode 100644 index 00000000..41dd2b7c --- /dev/null +++ b/Minecraft.World/SnowballItem.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "SnowballItem.h" +#include "SoundTypes.h" + +SnowballItem::SnowballItem(int id) : Item(id) +{ + this->maxStackSize = 16; +} + +shared_ptr SnowballItem::use(shared_ptr instance, Level *level, shared_ptr player) +{ + if (!player->abilities.instabuild) + { + instance->count--; + } + level->playSound((shared_ptr ) player, eSoundType_RANDOM_BOW, 0.5f, 0.4f / (random->nextFloat() * 0.4f + 0.8f)); + if (!level->isClientSide) level->addEntity( shared_ptr( new Snowball(level, player) ) ); + return instance; +} \ No newline at end of file diff --git a/Minecraft.World/SnowballItem.h b/Minecraft.World/SnowballItem.h new file mode 100644 index 00000000..bf2d20cb --- /dev/null +++ b/Minecraft.World/SnowballItem.h @@ -0,0 +1,12 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class SnowballItem : public Item +{ +public: + SnowballItem(int id); + + virtual shared_ptr use(shared_ptr instance, Level *level, shared_ptr player); +}; \ No newline at end of file diff --git a/Minecraft.World/Socket.cpp b/Minecraft.World/Socket.cpp new file mode 100644 index 00000000..4b703403 --- /dev/null +++ b/Minecraft.World/Socket.cpp @@ -0,0 +1,534 @@ +#include "stdafx.h" +#include "InputOutputStream.h" +#include "SocketAddress.h" +#include "Socket.h" +#include "ThreadName.h" +#include "..\Minecraft.Client\ServerConnection.h" +#include +#include "..\Minecraft.Client\PS3\PS3Extras\ShutdownManager.h" + +// This current socket implementation is for the creation of a single local link. 2 sockets can be created, one for either end of this local +// link, the end (0 or 1) is passed as a parameter to the ctor. + +CRITICAL_SECTION Socket::s_hostQueueLock[2]; +std::queue Socket::s_hostQueue[2]; +Socket::SocketOutputStreamLocal *Socket::s_hostOutStream[2]; +Socket::SocketInputStreamLocal *Socket::s_hostInStream[2]; +ServerConnection *Socket::s_serverConnection = NULL; + +void Socket::Initialise(ServerConnection *serverConnection) +{ + s_serverConnection = serverConnection; + + // Only initialise everything else once - just setting up static data, one time xrnm things, thread for ticking sockets + static bool init = false; + if( init ) + { + for( int i = 0; i < 2; i++ ) + { + if(TryEnterCriticalSection(&s_hostQueueLock[i])) + { + // Clear the queue + std::queue empty; + std::swap( s_hostQueue[i], empty ); + LeaveCriticalSection(&s_hostQueueLock[i]); + } + s_hostOutStream[i]->m_streamOpen = true; + s_hostInStream[i]->m_streamOpen = true; + } + return; + } + init = true; + + for( int i = 0; i < 2; i++ ) + { + InitializeCriticalSection(&Socket::s_hostQueueLock[i]); + s_hostOutStream[i] = new SocketOutputStreamLocal(i); + s_hostInStream[i] = new SocketInputStreamLocal(i); + } +} + +Socket::Socket(bool response) +{ + m_hostServerConnection = true; + m_hostLocal = true; + if( response ) + { + m_end = SOCKET_SERVER_END; + } + else + { + m_end = SOCKET_CLIENT_END; + Socket *socket = new Socket(1); + s_serverConnection->NewIncomingSocket(socket); + } + + for( int i = 0; i < 2; i++ ) + { + m_endClosed[i] = false; + } + m_socketClosedEvent = NULL; + createdOk = true; + networkPlayerSmallId = g_NetworkManager.GetHostPlayer()->GetSmallId(); +} + +Socket::Socket(INetworkPlayer *player, bool response /* = false*/, bool hostLocal /*= false*/) +{ + m_hostServerConnection = false; + m_hostLocal = hostLocal; + + for( int i = 0; i < 2; i++ ) + { + InitializeCriticalSection(&m_queueLockNetwork[i]); + m_inputStream[i] = NULL; + m_outputStream[i] = NULL; + m_endClosed[i] = false; + } + + if(!response || hostLocal) + { + m_inputStream[0] = new SocketInputStreamNetwork(this,0); + m_outputStream[0] = new SocketOutputStreamNetwork(this,0); + m_end = SOCKET_CLIENT_END; + } + if(response || hostLocal) + { + m_inputStream[1] = new SocketInputStreamNetwork(this,1); + m_outputStream[1] = new SocketOutputStreamNetwork(this,1); + m_end = SOCKET_SERVER_END; + } + m_socketClosedEvent = new C4JThread::Event; + //printf("New socket made %s\n", player->GetGamertag() ); + networkPlayerSmallId = player->GetSmallId(); + createdOk = true; +} + +SocketAddress *Socket::getRemoteSocketAddress() +{ + return NULL; +} + +INetworkPlayer *Socket::getPlayer() +{ + return g_NetworkManager.GetPlayerBySmallId(networkPlayerSmallId); +} + +void Socket::setPlayer(INetworkPlayer *player) +{ + if(player!=NULL) + { + networkPlayerSmallId = player->GetSmallId(); + } + else + { + networkPlayerSmallId = 0; + } +} + +void Socket::pushDataToQueue(const BYTE * pbData, DWORD dwDataSize, bool fromHost /*= true*/) +{ + int queueIdx = SOCKET_CLIENT_END; + if(!fromHost) + queueIdx = SOCKET_SERVER_END; + + if( queueIdx != m_end && !m_hostLocal ) + { + app.DebugPrintf("SOCKET: Error pushing data to queue. End is %d but queue idx id %d\n", m_end, queueIdx); + return; + } + + EnterCriticalSection(&m_queueLockNetwork[queueIdx]); + for( unsigned int i = 0; i < dwDataSize; i++ ) + { + m_queueNetwork[queueIdx].push(*pbData++); + } + LeaveCriticalSection(&m_queueLockNetwork[queueIdx]); +} + +void Socket::addIncomingSocket(Socket *socket) +{ + if( s_serverConnection != NULL ) + { + s_serverConnection->NewIncomingSocket(socket); + } +} + +InputStream *Socket::getInputStream(bool isServerConnection) +{ + if( !m_hostServerConnection ) + { + if( m_hostLocal ) + { + if( isServerConnection ) + { + return m_inputStream[SOCKET_SERVER_END]; + } + else + { + return m_inputStream[SOCKET_CLIENT_END]; + } + } + else + { + return m_inputStream[m_end]; + } + } + else + { + return s_hostInStream[m_end]; + } +} + +void Socket::setSoTimeout(int a ) +{ +} + +void Socket::setTrafficClass( int a ) +{ +} + +Socket::SocketOutputStream *Socket::getOutputStream(bool isServerConnection) +{ + if( !m_hostServerConnection ) + { + if( m_hostLocal ) + { + if( isServerConnection ) + { + return m_outputStream[SOCKET_SERVER_END]; + } + else + { + return m_outputStream[SOCKET_CLIENT_END]; + } + } + else + { + return m_outputStream[m_end]; + } + } + else + { + return s_hostOutStream[ 1 - m_end ]; + } +} + +bool Socket::close(bool isServerConnection) +{ + bool allClosed = false; + if( m_hostLocal ) + { + if( isServerConnection ) + { + m_endClosed[SOCKET_SERVER_END] = true; + if(m_endClosed[SOCKET_CLIENT_END]) + { + allClosed = true; + } + } + else + { + m_endClosed[SOCKET_CLIENT_END] = true; + if(m_endClosed[SOCKET_SERVER_END]) + { + allClosed = true; + } + } + } + else + { + allClosed = true; + m_endClosed[m_end] = true; + } + if( allClosed && m_socketClosedEvent != NULL ) + { + m_socketClosedEvent->Set(); + } + if(allClosed) createdOk = false; + return allClosed; +} + +/////////////////////////////////// Socket for input, on local connection //////////////////// + +Socket::SocketInputStreamLocal::SocketInputStreamLocal(int queueIdx) +{ + m_streamOpen = true; + m_queueIdx = queueIdx; +} + +// Try and get an input byte, blocking until one is available +int Socket::SocketInputStreamLocal::read() +{ + while(m_streamOpen && ShutdownManager::ShouldRun(ShutdownManager::eConnectionReadThreads)) + { + if(TryEnterCriticalSection(&s_hostQueueLock[m_queueIdx])) + { + if( s_hostQueue[m_queueIdx].size() ) + { + byte retval = s_hostQueue[m_queueIdx].front(); + s_hostQueue[m_queueIdx].pop(); + LeaveCriticalSection(&s_hostQueueLock[m_queueIdx]); + return retval; + } + LeaveCriticalSection(&s_hostQueueLock[m_queueIdx]); + } + Sleep(1); + } + return -1; +} + +// Try and get an input array of bytes, blocking until enough bytes are available +int Socket::SocketInputStreamLocal::read(byteArray b) +{ + return read(b, 0, b.length); +} + +// Try and get an input range of bytes, blocking until enough bytes are available +int Socket::SocketInputStreamLocal::read(byteArray b, unsigned int offset, unsigned int length) +{ + while(m_streamOpen) + { + if(TryEnterCriticalSection(&s_hostQueueLock[m_queueIdx])) + { + if( s_hostQueue[m_queueIdx].size() >= length ) + { + for( unsigned int i = 0; i < length; i++ ) + { + b[i+offset] = s_hostQueue[m_queueIdx].front(); + s_hostQueue[m_queueIdx].pop(); + } + LeaveCriticalSection(&s_hostQueueLock[m_queueIdx]); + return length; + } + LeaveCriticalSection(&s_hostQueueLock[m_queueIdx]); + } + Sleep(1); + } + return -1; +} + +void Socket::SocketInputStreamLocal::close() +{ + m_streamOpen = false; + EnterCriticalSection(&s_hostQueueLock[m_queueIdx]); + s_hostQueue[m_queueIdx].empty(); + LeaveCriticalSection(&s_hostQueueLock[m_queueIdx]); +} + +/////////////////////////////////// Socket for output, on local connection //////////////////// + +Socket::SocketOutputStreamLocal::SocketOutputStreamLocal(int queueIdx) +{ + m_streamOpen = true; + m_queueIdx = queueIdx; +} + +void Socket::SocketOutputStreamLocal::write(unsigned int b) +{ + if( m_streamOpen != true ) + { + return; + } + EnterCriticalSection(&s_hostQueueLock[m_queueIdx]); + s_hostQueue[m_queueIdx].push((byte)b); + LeaveCriticalSection(&s_hostQueueLock[m_queueIdx]); +} + +void Socket::SocketOutputStreamLocal::write(byteArray b) +{ + write(b, 0, b.length); +} + +void Socket::SocketOutputStreamLocal::write(byteArray b, unsigned int offset, unsigned int length) +{ + if( m_streamOpen != true ) + { + return; + } + MemSect(12); + EnterCriticalSection(&s_hostQueueLock[m_queueIdx]); + for( unsigned int i = 0; i < length; i++ ) + { + s_hostQueue[m_queueIdx].push(b[offset+i]); + } + LeaveCriticalSection(&s_hostQueueLock[m_queueIdx]); + MemSect(0); +} + +void Socket::SocketOutputStreamLocal::close() +{ + m_streamOpen = false; + EnterCriticalSection(&s_hostQueueLock[m_queueIdx]); + s_hostQueue[m_queueIdx].empty(); + LeaveCriticalSection(&s_hostQueueLock[m_queueIdx]); +} + +/////////////////////////////////// Socket for input, on network connection //////////////////// + +Socket::SocketInputStreamNetwork::SocketInputStreamNetwork(Socket *socket, int queueIdx) +{ + m_streamOpen = true; + m_queueIdx = queueIdx; + m_socket = socket; +} + +// Try and get an input byte, blocking until one is available +int Socket::SocketInputStreamNetwork::read() +{ + while(m_streamOpen && ShutdownManager::ShouldRun(ShutdownManager::eConnectionReadThreads)) + { + if(TryEnterCriticalSection(&m_socket->m_queueLockNetwork[m_queueIdx])) + { + if( m_socket->m_queueNetwork[m_queueIdx].size() ) + { + byte retval = m_socket->m_queueNetwork[m_queueIdx].front(); + m_socket->m_queueNetwork[m_queueIdx].pop(); + LeaveCriticalSection(&m_socket->m_queueLockNetwork[m_queueIdx]); + return retval; + } + LeaveCriticalSection(&m_socket->m_queueLockNetwork[m_queueIdx]); + } + Sleep(1); + } + return -1; +} + +// Try and get an input array of bytes, blocking until enough bytes are available +int Socket::SocketInputStreamNetwork::read(byteArray b) +{ + return read(b, 0, b.length); +} + +// Try and get an input range of bytes, blocking until enough bytes are available +int Socket::SocketInputStreamNetwork::read(byteArray b, unsigned int offset, unsigned int length) +{ + while(m_streamOpen) + { + if(TryEnterCriticalSection(&m_socket->m_queueLockNetwork[m_queueIdx])) + { + if( m_socket->m_queueNetwork[m_queueIdx].size() >= length ) + { + for( unsigned int i = 0; i < length; i++ ) + { + b[i+offset] = m_socket->m_queueNetwork[m_queueIdx].front(); + m_socket->m_queueNetwork[m_queueIdx].pop(); + } + LeaveCriticalSection(&m_socket->m_queueLockNetwork[m_queueIdx]); + return length; + } + LeaveCriticalSection(&m_socket->m_queueLockNetwork[m_queueIdx]); + } + Sleep(1); + } + return -1; +} + +void Socket::SocketInputStreamNetwork::close() +{ + m_streamOpen = false; +} + +/////////////////////////////////// Socket for output, on network connection //////////////////// + +Socket::SocketOutputStreamNetwork::SocketOutputStreamNetwork(Socket *socket, int queueIdx) +{ + m_queueIdx = queueIdx; + m_socket = socket; + m_streamOpen = true; +} + +void Socket::SocketOutputStreamNetwork::write(unsigned int b) +{ + if( m_streamOpen != true ) return; + byteArray barray; + byte bb; + bb = (byte)b; + barray.data = &bb; + barray.length = 1; + write(barray, 0, 1); + +} + +void Socket::SocketOutputStreamNetwork::write(byteArray b) +{ + write(b, 0, b.length); +} + +void Socket::SocketOutputStreamNetwork::write(byteArray b, unsigned int offset, unsigned int length) +{ + writeWithFlags(b, offset, length, 0); +} + +void Socket::SocketOutputStreamNetwork::writeWithFlags(byteArray b, unsigned int offset, unsigned int length, int flags) +{ + if( m_streamOpen != true ) return; + if( length == 0 ) return; + + // If this is a local connection, don't bother going through QNet as it just delivers it straight anyway + if( m_socket->m_hostLocal ) + { + // We want to write to the queue for the other end of this socket stream + int queueIdx = m_queueIdx; + if(queueIdx == SOCKET_CLIENT_END) + queueIdx = SOCKET_SERVER_END; + else + queueIdx = SOCKET_CLIENT_END; + + EnterCriticalSection(&m_socket->m_queueLockNetwork[queueIdx]); + for( unsigned int i = 0; i < length; i++ ) + { + m_socket->m_queueNetwork[queueIdx].push(b[offset+i]); + } + LeaveCriticalSection(&m_socket->m_queueLockNetwork[queueIdx]); + } + else + { + XRNM_SEND_BUFFER buffer; + buffer.pbyData = &b[offset]; + buffer.dwDataSize = length; + + INetworkPlayer *hostPlayer = g_NetworkManager.GetHostPlayer(); + if(hostPlayer == NULL) + { + app.DebugPrintf("Trying to write to network, but the hostPlayer is NULL\n"); + return; + } + INetworkPlayer *socketPlayer = m_socket->getPlayer(); + if(socketPlayer == NULL) + { + app.DebugPrintf("Trying to write to network, but the socketPlayer is NULL\n"); + return; + } + + if( m_queueIdx == SOCKET_SERVER_END ) + { + //printf( "Sent %u bytes of data from \"%ls\" to \"%ls\"\n", + //buffer.dwDataSize, + //hostPlayer->GetGamertag(), + //m_socket->networkPlayer->GetGamertag()); + + hostPlayer->SendData(socketPlayer, buffer.pbyData, buffer.dwDataSize, QNET_SENDDATA_RELIABLE | QNET_SENDDATA_SEQUENTIAL | flags); + + // DWORD queueSize = hostPlayer->GetSendQueueSize( NULL, QNET_GETSENDQUEUESIZE_BYTES ); + // if( queueSize > 24000 ) + // { + // //printf("Queue size is: %d, forcing doWork()\n",queueSize); + // g_NetworkManager.DoWork(); + // } + } + else + { + //printf( "Sent %u bytes of data from \"%ls\" to \"%ls\"\n", + //buffer.dwDataSize, + //m_socket->networkPlayer->GetGamertag(), + //hostPlayer->GetGamertag()); + + socketPlayer->SendData(hostPlayer, buffer.pbyData, buffer.dwDataSize, QNET_SENDDATA_RELIABLE | QNET_SENDDATA_SEQUENTIAL | flags); + } + } +} + +void Socket::SocketOutputStreamNetwork::close() +{ + m_streamOpen = false; +} \ No newline at end of file diff --git a/Minecraft.World/Socket.h b/Minecraft.World/Socket.h new file mode 100644 index 00000000..0019f984 --- /dev/null +++ b/Minecraft.World/Socket.h @@ -0,0 +1,134 @@ +#pragma once +#include +#include +#include +#include "InputStream.h" +#include "OutputStream.h" + +#define SOCKET_CLIENT_END 0 +#define SOCKET_SERVER_END 1 + +class SocketAddress; +class ServerConnection; + +class Socket +{ +public: + // 4J Added so we can add a priority write function + class SocketOutputStream : public OutputStream + { + public: + // The flags are those that can be used for the QNet SendData function + virtual void writeWithFlags(byteArray b, unsigned int offset, unsigned int length, int flags) { write(b, offset, length); } + }; + +private: + class SocketInputStreamLocal : public InputStream + { + public: + bool m_streamOpen; + private: + int m_queueIdx; + public: + SocketInputStreamLocal(int queueIdx); + + virtual int read(); + virtual int read(byteArray b); + virtual int read(byteArray b, unsigned int offset, unsigned int length); + virtual void close(); + virtual __int64 skip(__int64 n) { return n; } // 4J Stu - Not implemented + virtual void flush() {} + }; + + class SocketOutputStreamLocal : public SocketOutputStream + { + public: + bool m_streamOpen; + private: + int m_queueIdx; + public: + SocketOutputStreamLocal(int queueIdx); + + virtual void write(unsigned int b); + virtual void write(byteArray b); + virtual void write(byteArray b, unsigned int offset, unsigned int length); + virtual void close(); + virtual void flush() {} + }; + + class SocketInputStreamNetwork : public InputStream + { + bool m_streamOpen; + int m_queueIdx; + Socket *m_socket; + public: + SocketInputStreamNetwork(Socket *socket, int queueIdx); + + virtual int read(); + virtual int read(byteArray b); + virtual int read(byteArray b, unsigned int offset, unsigned int length); + virtual void close(); + virtual __int64 skip(__int64 n) { return n; } // 4J Stu - Not implemented + virtual void flush() {} + }; + class SocketOutputStreamNetwork : public SocketOutputStream + { + bool m_streamOpen; + int m_queueIdx; + Socket *m_socket; + public: + SocketOutputStreamNetwork(Socket *socket, int queueIdx); + + virtual void write(unsigned int b); + virtual void write(byteArray b); + virtual void write(byteArray b, unsigned int offset, unsigned int length); + virtual void writeWithFlags(byteArray b, unsigned int offset, unsigned int length, int flags); + virtual void close(); + virtual void flush() {} + }; + + bool m_hostServerConnection; // true if this is the connection between the host player and server + bool m_hostLocal; // true if this player on the same machine as the host + int m_end; // 0 for client side or 1 for host side + + // For local connections between the host player and the server + static CRITICAL_SECTION s_hostQueueLock[2]; + static std::queue s_hostQueue[2]; + static SocketOutputStreamLocal *s_hostOutStream[2]; + static SocketInputStreamLocal *s_hostInStream[2]; + + // For network connections + std::queue m_queueNetwork[2]; // For input data + CRITICAL_SECTION m_queueLockNetwork[2]; // For input data + SocketInputStreamNetwork *m_inputStream[2]; + SocketOutputStreamNetwork *m_outputStream[2]; + bool m_endClosed[2]; + + // Host only connection class + static ServerConnection *s_serverConnection; + + BYTE networkPlayerSmallId; +public: + C4JThread::Event* m_socketClosedEvent; + + INetworkPlayer *getPlayer(); + void setPlayer(INetworkPlayer *player); + +public: + static void Initialise(ServerConnection *serverConnection); + Socket(bool response = false); // 4J - Create a local socket, for end 0 or 1 of a connection + Socket(INetworkPlayer *player, bool response = false, bool hostLocal = false); // 4J - Create a socket for an INetworkPlayer + SocketAddress *getRemoteSocketAddress(); + void pushDataToQueue(const BYTE * pbData, DWORD dwDataSize, bool fromHost = true); + static void addIncomingSocket(Socket *socket); + InputStream *getInputStream(bool isServerConnection); + void setSoTimeout(int a ); + void setTrafficClass( int a ); + SocketOutputStream *getOutputStream(bool isServerConnection); + bool close(bool isServerConnection); + bool createdOk; + bool isLocal() { return m_hostLocal; } + + bool isClosing() { return m_endClosed[SOCKET_CLIENT_END] || m_endClosed[SOCKET_SERVER_END]; } + BYTE getSmallId() { return networkPlayerSmallId; } +}; \ No newline at end of file diff --git a/Minecraft.World/SocketAddress.h b/Minecraft.World/SocketAddress.h new file mode 100644 index 00000000..024e2d09 --- /dev/null +++ b/Minecraft.World/SocketAddress.h @@ -0,0 +1,6 @@ +#pragma once + +class SocketAddress +{ + +}; \ No newline at end of file diff --git a/Minecraft.World/SoundTypes.h b/Minecraft.World/SoundTypes.h new file mode 100644 index 00000000..acbe9167 --- /dev/null +++ b/Minecraft.World/SoundTypes.h @@ -0,0 +1,304 @@ +#pragma once + +// 4J-PB - if you change this, you need to update SoundEngine::wchSoundNames[] +enum eSOUND_TYPE +{ + eSoundType_MOB_CHICKEN_AMBIENT, + eSoundType_MOB_CHICKEN_HURT, + eSoundType_MOB_CHICKENPLOP, + eSoundType_MOB_COW_AMBIENT, + eSoundType_MOB_COW_HURT, + eSoundType_MOB_PIG_AMBIENT, + eSoundType_MOB_PIG_DEATH, + eSoundType_MOB_SHEEP_AMBIENT, + eSoundType_MOB_WOLF_GROWL, + eSoundType_MOB_WOLF_WHINE, + eSoundType_MOB_WOLF_PANTING, + eSoundType_MOB_WOLF_BARK, + eSoundType_MOB_WOLF_HURT, + eSoundType_MOB_WOLF_DEATH, + eSoundType_MOB_WOLF_SHAKE, + eSoundType_MOB_BLAZE_BREATHE, + eSoundType_MOB_BLAZE_HURT, + eSoundType_MOB_BLAZE_DEATH, + eSoundType_MOB_GHAST_MOAN, + eSoundType_MOB_GHAST_SCREAM, + eSoundType_MOB_GHAST_DEATH, + eSoundType_MOB_GHAST_FIREBALL, + eSoundType_MOB_GHAST_CHARGE, + eSoundType_MOB_ENDERMEN_IDLE, + eSoundType_MOB_ENDERMEN_HIT, + eSoundType_MOB_ENDERMEN_DEATH, + eSoundType_MOB_ENDERMEN_PORTAL, + eSoundType_MOB_ZOMBIEPIG_AMBIENT, + eSoundType_MOB_ZOMBIEPIG_HURT, + eSoundType_MOB_ZOMBIEPIG_DEATH, + eSoundType_MOB_ZOMBIEPIG_ZPIGANGRY, + eSoundType_MOB_SILVERFISH_AMBIENT, + eSoundType_MOB_SILVERFISH_HURT, + eSoundType_MOB_SILVERFISH_DEATH, + eSoundType_MOB_SILVERFISH_STEP, + eSoundType_MOB_SKELETON_AMBIENT, + eSoundType_MOB_SKELETON_HURT, + eSoundType_MOB_SPIDER_AMBIENT, + eSoundType_MOB_SPIDER_DEATH, + eSoundType_MOB_SLIME, + eSoundType_MOB_SLIME_ATTACK, + eSoundType_MOB_CREEPER_HURT, + eSoundType_MOB_CREEPER_DEATH, + eSoundType_MOB_ZOMBIE_AMBIENT, + eSoundType_MOB_ZOMBIE_HURT, + eSoundType_MOB_ZOMBIE_DEATH, + eSoundType_MOB_ZOMBIE_WOOD, + eSoundType_MOB_ZOMBIE_WOOD_BREAK, + eSoundType_MOB_ZOMBIE_METAL, + eSoundType_MOB_MAGMACUBE_BIG, + eSoundType_MOB_MAGMACUBE_SMALL, + eSoundType_MOB_CAT_PURR, + eSoundType_MOB_CAT_PURREOW, + eSoundType_MOB_CAT_MEOW, + eSoundType_MOB_CAT_HITT, +// eSoundType_MOB_IRONGOLEM_THROW, +// eSoundType_MOB_IRONGOLEM_HIT, +// eSoundType_MOB_IRONGOLEM_DEATH, +// eSoundType_MOB_IRONGOLEM_WALK, + eSoundType_RANDOM_BOW, + eSoundType_RANDOM_BOW_HIT, + eSoundType_RANDOM_EXPLODE, + eSoundType_RANDOM_FIZZ, + eSoundType_RANDOM_POP, + eSoundType_RANDOM_FUSE, + eSoundType_RANDOM_DRINK, + eSoundType_RANDOM_EAT, + eSoundType_RANDOM_BURP, + eSoundType_RANDOM_SPLASH, + eSoundType_RANDOM_CLICK, + eSoundType_RANDOM_GLASS, + eSoundType_RANDOM_ORB, + eSoundType_RANDOM_BREAK, + eSoundType_RANDOM_CHEST_OPEN, + eSoundType_RANDOM_CHEST_CLOSE, + eSoundType_RANDOM_DOOR_OPEN, + eSoundType_RANDOM_DOOR_CLOSE, + eSoundType_AMBIENT_WEATHER_RAIN, + eSoundType_AMBIENT_WEATHER_THUNDER, + eSoundType_AMBIENT_CAVE_CAVE, +#ifdef _XBOX + eSoundType_AMBIENT_CAVE_CAVE2, //- fixed version of eSoundType_CAVE_CAVE, without the two 192k sounds +#endif + eSoundType_PORTAL_PORTAL, + // 4J-PB - adding some that were still text in the code + eSoundType_PORTAL_TRIGGER, + eSoundType_PORTAL_TRAVEL, + + eSoundType_FIRE_IGNITE, + eSoundType_FIRE_FIRE, + eSoundType_DAMAGE_HURT, + eSoundType_DAMAGE_FALL_SMALL, + eSoundType_DAMAGE_FALL_BIG, + eSoundType_NOTE_HARP, + eSoundType_NOTE_BD, + eSoundType_NOTE_SNARE, + eSoundType_NOTE_HAT, + eSoundType_NOTE_BASSATTACK, + eSoundType_TILE_PISTON_IN, + eSoundType_TILE_PISTON_OUT, + eSoundType_LIQUID_WATER, + eSoundType_LIQUID_LAVA_POP, + eSoundType_LIQUID_LAVA, + eSoundType_STEP_STONE, + eSoundType_STEP_WOOD, + eSoundType_STEP_GRAVEL, + eSoundType_STEP_GRASS, + eSoundType_STEP_METAL, + eSoundType_STEP_CLOTH, + eSoundType_STEP_SAND, + // soundbank 2 + eSoundType_MOB_ENDERDRAGON_END, + eSoundType_MOB_ENDERDRAGON_GROWL, + eSoundType_MOB_ENDERDRAGON_HIT, + eSoundType_MOB_ENDERDRAGON_MOVE, + eSoundType_MOB_IRONGOLEM_THROW, + eSoundType_MOB_IRONGOLEM_HIT, + eSoundType_MOB_IRONGOLEM_DEATH, + eSoundType_MOB_IRONGOLEM_WALK, + + // TU14 + eSoundType_DAMAGE_THORNS, + eSoundType_RANDOM_ANVIL_BREAK, + eSoundType_RANDOM_ANVIL_LAND, + eSoundType_RANDOM_ANVIL_USE, + eSoundType_MOB_VILLAGER_HAGGLE, + eSoundType_MOB_VILLAGER_IDLE, + eSoundType_MOB_VILLAGER_HIT, + eSoundType_MOB_VILLAGER_DEATH, + eSoundType_MOB_VILLAGER_YES, + eSoundType_MOB_VILLAGER_NO, + eSoundType_MOB_ZOMBIE_INFECT, + eSoundType_MOB_ZOMBIE_UNFECT, + eSoundType_MOB_ZOMBIE_REMEDY, + eSoundType_STEP_SNOW, + eSoundType_STEP_LADDER, + eSoundType_DIG_CLOTH, + eSoundType_DIG_GRASS, + eSoundType_DIG_GRAVEL, + eSoundType_DIG_SAND, + eSoundType_DIG_SNOW, + eSoundType_DIG_STONE, + eSoundType_DIG_WOOD, + + eSoundType_MAX +}; + +// 4J-PB - if you change this, you need to update SoundEngine::wchUISoundNames[] +enum ESoundEffect +{ + eSFX_Back, + eSFX_Craft, + eSFX_CraftFail, + eSFX_Focus, + eSFX_Press, + eSFX_Scroll, + eSFX_MAX +}; + +enum eMATERIALSOUND_TYPE +{ + eMaterialSoundType_STONE, + eMaterialSoundType_WOOD, + eMaterialSoundType_GRAVEL, + eMaterialSoundType_GRASS, + eMaterialSoundType_METAL, + eMaterialSoundType_GLASS, + eMaterialSoundType_CLOTH, + eMaterialSoundType_SAND, + eMaterialSoundType_SNOW, + eMaterialSoundType_LADDER, + eMaterialSoundType_ANVIL, +}; + +/* +enum eSOUND_TYPE +{ +L"mob.chicken", +L"mob.chickenhurt", +L"mob.chickenplop", + +L"mob.cow", +L"mob.cowhurt", + +L"mob.pig", +L"mob.pigdeath", + +L"mob.sheep", + +L"mob.wolf.growl", +L"mob.wolf.whine", +L"mob.wolf.panting", +L"mob.wolf.bark", +L"mob.wolf.hurt", +L"mob.wolf.death", +L"mob.wolf.shake", + +L"mob.blaze.breathe", +L"mob.blaze.hit", +L"mob.blaze.death", + +L"mob.ghast.moan", +L"mob.ghast.scream", +L"mob.ghast.death", +L"mob.ghast.fireball", + L"mob.ghast.charge", + + L"mob.endermen.idle", + L"mob.endermen.hit", + L"mob.endermen.death", + L"mob.endermen.portal", + + L"mob.zombiepig.zpig", + L"mob.zombiepig.zpighurt", + L"mob.zombiepig.zpigdeath", + L"mob.zombiepig.zpigangry", + + L"mob.silverfish.say", + L"mob.silverfish.hit", + L"mob.silverfish.kill", + L"mob.silverfish.step", + + L"mob.skeleton", + L"mob.skeleton.hurt", + + L"mob.spider", + L"mob.spiderdeath", + + L"mob.slime", + L"mob.slimeattack", + + L"mob.creeper", + L"mob.creeperdeath", + + L"mob.zombie", + L"mob.zombiehurt", + L"mob.zombiedeath", + L"mob.zombie.wood", + L"mob.zombie.woodbreak", + L"mob.zombie.metal", + + L"mob.magmacube.big", + L"mob.magmacube.small", + + L"random.bow", + L"random.bowhit", + L"random.explode", + L"random.fizz", + L"random.pop", + L"random.fuse", + L"random.drink", + L"random.eat", + L"random.burp", + L"random.splash", + L"random.click", + L"random.glass", + L"random.orb", + L"random.break", + L"random.chestopen", + L"random.chestclosed", + L"random.door_open", + L"random.door_close", + + L"ambient.weather.rain", + L"ambient.weather.thunder", + + L"ambient.cave.cave", + + L"portal.portal", + + L"fire.ignite", + L"fire.fire", + + L"damage.hurtflesh", + L"damage.fallsmall", + L"damage.fallbig", + + L"note.harp", + L"note.bd", + L"note.snare", + L"note.hat", + L"note.bassattack", + + L"tile.piston.in", + L"tile.piston.out", + + L"liquid.water", + L"liquid.lavapop", + L"liquid.lava", + + L"step.stone", + L"step.wood", + L"step.gravel", + L"step.grass", + L"step.metal", + L"step.cloth", + L"step.sand", + +};*/ \ No newline at end of file diff --git a/Minecraft.World/SparseDataStorage.cpp b/Minecraft.World/SparseDataStorage.cpp new file mode 100644 index 00000000..6db131e7 --- /dev/null +++ b/Minecraft.World/SparseDataStorage.cpp @@ -0,0 +1,625 @@ +#include "stdafx.h" +#include "SparseDataStorage.h" + +// Note: See header for an overview of this class + +int SparseDataStorage::deleteQueueIndex; +XLockFreeStack SparseDataStorage::deleteQueue[3]; + +void SparseDataStorage::staticCtor() +{ + for( int i = 0; i < 3; i++ ) + { + deleteQueue[i].Initialize(); + } +} + +// Initialise data storage, with very limited compression - the very first plane is stored as either compressed to be "all 0", and the rest of the planes aren't compressed at all. +// The reason behind this is to keep the total allocation as a round number of 4K (small) pages, ie 16K. +// By doing this, and doing this "special" allocation as a XPhysicalAlloc rather than a malloc, we can help ensure that this full allocation gets cleaned up properly when the first +// proper compression is done on this storage. If it were just allocated with malloc, then the memory management system would have a large number of 16512 allocations to free, and +// it seems from experimentation that these basically don't make it back to the system as free pages. +// Note - the other approach here would be to allocate *no* actual storage for the data at the ctor stage. However, as chunks are created then this creates an awful lot of intermediate +// stages as each line of data is added, so it is actually much cleaner to just allocate almost fully here & then attempt to do a single compression pass over the data later on. +SparseDataStorage::SparseDataStorage() +{ + // Allocate using physical alloc. As this will (by default) return memory from the pool of 4KB pages, the address will in the range of MM_PHYSICAL_4KB_BASE upwards. We can use + // this fact to identify the allocation later, and so free it with the corresponding call to XPhysicalFree. +#ifdef _XBOX + unsigned char *planeIndices = (unsigned char *)XPhysicalAlloc(128 * 128, MAXULONG_PTR, 4096, PAGE_READWRITE); +#else + unsigned char *planeIndices = (unsigned char *)malloc(128 * 128); +#endif + unsigned char *data = planeIndices + 128; + planeIndices[0] = ALL_0_INDEX; + for( int i = 1; i < 128; i++ ) + { + planeIndices[i] = i - 1; + } + XMemSet(data, 0, 128 * 127); + + // Data and count packs together the pointer to our data and the count of planes allocated - 127 planes allocated in this case +#pragma warning ( disable : 4826 ) + dataAndCount = 0x007F000000000000L | (( (__int64) planeIndices ) & 0x0000ffffffffffffL); +#pragma warning ( default : 4826 ) +#ifdef DATA_COMPRESSION_STATS + count = 128; +#endif +} + +SparseDataStorage::SparseDataStorage(bool isUpper) +{ + // Allocate using physical alloc. As this will (by default) return memory from the pool of 4KB pages, the address will in the range of MM_PHYSICAL_4KB_BASE upwards. We can use + // this fact to identify the allocation later, and so free it with the corresponding call to XPhysicalFree. + unsigned char *planeIndices = (unsigned char *)malloc(128); + for( int i = 0; i < 128; i++ ) + { + planeIndices[i] = ALL_0_INDEX; + } + + // Data and count packs together the pointer to our data and the count of planes allocated - 127 planes allocated in this case +#pragma warning ( disable : 4826 ) + dataAndCount = 0x0000000000000000L | (( (__int64) planeIndices ) & 0x0000ffffffffffffL); +#pragma warning ( default : 4826 ) +#ifdef DATA_COMPRESSION_STATS + count = 128; +#endif +} + +SparseDataStorage::~SparseDataStorage() +{ + unsigned char *indicesAndData = (unsigned char *)(dataAndCount & 0x0000ffffffffffff); + // Determine correct means to free this data - could have been allocated either with XPhysicalAlloc or malloc + +#ifdef _XBOX + if( (unsigned int)indicesAndData >= MM_PHYSICAL_4KB_BASE ) + { + XPhysicalFree(indicesAndData); + } + else +#endif + { + free(indicesAndData); + } +// printf("Free (in dtor) 0x%x\n", indicesAndData); +} + +SparseDataStorage::SparseDataStorage(SparseDataStorage *copyFrom) +{ + // Extra details of source storage + __int64 sourceDataAndCount = copyFrom->dataAndCount; + unsigned char *sourceIndicesAndData = (unsigned char *)(sourceDataAndCount & 0x0000ffffffffffff); + int sourceCount = (sourceDataAndCount >> 48 ) & 0xffff; + + // Allocate & copy indices ( 128 bytes ) and any allocated planes (128 * count) + unsigned char *destIndicesAndData = (unsigned char *)malloc( sourceCount * 128 + 128 ); + + // AP - I've moved this to be before the memcpy because of a very strange bug on vita. Sometimes dataAndCount wasn't valid in time when ::get was called. + // This should never happen and this isn't a proper solution but fixes it for now. +#pragma warning ( disable : 4826 ) + dataAndCount = ( sourceDataAndCount & 0xffff000000000000L ) | ( ((__int64) destIndicesAndData ) & 0x0000ffffffffffffL ); +#pragma warning ( default : 4826 ) + + XMemCpy( destIndicesAndData, sourceIndicesAndData, sourceCount * 128 + 128 ); + +#ifdef DATA_COMPRESSION_STATS + count = sourceCount; +#endif +} + +// Set all data values from a data array of length 16384 (128 x 16 x 16 x 0.5). Source data must have same order as original java game +void SparseDataStorage::setData(byteArray dataIn, unsigned int inOffset) +{ + // Original order is defined as: + // pos = (x << 11 | z << 7 | y); + // slot = pos >> 1; + // part = pos & 1; + // if ( part == 0 ) value = data[slot] & 0xf + // else value = (data[slot] >> 4) & 0xf + + // Two passed through the data. First pass sets up plane indices, and counts number of planes that we actually need to allocate + int allocatedPlaneCount = 0; + unsigned char _planeIndices[128]; + + //unsigned char *lastDataPointer = (unsigned char *)(dataAndCount & 0x0000ffffffffffff); + + for( int y = 0; y < 128; y++ ) + { + bool all0 = true; + + for( int xz = 0; xz < 256; xz++ ) // 256 in loop as 16 x 16 separate bytes need checked + { + int pos = ( xz << 7 ) | y; + int slot = pos >> 1; + int part = pos & 1; + unsigned char value = ( dataIn[slot + inOffset] >> (part * 4) ) & 15; + if( value != 0 ) all0 = false; + } + if( all0 ) + { + _planeIndices[y] = ALL_0_INDEX; + } + else + { + _planeIndices[y] = allocatedPlaneCount++; + } + } + + // Allocate required storage + unsigned char *planeIndices = (unsigned char *)malloc(128 * allocatedPlaneCount + 128); + unsigned char *data = planeIndices + 128; + XMemCpy(planeIndices, _planeIndices, 128); + + // Second pass through to actually copy the data in to the storage allocated for the required planes + unsigned char *pucOut = data; + for( int y = 0; y < 128 ; y++ ) + { + // Index will be < 128 if we allocated storage for it and it has a valid index. No need to actually check the index as + // we know they were sequentially allocated above. + if( planeIndices[y] < 128 ) + { + int part = y & 1; + //int shift = 4 * part; + unsigned char *pucIn = &dataIn[ (y >> 1) + inOffset]; + + for( int xz = 0; xz < 128; xz++ ) // 128 ( 16 x 16 x 0.5 ) in loop as packing 2 values into each destination byte + { + *pucOut = ( ( *pucIn ) >> ( part * 4 ) ) & 15; + pucIn += 64; + + *pucOut |= ( ( ( *pucIn ) >> ( part * 4 ) ) & 15 ) << 4; + pucIn += 64; + pucOut++; + } + } + } + + // Get new data and count packed info +#pragma warning ( disable : 4826 ) + __int64 newDataAndCount = ((__int64) planeIndices) & 0x0000ffffffffffffL; +#pragma warning ( default : 4826 ) + newDataAndCount |= ((__int64)allocatedPlaneCount) << 48; + + updateDataAndCount( newDataAndCount ); +} + +// Gets all data values into an array of length 16384. Destination data will have same order as original java game. +void SparseDataStorage::getData(byteArray retArray, unsigned int retOffset) +{ + XMemSet(retArray.data + + retOffset, 0, 16384); + unsigned char *planeIndices, *data; + getPlaneIndicesAndData(&planeIndices, &data); + + // Original order is defined as: + // pos = (x << 11 | z << 7 | y); + // slot = pos >> 1; + // part = pos & 1; + // if ( part == 0 ) value = data[slot] & 0xf + // else value = (data[slot] >> 4) & 0xf + + for( int y = 0; y < 128; y++ ) + { + if( planeIndices[y] == ALL_0_INDEX ) + { + // No need to do anything in this case as retArray is initialised to zero + } + else + { + int part = y & 1; + int shift = 4 * part; + unsigned char *pucOut = &retArray.data[ (y >> 1) + + retOffset]; + unsigned char *pucIn = &data[ planeIndices[ y ] * 128 ]; + for( int xz = 0; xz < 128; xz++ ) // 128 in loop (16 x 16 x 0.5) as input data is being treated in pairs of nybbles that are packed in the same byte + { + unsigned char value = (*pucIn) & 15; + *pucOut |= ( value << shift ); + pucOut += 64; + + value = ((*pucIn) >> 4 ) & 15; + *pucOut |= ( value << shift ); + pucOut += 64; + + pucIn++; + } + } + } +} + +// Get an individual data value +int SparseDataStorage::get(int x, int y, int z) +{ + unsigned char *planeIndices, *data; + getPlaneIndicesAndData(&planeIndices, &data); + + if( planeIndices[y] == ALL_0_INDEX ) + { + return 0; + } + else + { + int planeIndex = x * 16 + z; // Index within this xz plane + int byteIndex = planeIndex / 2; // Byte index within the plane (2 tiles stored per byte) + int shift = ( planeIndex & 1 ) * 4; // Bit shift within the byte + int retval = ( data[ planeIndices[y] * 128 + byteIndex ] >> shift ) & 15; + + return retval; + } +} + +// Set an individual data value +void SparseDataStorage::set(int x, int y, int z, int val) +{ + unsigned char *planeIndices, *data; + getPlaneIndicesAndData(&planeIndices, &data); + + // If this plane isn't yet allocated, then we might have some extra work to do + if( planeIndices[y] >= ALL_0_INDEX ) + { + // No data allocated. Early out though if we are storing what is already represented by our special index. + if( ( val == 0 ) && ( planeIndices[y] == ALL_0_INDEX ) ) + { + return; + } + + // Reallocate the storage for planes to accomodate one extra + addNewPlane(y); + + // Get pointers again as these may have moved + getPlaneIndicesAndData(&planeIndices, &data); + } + + // Either data was already allocated, or we've just done that. Now store our value into the right place. + + int planeIndex = x * 16 + z; // Index within this xz plane + int byteIndex = planeIndex / 2; // Byte index within the plane (2 tiles stored per byte) + int shift = ( planeIndex & 1 ) * 4; // Bit shift within the byte + int mask = 0xf0 >> shift; + + int idx = planeIndices[y] * 128 + byteIndex; + data[idx] = ( data[idx] & mask ) | ( val << shift ); + +} + +// Sets a region of data values with the data at offset position in the array dataIn - external ordering compatible with java DataLayer +// Note - when data was extracted from the original data layers by LevelChunk::getBlocksAndData, y0 had to have even alignment and y1 - y0 also +// needed to be even as data was packed in nyblles in this dimension, and the code didn't make any attempt to unpack it. This behaviour is copied +// here for compatibility even though our source data isn't packed this way. +// Returns size of data copied. +int SparseDataStorage::setDataRegion(byteArray dataIn, int x0, int y0, int z0, int x1, int y1, int z1, int offset, tileUpdatedCallback callback, void *param, int yparam) +{ + // Actual setting of data happens when calling set method so no need to lock here + unsigned char *pucIn = &dataIn.data[offset]; + if( callback ) + { + for( int x = x0; x < x1; x++ ) + { + for( int z = z0; z < z1; z++ ) + { + // Emulate how data was extracted from DataLayer... see comment above + int yy0 = y0 & 0xfffffffe; + int len = ( y1 - y0 ) / 2; + for( int i = 0; i < len; i++ ) + { + int y = yy0 + ( i * 2 ); + + int toSet = (*pucIn) & 15; + if( get(x, y, z) != toSet ) + { + set(x, y, z, toSet ); + callback(x, y, z, param, yparam); + } + toSet = ((*pucIn) >> 4 ) & 15; + if( get(x, y + 1, z) != toSet ) + { + set(x, y + 1, z, toSet ); + callback(x, y + 1, z, param, yparam); + } + pucIn++; + } + } + } + } + else + { + for( int x = x0; x < x1; x++ ) + { + for( int z = z0; z < z1; z++ ) + { + // Emulate how data was extracted from DataLayer... see comment above + int yy0 = y0 & 0xfffffffe; + int len = ( y1 - y0 ) / 2; + for( int i = 0; i < len; i++ ) + { + int y = yy0 + ( i * 2 ); + + set(x, y, z, (*pucIn) & 15 ); + set(x, y + 1, z, ((*pucIn) >> 4 ) & 15 ); + pucIn++; + } + } + } + } + ptrdiff_t count = pucIn - &dataIn.data[offset]; + + return (int)count; +} + +// Updates the data at offset position dataInOut with a region of data information - external ordering compatible with java DataLayer +// Note - when data was placed in the original data layers by LevelChunk::setBlocksAndData, y0 had to have even alignment and y1 - y0 also +// needed to be even as data was packed in nyblles in this dimension, and the code didn't make any attempt to unpack it. This behaviour is copied +// here for compatibility even though our source data isn't packed this way +// Returns size of data copied. +int SparseDataStorage::getDataRegion(byteArray dataInOut, int x0, int y0, int z0, int x1, int y1, int z1, int offset) +{ + unsigned char *pucOut = &dataInOut.data[offset]; + for( int x = x0; x < x1; x++ ) + { + for( int z = z0; z < z1; z++ ) + { + // Emulate how data was extracted from DataLayer... see comment above + int yy0 = y0 & 0xfffffffe; + int len = ( y1 - y0 ) / 2; + for( int i = 0; i < len; i++ ) + { + int y = yy0 + ( i * 2 ); + + *pucOut = get( x, y, z); + *pucOut |= get( x, y + 1, z) << 4; + pucOut++; + } + } + } + ptrdiff_t count = pucOut - &dataInOut.data[offset]; + + return (int)count; +} + +void SparseDataStorage::addNewPlane(int y) +{ + bool success = false; + do + { + // Get last packed data pointer & count + __int64 lastDataAndCount = dataAndCount; + + // Unpack count & data pointer + int lastLinesUsed = (int)(( lastDataAndCount >> 48 ) & 0xffff); + unsigned char *lastDataPointer = (unsigned char *)(lastDataAndCount & 0x0000ffffffffffff); + + // Find out what to prefill the newly allocated line with + unsigned char planeIndex = lastDataPointer[y]; + + if( planeIndex < ALL_0_INDEX ) return; // Something has already allocated this line - we're done + + int linesUsed = lastLinesUsed + 1; + + // Allocate new memory storage, copy over anything from old storage, and initialise remainder + unsigned char *dataPointer = (unsigned char *)malloc(linesUsed * 128 + 128); + XMemCpy( dataPointer, lastDataPointer, 128 * lastLinesUsed + 128); + XMemSet( dataPointer + ( 128 * lastLinesUsed ) + 128, 0, 128 ); + dataPointer[y] = lastLinesUsed; + + // Get new data and count packed info +#pragma warning ( disable : 4826 ) + __int64 newDataAndCount = ((__int64) dataPointer) & 0x0000ffffffffffffL; +#pragma warning ( default : 4826 ) + newDataAndCount |= ((__int64)linesUsed) << 48; + + // Attempt to update the data & count atomically. This command will Only succeed if the data stored at + // dataAndCount is equal to lastDataAndCount, and will return the value present just before the write took place + __int64 lastDataAndCount2 = InterlockedCompareExchangeRelease64( &dataAndCount, newDataAndCount, lastDataAndCount ); + + if( lastDataAndCount2 == lastDataAndCount ) + { + success = true; + // Queue old data to be deleted + queueForDelete( lastDataPointer ); +// printf("Marking for delete 0x%x\n", lastDataPointer); +#ifdef DATA_COMPRESSION_STATS + count = linesUsed; +#endif + } + else + { + // If we didn't succeed, queue data that we made to be deleted, and try again + queueForDelete( dataPointer ); +// printf("Marking for delete (fail) 0x%x\n", dataPointer); + } + } while( !success ); +} + +void SparseDataStorage::getPlaneIndicesAndData(unsigned char **planeIndices, unsigned char **data) +{ + unsigned char *indicesAndData = (unsigned char *)(dataAndCount & 0x0000ffffffffffff); + + *planeIndices = indicesAndData; + *data = indicesAndData + 128; + +} + +void SparseDataStorage::queueForDelete(unsigned char *data) +{ + // Add this into a queue for deleting. This shouldn't be actually deleted until tick has been called twice from when + // the data went into the queue. + deleteQueue[deleteQueueIndex].Push( data ); +} + +void SparseDataStorage::tick() +{ + // We have 3 queues for deleting. Always delete from the next one after where we are writing to, so it should take 2 ticks + // before we ever delete something, from when the request to delete it came in + int freeIndex = ( deleteQueueIndex + 1 ) % 3; + +// printf("Free queue: %d, %d\n",deleteQueue[freeIndex].GetEntryCount(),deleteQueue[freeIndex].GetAllocated()); + unsigned char *toFree = NULL; + do + { + toFree = deleteQueue[freeIndex].Pop(); +// if( toFree ) printf("Deleting 0x%x\n", toFree); + // Determine correct means to free this data - could have been allocated either with XPhysicalAlloc or malloc +#ifdef _XBOX + if( (unsigned int)toFree >= MM_PHYSICAL_4KB_BASE ) + { + XPhysicalFree(toFree); + } + else +#endif + { + free(toFree); + } + } while( toFree ); + + deleteQueueIndex = ( deleteQueueIndex + 1 ) % 3; +} + +// Update storage with a new values for dataAndCount, repeating as necessary if other simultaneous writes happen. +void SparseDataStorage::updateDataAndCount(__int64 newDataAndCount) +{ + // Now actually assign this data to the storage. Just repeat until successful, there isn't any useful really that we can merge the results of this + // with any other simultaneous writes that might be happening. + bool success = false; + do + { + __int64 lastDataAndCount = dataAndCount; + unsigned char *lastDataPointer = (unsigned char *)(lastDataAndCount & 0x0000ffffffffffff); + + // Attempt to update the data & count atomically. This command will Only succeed if the data stored at + // dataAndCount is equal to lastDataAndCount, and will return the value present just before the write took place + __int64 lastDataAndCount2 = InterlockedCompareExchangeRelease64( &dataAndCount, newDataAndCount, lastDataAndCount ); + + if( lastDataAndCount2 == lastDataAndCount ) + { + success = true; + // Queue old data to be deleted +// printf("Marking for delete 0x%x (full replace)\n", lastDataPointer); + queueForDelete( lastDataPointer ); + } + } while( !success); + +#ifdef DATA_COMPRESSION_STATS + count = ( newDataAndCount >> 48 ) & 0xffff; +#endif + +} + +// Attempt to compress the stored data. This method makes no guarantee of success - if it fails due to something else writing to the storage whilst this is running, then it won't actually do anything. +int SparseDataStorage::compress() +{ + unsigned char _planeIndices[128]; + bool needsCompressed = false; + + __int64 lastDataAndCount = dataAndCount; + + unsigned char *planeIndices = (unsigned char *)(lastDataAndCount & 0x0000ffffffffffff); + unsigned char *data = planeIndices + 128; + + int planesToAlloc = 0; + for( int i = 0; i < 128; i++ ) + { + if( planeIndices[i] == ALL_0_INDEX ) + { + _planeIndices[i] = ALL_0_INDEX; + } + else + { + unsigned char *pucData = &data[ 128 * planeIndices[i] ]; + bool all0 = true; + for( int j = 0; j < 128; j++ ) // 16 x 16 x 4-bits + { + if( *pucData != 0 ) all0 = false; + pucData++; + } + if( all0 ) + { + _planeIndices[i] = ALL_0_INDEX; + needsCompressed = true; + } + else + { + _planeIndices[i] = planesToAlloc++; + } + } + } + + if( needsCompressed ) + { + unsigned char *newIndicesAndData = (unsigned char *)malloc( 128 + 128 * planesToAlloc ); + unsigned char *pucData = newIndicesAndData + 128; + XMemCpy( newIndicesAndData, _planeIndices, 128 ); + + for( int i = 0; i < 128; i++ ) + { + if( newIndicesAndData[i] < ALL_0_INDEX ) + { + XMemCpy( pucData, &data[ 128 * planeIndices[i] ], 128 ); + pucData += 128; + } + } + + // Get new data and count packed info +#pragma warning ( disable : 4826 ) + __int64 newDataAndCount = ((__int64) newIndicesAndData) & 0x0000ffffffffffffL; +#pragma warning ( default : 4826 ) + newDataAndCount |= ((__int64)planesToAlloc) << 48; + + // Attempt to update the data & count atomically. This command will Only succeed if the data stored at + // dataAndCount is equal to lastDataAndCount, and will return the value present just before the write took place + __int64 lastDataAndCount2 = InterlockedCompareExchangeRelease64( &dataAndCount, newDataAndCount, lastDataAndCount ); + + if( lastDataAndCount2 != lastDataAndCount ) + { + // Failed to write. Don't bother trying again... being very conservative here. +// printf("Marking for delete 0x%x (compress fail)\n", newIndicesAndData); + queueForDelete( newIndicesAndData ); + } + else + { + // Success + queueForDelete( planeIndices ); +// printf("Successfully compressed to %d planes, to delete 0x%x\n", planesToAlloc, planeIndices); +#ifdef DATA_COMPRESSION_STATS + count = planesToAlloc; +#endif + } + + return planesToAlloc; + } + else + { + return (int)((lastDataAndCount >> 48 ) & 0xffff); + } +} + + +bool SparseDataStorage::isCompressed() +{ + + int count = ( dataAndCount >> 48 ) & 0xffff; + return (count < 127); + + +} + +void SparseDataStorage::write(DataOutputStream *dos) +{ + int count = ( dataAndCount >> 48 ) & 0xffff; + dos->writeInt(count); + unsigned char *dataPointer = (unsigned char *)(dataAndCount & 0x0000ffffffffffff); + byteArray wrapper(dataPointer, count * 128 + 128); + dos->write(wrapper); +} + +void SparseDataStorage::read(DataInputStream *dis) +{ + int count = dis->readInt(); + unsigned char *dataPointer = (unsigned char *)malloc(count * 128 + 128); + byteArray wrapper(dataPointer, count * 128 + 128); + dis->readFully(wrapper); + +#pragma warning ( disable : 4826 ) + __int64 newDataAndCount = ((__int64) dataPointer) & 0x0000ffffffffffffL; +#pragma warning ( default : 4826 ) + newDataAndCount |= ((__int64)count) << 48; + + updateDataAndCount(newDataAndCount); +} diff --git a/Minecraft.World/SparseDataStorage.h b/Minecraft.World/SparseDataStorage.h new file mode 100644 index 00000000..93d5c424 --- /dev/null +++ b/Minecraft.World/SparseDataStorage.h @@ -0,0 +1,84 @@ +#pragma once +#include "xmcore.h" + +// 4J added - Storage for data (ie the extra per tile storage). Data is normally stored as 4-bits per tile, in a DataLayer class of 16384 bytes ( 128 x 16 x 16 x 0.5 ) +// This class provides more economical storage for such data by taking into consideration that it is quite common for large parts of the data to be very compressible (ie zero). +// We are aiming here to balance performance (this data is accessed very frequently) against size. + +// Details of storage method: + +// 1. Data is split into horizontal planes, of which there are 128, and each taking up 128 bytes (16 x 16 x 0.5) +// 2. Each of these layers has a permanently allocated index in this class (planeIndices). +// 3. Data for allocatedPlaneCount planes worth of data is allocated in the data array ( allocatedPlaneCount * 128 bytes ) +// 4. If a plane index for a layer is < 128, then the data for that layer is at data[ index * 128 ] +// 5. If a plane index for a layer is 128, then all values for that plane are 0 + +// This class needs to be thread safe as there are times where chunk data is shared between server & main threads. Data values are queried +// very regularly so this needs to be as light-weight as possible. + +// To meet these requirements, this class is now implemented using a lock-free system, implemented using a read-copy-update (RCU) type algorithm. Some details... + +// (1) The storage details for the class are now packed into a single __int64, which contains both a pointer to the data that is required and a count of how many planes worth +// of storage are allocated. This allows the full storage to be updated atomically using compare and exchange operations (implemented with InterlockedCompareExchangeRelease64). +// (2) The data pointer referenced in this __int64 points to an area of memory which is 128 + 128 * plane_count bytes long, where the first 128 bytes stoere the plane indices, and +// the rest of the data is variable in size to accomodate however many planes are required to be stored +// (3) The RCU bit of the algorithm means that any read operations don't need to do any checks or locks at all. When the data needs to be updated, a copy of it is made and updated, +// then an attempt is made to swap the new data in - if this succeeds then the old data pointer is deleted later at some point where we know nothing will be reading from it anymore. +// This is achieved by putting the delete request in a queue which means it won't actually get deleted until 2 game ticks after the last time its reference existed, which should give +// us a large margin of safety. If the attempt to swap the new data in fails, then the whole write operation has to be attempted again - this is the only time there is really a +// high cost for this algorithm and such write collisions should be rare. + +//#define DATA_COMPRESSION_STATS +class TileCompressData_SPU; + +class SparseDataStorage +{ + friend class TileCompressData_SPU; +private: +// unsigned char planeIndices[128]; + __int64 dataAndCount; // Contains packed-together data pointer (lower 48-bits), and count of lines used (upper 16-bits) + +// unsigned char *data; +// unsigned int allocatedPlaneCount; + + static const int ALL_0_INDEX = 128; + +#ifdef _XBOX + static const unsigned int MM_PHYSICAL_4KB_BASE = 0xE0000000; // Start of where 4KB page sized physical allocations are made +#endif +public: + SparseDataStorage(); + SparseDataStorage(bool isUpper); + SparseDataStorage(SparseDataStorage *copyFrom); // ctor with deep copy + ~SparseDataStorage(); + + void setData(byteArray dataIn, unsigned int inOffset); // Set all data values from a data array of length 16384 (128 x 16 x 16 x 0.5). + void getData(byteArray retArray, unsigned int retOffset); // Gets all data values into an array of length 16384. + int get(int x, int y, int z); // Get an individual data value + void set(int x, int y, int z, int val); // Set an individual data value + typedef void (*tileUpdatedCallback)(int x, int y , int z, void *param, int yparam); + int setDataRegion(byteArray dataIn, int x0, int y0, int z0, int x1, int y1, int z1, int offset, tileUpdatedCallback callback, void *param, int yparam); // Sets a region of data values with the data at offset position in the array dataIn - external ordering compatible with java DataLayer + int getDataRegion(byteArray dataInOut, int x0, int y0, int z0, int x1, int y1, int z1, int offset); // Updates the data at offset position dataInOut with a region of data information - external ordering compatible with java DataLayer + + static void staticCtor(); + + void addNewPlane(int y); + void getPlaneIndicesAndData(unsigned char **planeIndices, unsigned char **data); + void updateDataAndCount(__int64 newDataAndCount); + int compress(); + + bool isCompressed(); + void queueForDelete(unsigned char *data); + + static void tick(); + static int deleteQueueIndex; + static XLockFreeStack deleteQueue[3]; + +#ifdef DATA_COMPRESSION_STATS + int count; +#endif + + void write(DataOutputStream *dos); + void read(DataInputStream *dis); +}; + diff --git a/Minecraft.World/SparseLightStorage.cpp b/Minecraft.World/SparseLightStorage.cpp new file mode 100644 index 00000000..f9a2f0cb --- /dev/null +++ b/Minecraft.World/SparseLightStorage.cpp @@ -0,0 +1,642 @@ +#include "stdafx.h" +#include "SparseLightStorage.h" + +// Note: See header for an overview of this class + +int SparseLightStorage::deleteQueueIndex; +XLockFreeStack SparseLightStorage::deleteQueue[3]; + +void SparseLightStorage::staticCtor() +{ + for( int i = 0; i < 3; i++ ) + { + deleteQueue[i].Initialize(); + } +} + +// Initialise lighting storage, with very limited compression - the very first plane is stored as either compressed to be "all 15" or "all 0" depending on whether this +// will store sky or not, and the rest of the planes aren't compressed at all. The reason behind this is to keep the total allocation as a round number of 4K (small) pages, ie 16K. +// By doing this, and doing this "special" allocation as a XPhysicalAlloc rather than a malloc, we can help ensure that this full allocation gets cleaned up properly when the first +// proper compression is done on this storage. If it were just allocated with malloc, then the memory management system would have a large number of 16512 allocations to free, and +// it seems from experimentation that these basically don't make it back to the system as free pages. +// Note - the other approach here would be to allocate *no* actual storage for the lights at the ctor stage. However, as chunks are created then this creates an awful lot of intermediate +// stages as each line of lighting is added, so it is actually much cleaner to just allocate almost fully here & then attempt to do a single compression pass over the data later on. +SparseLightStorage::SparseLightStorage(bool sky) +{ + // Allocate using physical alloc. As this will (by default) return memory from the pool of 4KB pages, the address will in the range of MM_PHYSICAL_4KB_BASE upwards. We can use + // this fact to identify the allocation later, and so free it with the corresponding call to XPhysicalFree. +#ifdef _XBOX + unsigned char *planeIndices = (unsigned char *)XPhysicalAlloc(128 * 128, MAXULONG_PTR, 4096, PAGE_READWRITE); +#else + unsigned char *planeIndices = (unsigned char *)malloc(128 * 128); +#endif + unsigned char *data = planeIndices + 128; + planeIndices[127] = sky ? ALL_15_INDEX : ALL_0_INDEX; + for( int i = 0; i < 127; i++ ) + { + planeIndices[i] = i; + } + XMemSet(data, 0, 128 * 127); + + // Data and count packs together the pointer to our data and the count of planes allocated - 127 planes allocated in this case +#pragma warning ( disable : 4826 ) + dataAndCount = 0x007F000000000000L | (( (__int64) planeIndices ) & 0x0000ffffffffffffL); +#pragma warning ( default : 4826 ) +#ifdef LIGHT_COMPRESSION_STATS + count = 127; +#endif +} + +SparseLightStorage::SparseLightStorage(bool sky, bool isUpper) +{ + // Allocate using physical alloc. As this will (by default) return memory from the pool of 4KB pages, the address will in the range of MM_PHYSICAL_4KB_BASE upwards. We can use + // this fact to identify the allocation later, and so free it with the corresponding call to XPhysicalFree. + unsigned char *planeIndices = (unsigned char *)malloc(128); + for( int i = 0; i < 128; i++ ) + { + planeIndices[i] = sky ? ALL_15_INDEX : ALL_0_INDEX; + } + + // Data and count packs together the pointer to our data and the count of planes allocated - 0 planes allocated in this case +#pragma warning ( disable : 4826 ) + dataAndCount = 0x0000000000000000L | (( (__int64) planeIndices ) & 0x0000ffffffffffffL); +#pragma warning ( default : 4826 ) +#ifdef LIGHT_COMPRESSION_STATS + count = 0; +#endif +} + +SparseLightStorage::~SparseLightStorage() +{ + unsigned char *indicesAndData = (unsigned char *)(dataAndCount & 0x0000ffffffffffff); + // Determine correct means to free this data - could have been allocated either with XPhysicalAlloc or malloc + +#ifdef _XBOX + if( (unsigned int)indicesAndData >= MM_PHYSICAL_4KB_BASE ) + { + XPhysicalFree(indicesAndData); + } + else +#endif + { + free(indicesAndData); + } +// printf("Free (in dtor) 0x%x\n", indicesAndData); +} + +SparseLightStorage::SparseLightStorage(SparseLightStorage *copyFrom) +{ + // Extra details of source storage + __int64 sourceDataAndCount = copyFrom->dataAndCount; + unsigned char *sourceIndicesAndData = (unsigned char *)(sourceDataAndCount & 0x0000ffffffffffff); + int sourceCount = (sourceDataAndCount >> 48 ) & 0xffff; + + // Allocate & copy indices ( 128 bytes ) and any allocated planes (128 * count) + unsigned char *destIndicesAndData = (unsigned char *)malloc( sourceCount * 128 + 128 ); + + // AP - I've moved this to be before the memcpy because of a very strange bug on vita. Sometimes dataAndCount wasn't valid in time when ::get was called. + // This should never happen and this isn't a proper solution but fixes it for now. +#pragma warning ( disable : 4826 ) + dataAndCount = ( sourceDataAndCount & 0xffff000000000000L ) | ( ((__int64) destIndicesAndData ) & 0x0000ffffffffffffL ); +#pragma warning ( default : 4826 ) + + XMemCpy( destIndicesAndData, sourceIndicesAndData, sourceCount * 128 + 128 ); + +#ifdef LIGHT_COMPRESSION_STATS + count = sourceCount; +#endif +} + +// Set all lighting values from a data array of length 16384 (128 x 16 x 16 x 0.5). Source data must have same order as original java game +void SparseLightStorage::setData(byteArray dataIn, unsigned int inOffset) +{ + // Original order is defined as: + // pos = (x << 11 | z << 7 | y); + // slot = pos >> 1; + // part = pos & 1; + // if ( part == 0 ) value = data[slot] & 0xf + // else value = (data[slot] >> 4) & 0xf + + // Two passed through the data. First pass sets up plane indices, and counts number of planes that we actually need to allocate + int allocatedPlaneCount = 0; + unsigned char _planeIndices[128]; + + for( int y = 0; y < 128; y++ ) + { + bool all0 = true; + bool all15 = true; + + for( int xz = 0; xz < 256; xz++ ) // 256 in loop as 16 x 16 separate bytes need checked + { + int pos = ( xz << 7 ) | y; + int slot = pos >> 1; + int part = pos & 1; + unsigned char value = ( dataIn[slot + inOffset] >> (part * 4) ) & 15; + if( value != 0 ) all0 = false; + if( value != 15 ) all15 = false; + } + if( all0 ) + { + _planeIndices[y] = ALL_0_INDEX; + } + else if( all15 ) + { + _planeIndices[y] = ALL_15_INDEX; + } + else + { + _planeIndices[y] = allocatedPlaneCount++; + } + } + + // Allocate required storage + unsigned char *planeIndices = (unsigned char *)malloc(128 * allocatedPlaneCount + 128); + unsigned char *data = planeIndices + 128; + XMemCpy(planeIndices, _planeIndices, 128); + + // Second pass through to actually copy the data in to the storage allocated for the required planes + unsigned char *pucOut = data; + for( int y = 0; y < 128 ; y++ ) + { + // Index will be < 128 if we allocated storage for it and it has a valid index. No need to actually check the index as + // we know they were sequentially allocated above. + if( planeIndices[y] < 128 ) + { + int part = y & 1; + //int shift = 4 * part; + unsigned char *pucIn = &dataIn[ (y >> 1) + inOffset]; + + for( int xz = 0; xz < 128; xz++ ) // 128 ( 16 x 16 x 0.5 ) in loop as packing 2 values into each destination byte + { + *pucOut = ( ( *pucIn ) >> ( part * 4 ) ) & 15; + pucIn += 64; + + *pucOut |= ( ( ( *pucIn ) >> ( part * 4 ) ) & 15 ) << 4; + pucIn += 64; + pucOut++; + } + } + } + + // Get new data and count packed info +#pragma warning ( disable : 4826 ) + __int64 newDataAndCount = ((__int64) planeIndices) & 0x0000ffffffffffffL; +#pragma warning ( default : 4826 ) + newDataAndCount |= ((__int64)allocatedPlaneCount) << 48; + + updateDataAndCount( newDataAndCount ); +} + +// Gets all lighting values into an array of length 16384. Destination data will have same order as original java game. +void SparseLightStorage::getData(byteArray retArray, unsigned int retOffset) +{ + XMemSet(retArray.data + retOffset, 0, 16384); + unsigned char *planeIndices, *data; + getPlaneIndicesAndData(&planeIndices, &data); + + // Original order is defined as: + // pos = (x << 11 | z << 7 | y); + // slot = pos >> 1; + // part = pos & 1; + // if ( part == 0 ) value = data[slot] & 0xf + // else value = (data[slot] >> 4) & 0xf + + for( int y = 0; y < 128; y++ ) + { + if( planeIndices[y] == ALL_0_INDEX ) + { + // No need to do anything in this case as retArray is initialised to zero + } + else if( planeIndices[y] == ALL_15_INDEX ) + { + int part = y & 1; + unsigned char value = 15 << ( part * 4 ); + unsigned char *pucOut = &retArray.data[ (y >> 1) + retOffset]; + for( int xz = 0; xz < 256; xz++ ) + { + *pucOut |= value; + pucOut += 64; + } + } + else + { + int part = y & 1; + int shift = 4 * part; + unsigned char *pucOut = &retArray.data[ (y >> 1) + retOffset]; + unsigned char *pucIn = &data[ planeIndices[ y ] * 128 ]; + for( int xz = 0; xz < 128; xz++ ) // 128 in loop (16 x 16 x 0.5) as input data is being treated in pairs of nybbles that are packed in the same byte + { + unsigned char value = (*pucIn) & 15; + *pucOut |= ( value << shift ); + pucOut += 64; + + value = ((*pucIn) >> 4 ) & 15; + *pucOut |= ( value << shift ); + pucOut += 64; + + pucIn++; + } + } + } +} + +// Get an individual lighting value +int SparseLightStorage::get(int x, int y, int z) +{ + unsigned char *planeIndices, *data; + getPlaneIndicesAndData(&planeIndices, &data); + + if( planeIndices[y] == ALL_0_INDEX ) + { + return 0; + } + else if ( planeIndices[y] == ALL_15_INDEX ) + { + return 15; + } + else + { + int planeIndex = x * 16 + z; // Index within this xz plane + int byteIndex = planeIndex / 2; // Byte index within the plane (2 tiles stored per byte) + int shift = ( planeIndex & 1 ) * 4; // Bit shift within the byte + int retval = ( data[ planeIndices[y] * 128 + byteIndex ] >> shift ) & 15; + + return retval; + } +} + +// Set an individual lighting value +void SparseLightStorage::set(int x, int y, int z, int val) +{ + unsigned char *planeIndices, *data; + getPlaneIndicesAndData(&planeIndices, &data); + + // If this plane isn't yet allocated, then we might have some extra work to do + if( planeIndices[y] >= ALL_0_INDEX ) + { + // No data allocated. Early out though if we are storing what is already represented by our special index. + if( ( val == 0 ) && ( planeIndices[y] == ALL_0_INDEX ) ) + { + return; + } + if( ( val == 15 ) && ( planeIndices[y] == ALL_15_INDEX ) ) + { + return; + } + + // Reallocate the storage for planes to accomodate one extra + addNewPlane(y); + + // Get pointers again as these may have moved + getPlaneIndicesAndData(&planeIndices, &data); + } + + // Either data was already allocated, or we've just done that. Now store our value into the right place. + + int planeIndex = x * 16 + z; // Index within this xz plane + int byteIndex = planeIndex / 2; // Byte index within the plane (2 tiles stored per byte) + int shift = ( planeIndex & 1 ) * 4; // Bit shift within the byte + int mask = 0xf0 >> shift; + + int idx = planeIndices[y] * 128 + byteIndex; + data[idx] = ( data[idx] & mask ) | ( val << shift ); + +} + +void SparseLightStorage::setAllBright() +{ + unsigned char *planeIndices = (unsigned char *)malloc(128); + for( int i = 0; i < 128; i++ ) + { + planeIndices[i] = ALL_15_INDEX; + } + // Data and count packs together the pointer to our data and the count of planes allocated, which is currently zero +#pragma warning ( disable : 4826 ) + __int64 newDataAndCount = ( (__int64) planeIndices ) & 0x0000ffffffffffffL; +#pragma warning ( default : 4826 ) + + updateDataAndCount( newDataAndCount ); +} + +// Sets a region of lighting values with the data at offset position in the array dataIn - external ordering compatible with java DataLayer +// Note - when data was extracted from the original data layers by LevelChunk::getBlocksAndData, y0 had to have even alignment and y1 - y0 also +// needed to be even as data was packed in nyblles in this dimension, and the code didn't make any attempt to unpack it. This behaviour is copied +// here for compatibility even though our source data isn't packed this way. +// Returns size of data copied. +int SparseLightStorage::setDataRegion(byteArray dataIn, int x0, int y0, int z0, int x1, int y1, int z1, int offset) +{ + // Actual setting of data happens when calling set method so no need to lock here + unsigned char *pucIn = &dataIn.data[offset]; + for( int x = x0; x < x1; x++ ) + { + for( int z = z0; z < z1; z++ ) + { + // Emulate how data was extracted from DataLayer... see comment above + int yy0 = y0 & 0xfffffffe; + int len = ( y1 - y0 ) / 2; + for( int i = 0; i < len; i++ ) + { + int y = yy0 + ( i * 2 ); + + set(x, y, z, (*pucIn) & 15 ); + set(x, y + 1, z, ((*pucIn) >> 4 ) & 15 ); + pucIn++; + } + } + } + ptrdiff_t count = pucIn - &dataIn.data[offset]; + + return (int)count; +} + +// Updates the data at offset position dataInOut with a region of lighting information - external ordering compatible with java DataLayer +// Note - when data was placed in the original data layers by LevelChunk::setBlocksAndData, y0 had to have even alignment and y1 - y0 also +// needed to be even as data was packed in nyblles in this dimension, and the code didn't make any attempt to unpack it. This behaviour is copied +// here for compatibility even though our source data isn't packed this way +// Returns size of data copied. +int SparseLightStorage::getDataRegion(byteArray dataInOut, int x0, int y0, int z0, int x1, int y1, int z1, int offset) +{ + unsigned char *pucOut = &dataInOut.data[offset]; + for( int x = x0; x < x1; x++ ) + { + for( int z = z0; z < z1; z++ ) + { + // Emulate how data was extracted from DataLayer... see comment above + int yy0 = y0 & 0xfffffffe; + int len = ( y1 - y0 ) / 2; + for( int i = 0; i < len; i++ ) + { + int y = yy0 + ( i * 2 ); + + *pucOut = get( x, y, z); + *pucOut |= get( x, y + 1, z) << 4; + pucOut++; + } + } + } + ptrdiff_t count = pucOut - &dataInOut.data[offset]; + + return (int)count; +} + +void SparseLightStorage::addNewPlane(int y) +{ + bool success = false; + do + { + // Get last packed data pointer & count + __int64 lastDataAndCount = dataAndCount; + + // Unpack count & data pointer + int lastLinesUsed = (int)(( lastDataAndCount >> 48 ) & 0xffff); + unsigned char *lastDataPointer = (unsigned char *)(lastDataAndCount & 0x0000ffffffffffff); + + // Find out what to prefill the newly allocated line with + unsigned char planeIndex = lastDataPointer[y]; + int prefill = 0; + if( planeIndex < ALL_0_INDEX ) return; // Something has already allocated this line - we're done + else if( planeIndex == ALL_15_INDEX ) prefill = 255; + + int linesUsed = lastLinesUsed + 1; + + // Allocate new memory storage, copy over anything from old storage, and initialise remainder + unsigned char *dataPointer = (unsigned char *)malloc(linesUsed * 128 + 128); + XMemCpy( dataPointer, lastDataPointer, 128 * lastLinesUsed + 128); + XMemSet( dataPointer + ( 128 * lastLinesUsed ) + 128, prefill, 128 ); + dataPointer[y] = lastLinesUsed; + + // Get new data and count packed info +#pragma warning ( disable : 4826 ) + __int64 newDataAndCount = ((__int64) dataPointer) & 0x0000ffffffffffffL; +#pragma warning ( default : 4826 ) + newDataAndCount |= ((__int64)linesUsed) << 48; + + // Attempt to update the data & count atomically. This command will Only succeed if the data stored at + // dataAndCount is equal to lastDataAndCount, and will return the value present just before the write took place + __int64 lastDataAndCount2 = InterlockedCompareExchangeRelease64( &dataAndCount, newDataAndCount, lastDataAndCount ); + + if( lastDataAndCount2 == lastDataAndCount ) + { + success = true; + // Queue old data to be deleted + queueForDelete( lastDataPointer ); +// printf("Marking for delete 0x%x\n", lastDataPointer); +#ifdef LIGHT_COMPRESSION_STATS + count = linesUsed; +#endif + } + else + { + // If we didn't succeed, queue data that we made to be deleted, and try again + queueForDelete( dataPointer ); +// printf("Marking for delete (fail) 0x%x\n", dataPointer); + } + } while( !success ); +} + +void SparseLightStorage::getPlaneIndicesAndData(unsigned char **planeIndices, unsigned char **data) +{ + unsigned char *indicesAndData = (unsigned char *)(dataAndCount & 0x0000ffffffffffff); + + *planeIndices = indicesAndData; + *data = indicesAndData + 128; + +} + +void SparseLightStorage::queueForDelete(unsigned char *data) +{ + // Add this into a queue for deleting. This shouldn't be actually deleted until tick has been called twice from when + // the data went into the queue. + deleteQueue[deleteQueueIndex].Push( data ); +} + +void SparseLightStorage::tick() +{ + // We have 3 queues for deleting. Always delete from the next one after where we are writing to, so it should take 2 ticks + // before we ever delete something, from when the request to delete it came in + int freeIndex = ( deleteQueueIndex + 1 ) % 3; + +// printf("Free queue: %d, %d\n",deleteQueue[freeIndex].GetEntryCount(),deleteQueue[freeIndex].GetAllocated()); + unsigned char *toFree = NULL; + do + { + toFree = deleteQueue[freeIndex].Pop(); +// if( toFree ) printf("Deleting 0x%x\n", toFree); + // Determine correct means to free this data - could have been allocated either with XPhysicalAlloc or malloc +#ifdef _XBOX + if( (unsigned int)toFree >= MM_PHYSICAL_4KB_BASE ) + { + XPhysicalFree(toFree); + } + else +#endif + { + free(toFree); + } + } while( toFree ); + + deleteQueueIndex = ( deleteQueueIndex + 1 ) % 3; +} + +// Update storage with a new values for dataAndCount, repeating as necessary if other simultaneous writes happen. +void SparseLightStorage::updateDataAndCount(__int64 newDataAndCount) +{ + // Now actually assign this data to the storage. Just repeat until successful, there isn't any useful really that we can merge the results of this + // with any other simultaneous writes that might be happening. + bool success = false; + do + { + __int64 lastDataAndCount = dataAndCount; + unsigned char *lastDataPointer = (unsigned char *)(lastDataAndCount & 0x0000ffffffffffff); + + // Attempt to update the data & count atomically. This command will Only succeed if the data stored at + // dataAndCount is equal to lastDataAndCount, and will return the value present just before the write took place + __int64 lastDataAndCount2 = InterlockedCompareExchangeRelease64( &dataAndCount, newDataAndCount, lastDataAndCount ); + + if( lastDataAndCount2 == lastDataAndCount ) + { + success = true; + // Queue old data to be deleted +// printf("Marking for delete 0x%x (full replace)\n", lastDataPointer); + queueForDelete( lastDataPointer ); + } + } while( !success); + +#ifdef LIGHT_COMPRESSION_STATS + count = ( newDataAndCount >> 48 ) & 0xffff; +#endif + +} + +// Attempt to compress the stored data. This method makes no guarantee of success - if it fails due to something else writing to the storage whilst this is running, then it won't actually do anything. +int SparseLightStorage::compress() +{ + unsigned char _planeIndices[128]; + bool needsCompressed = false; + + __int64 lastDataAndCount = dataAndCount; + + unsigned char *planeIndices = (unsigned char *)(lastDataAndCount & 0x0000ffffffffffff); + unsigned char *data = planeIndices + 128; + + int planesToAlloc = 0; + for( int i = 0; i < 128; i++ ) + { + if( planeIndices[i] == ALL_0_INDEX ) + { + _planeIndices[i] = ALL_0_INDEX; + } + else if( planeIndices[i] == ALL_15_INDEX ) + { + _planeIndices[i] = ALL_15_INDEX; + } + else + { + unsigned char *pucData = &data[ 128 * planeIndices[i] ]; + bool all0 = true; + bool all15 = true; + for( int j = 0; j < 128; j++ ) // 16 x 16 x 4-bits + { + if( *pucData != 0 ) all0 = false; + if( *pucData != 255 ) all15 = false; + pucData++; + } + if( all0 ) + { + _planeIndices[i] = ALL_0_INDEX; + needsCompressed = true; + } + else if ( all15 ) + { + _planeIndices[i] = ALL_15_INDEX; + needsCompressed = true; + } + else + { + _planeIndices[i] = planesToAlloc++; + } + } + } + + if( needsCompressed ) + { + unsigned char *newIndicesAndData = (unsigned char *)malloc( 128 + 128 * planesToAlloc ); + unsigned char *pucData = newIndicesAndData + 128; + XMemCpy( newIndicesAndData, _planeIndices, 128 ); + + for( int i = 0; i < 128; i++ ) + { + if( newIndicesAndData[i] < ALL_0_INDEX ) + { + XMemCpy( pucData, &data[ 128 * planeIndices[i] ], 128 ); + pucData += 128; + } + } + + // Get new data and count packed info +#pragma warning ( disable : 4826 ) + __int64 newDataAndCount = ((__int64) newIndicesAndData) & 0x0000ffffffffffffL; +#pragma warning ( default : 4826 ) + newDataAndCount |= ((__int64)planesToAlloc) << 48; + + // Attempt to update the data & count atomically. This command will Only succeed if the data stored at + // dataAndCount is equal to lastDataAndCount, and will return the value present just before the write took place + __int64 lastDataAndCount2 = InterlockedCompareExchangeRelease64( &dataAndCount, newDataAndCount, lastDataAndCount ); + + if( lastDataAndCount2 != lastDataAndCount ) + { + // Failed to write. Don't bother trying again... being very conservative here. +// printf("Marking for delete 0x%x (compress fail)\n", newIndicesAndData); + queueForDelete( newIndicesAndData ); + } + else + { + // Success + queueForDelete( planeIndices ); +// printf("Successfully compressed to %d planes, to delete 0x%x\n", planesToAlloc, planeIndices); +#ifdef LIGHT_COMPRESSION_STATS + count = planesToAlloc; +#endif + } + + return planesToAlloc; + } + else + { + return (int)((lastDataAndCount >> 48 ) & 0xffff); + } +} + + +bool SparseLightStorage::isCompressed() +{ + + int count = ( dataAndCount >> 48 ) & 0xffff; + return (count < 127); + + +} + +void SparseLightStorage::write(DataOutputStream *dos) +{ + int count = ( dataAndCount >> 48 ) & 0xffff; + dos->writeInt(count); + unsigned char *dataPointer = (unsigned char *)(dataAndCount & 0x0000ffffffffffff); + byteArray wrapper(dataPointer, count * 128 + 128); + dos->write(wrapper); +} + +void SparseLightStorage::read(DataInputStream *dis) +{ + int count = dis->readInt(); + unsigned char *dataPointer = (unsigned char *)malloc(count * 128 + 128); + byteArray wrapper(dataPointer, count * 128 + 128); + dis->readFully(wrapper); + +#pragma warning ( disable : 4826 ) + __int64 newDataAndCount = ((__int64) dataPointer) & 0x0000ffffffffffffL; +#pragma warning ( default : 4826 ) + newDataAndCount |= ((__int64)count) << 48; + + updateDataAndCount( newDataAndCount ); +} diff --git a/Minecraft.World/SparseLightStorage.h b/Minecraft.World/SparseLightStorage.h new file mode 100644 index 00000000..d05d7645 --- /dev/null +++ b/Minecraft.World/SparseLightStorage.h @@ -0,0 +1,87 @@ +#pragma once +#include "xmcore.h" + +// 4J added - Storage for block & sky light data. Lighting data is normally stored as 4-bits per tile, in a DataLayer class of 16384 bytes ( 128 x 16 x 16 x 0.5 ) +// This class provides more economical storage for such data by taking into consideration that it is quite common for large parts of the lighting data in a level to +// be very compressible (large amounts of 0 for block lights, 0 and 15 for sky lights). +// We are aiming here to balance performance (lighting data is accessed very frequently) against size. + +// Details of storage method: + +// 1. Lighting is split into horizontal planes, of which there are 128, and each taking up 128 bytes (16 x 16 x 0.5) +// 2. Each of these layers has a permanently allocated index in this class (planeIndices). +// 3. Data for allocatedPlaneCount planes worth of data is allocated in the data array ( allocatedPlaneCount * 128 bytes ) +// 4. If a plane index for a layer is < 128, then the data for that layer is at data[ index * 128 ] +// 5. If a plane index for a layer is 128, then all values for that plane are 0 +// 6. If a plane index for a layer is 129, then all values for that plane are 15 + +// This class needs to be thread safe as there are times where chunk (and light) data are shared between server & main threads. Light values are queried +// very regularly so this needs to be as light-weight as possible. + +// To meet these requirements, this class is now implemented using a lock-free system, implemented using a read-copy-update (RCU) type algorithm. Some details... + +// (1) The storage details for the class are now packed into a single __int64, which contains both a pointer to the data that is required and a count of how many planes worth +// of storage are allocated. This allows the full storage to be updated atomically using compare and exchange operations (implemented with InterlockedCompareExchangeRelease64). +// (2) The data pointer referenced in this __int64 points to an area of memory which is 128 + 128 * plane_count bytes long, where the first 128 bytes stoere the plane indices, and +// the rest of the data is variable in size to accomodate however many planes are required to be stored +// (3) The RCU bit of the algorithm means that any read operations don't need to do any checks or locks at all. When the data needs to be updated, a copy of it is made and updated, +// then an attempt is made to swap the new data in - if this succeeds then the old data pointer is deleted later at some point where we know nothing will be reading from it anymore. +// This is achieved by putting the delete request in a queue which means it won't actually get deleted until 2 game ticks after the last time its reference existed, which should give +// us a large margin of safety. If the attempt to swap the new data in fails, then the whole write operation has to be attempted again - this is the only time there is really a +// high cost for this algorithm and such write collisions should be rare. + +//#define LIGHT_COMPRESSION_STATS +class TileCompressData_SPU; + +class SparseLightStorage +{ + friend class TileCompressData_SPU; +private: +// unsigned char planeIndices[128]; + __int64 dataAndCount; // Contains packed-together data pointer (lower 48-bits), and count of lines used (upper 16-bits) + +// unsigned char *data; +// unsigned int allocatedPlaneCount; + + static const int ALL_0_INDEX = 128; + static const int ALL_15_INDEX = 129; +#ifdef _XBOX + static const unsigned int MM_PHYSICAL_4KB_BASE = 0xE0000000; // Start of where 4KB page sized physical allocations are made +#endif +public: + SparseLightStorage(bool sky); + SparseLightStorage(bool sky, bool isUpper); + SparseLightStorage(SparseLightStorage *copyFrom); // ctor with deep copy + ~SparseLightStorage(); + + void setData(byteArray dataIn, unsigned int inOffset); // Set all lighting values from a data array of length 16384 (128 x 16 x 16 x 0.5). + void getData(byteArray retArray, unsigned int retOffset); // Gets all lighting values into an array of length 16384. + int get(int x, int y, int z); // Get an individual lighting value + void set(int x, int y, int z, int val); // Set an individual lighting value + void setAllBright(); // Set all lighting values to fully bright + int setDataRegion(byteArray dataIn, int x0, int y0, int z0, int x1, int y1, int z1, int offset); // Sets a region of lighting values with the data at offset position in the array dataIn - external ordering compatible with java DataLayer + int getDataRegion(byteArray dataInOut, int x0, int y0, int z0, int x1, int y1, int z1, int offset); // Updates the data at offset position dataInOut with a region of lighting information - external ordering compatible with java DataLayer + + static void staticCtor(); + + void addNewPlane(int y); + void getPlaneIndicesAndData(unsigned char **planeIndices, unsigned char **data); + void updateDataAndCount(__int64 newDataAndCount); + int compress(); + + bool isCompressed(); + void queueForDelete(unsigned char *data); + + static void tick(); + static int deleteQueueIndex; + static XLockFreeStack deleteQueue[3]; + + +#ifdef LIGHT_COMPRESSION_STATS + int count; +#endif + + void write(DataOutputStream *dos); + void read(DataInputStream *dis); +}; + diff --git a/Minecraft.World/Spider.cpp b/Minecraft.World/Spider.cpp new file mode 100644 index 00000000..ba58ce54 --- /dev/null +++ b/Minecraft.World/Spider.cpp @@ -0,0 +1,192 @@ +#include "stdafx.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.effect.h" +#include "net.minecraft.world.entity.h" +#include "com.mojang.nbt.h" +#include "Spider.h" +#include "..\Minecraft.Client\Textures.h" +#include "SoundTypes.h" + + + +Spider::Spider(Level *level) : Monster( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_SPIDER; // 4J was L"/mob/spider.png"; + this->setSize(1.4f, 0.9f); + runSpeed = 0.8f; +} + +void Spider::defineSynchedData() +{ + Monster::defineSynchedData(); + + entityData->define(DATA_FLAGS_ID, (byte) 0); +} + +void Spider::tick() +{ + Monster::tick(); + + if (!level->isClientSide) + { + // this is to synchronize the spiders' climb state + // in multiplayer (to stop them from "flashing") + setClimbing(horizontalCollision); + } +} + +int Spider::getMaxHealth() +{ + return 16; +} + +double Spider::getRideHeight() +{ + return bbHeight * 0.75 - 0.5f; +} + +bool Spider::makeStepSound() +{ + return false; +} + +shared_ptr Spider::findAttackTarget() +{ +#ifndef _FINAL_BUILD +#ifdef _DEBUG_MENUS_ENABLED + if(app.GetMobsDontAttackEnabled()) + { + return shared_ptr(); + } +#endif +#endif + + float br = getBrightness(1); + if (br < 0.5f) + { + double maxDist = 16; + return level->getNearestAttackablePlayer(shared_from_this(), maxDist); + } + return shared_ptr(); +} + +int Spider::getAmbientSound() +{ + return eSoundType_MOB_SPIDER_AMBIENT; +} + +int Spider::getHurtSound() +{ + return eSoundType_MOB_SPIDER_AMBIENT; +} + +int Spider::getDeathSound() +{ + return eSoundType_MOB_SPIDER_DEATH; +} + +void Spider::checkHurtTarget(shared_ptr target, float d) +{ + float br = getBrightness(1); + if (br > 0.5f && random->nextInt(100) == 0) + { + this->attackTarget = nullptr; + return; + } + + if (d > 2 && d < 6 && random->nextInt(10) == 0) + { + if (onGround) + { + double xdd = target->x - x; + double zdd = target->z - z; + float dd = (float) sqrt(xdd * xdd + zdd * zdd); + xd = (xdd / dd * 0.5f) * 0.8f + xd * 0.2f; + zd = (zdd / dd * 0.5f) * 0.8f + zd * 0.2f; + yd = 0.4f; + } + } + else + { + Monster::checkHurtTarget(target, d); + } +} + +int Spider::getDeathLoot() +{ + return Item::string->id; +} + +void Spider::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + Monster::dropDeathLoot(wasKilledByPlayer, playerBonusLevel); + + if (wasKilledByPlayer && (random->nextInt(3) == 0 || random->nextInt(1 + playerBonusLevel) > 0)) + { + spawnAtLocation(Item::spiderEye_Id, 1); + } +} + +/** + * The the spiders act as if they're always on a ladder, which enables them + * to climb walls. + */ + +bool Spider::onLadder() +{ + return isClimbing(); +} + +void Spider::makeStuckInWeb() +{ + // do nothing - spiders don't get stuck in web +} + +float Spider::getModelScale() +{ + return 1.0f; +} + +MobType Spider::getMobType() +{ + return ARTHROPOD; +} + +bool Spider::canBeAffected(MobEffectInstance *newEffect) +{ + if (newEffect->getId() == MobEffect::poison->id) + { + return false; + } + return Monster::canBeAffected(newEffect); +} + +bool Spider::isClimbing() +{ + return (entityData->getByte(DATA_FLAGS_ID) & 0x1) != 0; +} + +void Spider::setClimbing(bool value) +{ + byte flags = entityData->getByte(DATA_FLAGS_ID); + if (value) + { + flags |= 0x1; + } + else + { + flags &= ~0x1; + } + entityData->set(DATA_FLAGS_ID, flags); +} \ No newline at end of file diff --git a/Minecraft.World/Spider.h b/Minecraft.World/Spider.h new file mode 100644 index 00000000..f0257d6c --- /dev/null +++ b/Minecraft.World/Spider.h @@ -0,0 +1,45 @@ +#pragma once +using namespace std; + +#include "Monster.h" + +class Spider : public Monster +{ +public: + eINSTANCEOF GetType() { return eTYPE_SPIDER; } + static Entity *create(Level *level) { return new Spider(level); } + +private: + static const int DATA_FLAGS_ID = 16; + +public: + Spider(Level *level); + +protected: + virtual void defineSynchedData(); + +public: + virtual void tick(); + virtual int getMaxHealth(); + virtual double getRideHeight(); + +protected: + virtual bool makeStepSound(); + virtual shared_ptr findAttackTarget(); + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual void checkHurtTarget(shared_ptr target, float d); + virtual int getDeathLoot(); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + +public: + virtual bool onLadder(); + + virtual void makeStuckInWeb(); + virtual float getModelScale(); + virtual MobType getMobType(); + virtual bool canBeAffected(MobEffectInstance *newEffect); + virtual bool isClimbing(); + virtual void setClimbing(bool value); +}; diff --git a/Minecraft.World/SpikeFeature.cpp b/Minecraft.World/SpikeFeature.cpp new file mode 100644 index 00000000..e8886d00 --- /dev/null +++ b/Minecraft.World/SpikeFeature.cpp @@ -0,0 +1,179 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.boss.enderdragon.h" +#include "net.minecraft.world.level.tile.h" +#include "SpikeFeature.h" + +SpikeFeature::SpikeFeature(int tile) +{ + this->tile = tile; + //m_iIndex=0; +} + +bool SpikeFeature::place(Level *level, Random *random, int x, int y, int z) +{ + if (!(level->isEmptyTile(x, y, z) && level->getTile(x, y - 1, z) == tile)) + { + return false; + } + int hh = random->nextInt(32) + 6; + int r = random->nextInt(4) + 1; + + for (int xx = x - r; xx <= x + r; xx++) + for (int zz = z - r; zz <= z + r; zz++) + { + int xd = xx - x; + int zd = zz - z; + if (xd * xd + zd * zd <= r * r + 1) + { + if (level->getTile(xx, y - 1, zz) != tile) + { + return false; + } + } + } + + for (int yy = y; yy < y + hh; yy++) + { + if (yy < Level::genDepth) + { + for (int xx = x - r; xx <= x + r; xx++) + for (int zz = z - r; zz <= z + r; zz++) + { + int xd = xx - x; + int zd = zz - z; + if (xd * xd + zd * zd <= r * r + 1) + { + level->setTile(xx, yy, zz, Tile::obsidian_Id); + } + } + } else break; + } + + shared_ptr enderCrystal = shared_ptr(new EnderCrystal(level)); + enderCrystal->moveTo(x + 0.5f, y + hh, z + 0.5f, random->nextFloat() * 360, 0); + level->addEntity(enderCrystal); + level->setTile(x, y + hh, z, Tile::unbreakable_Id); + + return true; +} + +bool SpikeFeature::placeWithIndex(Level *level, Random *random, int x, int y, int z,int iIndex, int iRadius) +{ + app.DebugPrintf("Spike - %d,%d,%d - index %d\n",x,y,z,iIndex); + + int hh = 12 + (iIndex*3); + + // fill any tiles below the spike + + for (int xx = x - iRadius; xx <= x + iRadius; xx++) + { + for (int zz = z - iRadius; zz <= z + iRadius; zz++) + { + int xd = xx - x; + int zd = zz - z; + if (xd * xd + zd * zd <= iRadius * iRadius + 1) + { + int iTileBelow=1; + + while((y-iTileBelow>-10) && level->getTile(xx, y - iTileBelow, zz) != tile) + { + if(level->isEmptyTile(xx, y - iTileBelow, zz)) + { + // empty tile + level->setTileNoUpdate(xx, y - iTileBelow, zz, Tile::obsidian_Id); + } + else + { + level->setTile(xx, y - iTileBelow, zz, Tile::obsidian_Id); + } + iTileBelow++; + } + } + } + } + + for (int yy = y; yy < y + hh; yy++) + { + if (yy < Level::genDepth) + { + for (int xx = x - iRadius; xx <= x + iRadius; xx++) + { + for (int zz = z - iRadius; zz <= z + iRadius; zz++) + { + int xd = xx - x; + int zd = zz - z; + int iVal = xd * xd + zd * zd; + if ( iVal <= iRadius * iRadius + 1) + { + //level->setTile(xx, yy, zz, Tile::obsidian_Id); + placeBlock(level, xx, yy, zz, Tile::obsidian_Id, 0); + } + } + } + } + else + { + app.DebugPrintf("Breaking out of spike feature\n"); + break; + } + } + + // cap the last spikes with a fence to stop lucky arrows hitting the crystal + + if(iIndex>5) + { + for (int yy = y; yy < y + hh; yy++) + { + if (yy < Level::genDepth) + { + for (int xx = x - 2; xx <= x + 2; xx++) + { + for (int zz = z - 2; zz <= z + 2; zz++) + { + int xd = xx - x; + int zd = zz - z; + int iVal = xd * xd + zd * zd; + if ( iVal >= 2 * 2) + { + if(yy==(y + hh - 1)) + { + placeBlock(level, xx, y + hh, zz, Tile::ironFence_Id, 0); + placeBlock(level, xx, y + hh +1, zz, Tile::ironFence_Id, 0); + placeBlock(level, xx, y + hh +2, zz, Tile::ironFence_Id, 0); + } + } + } + } + } + else + { + app.DebugPrintf("Breaking out of spike feature\n"); + break; + } + } + + // and cap off the top + int yy = y + hh + 3; + + if (yy < Level::genDepth) + { + for (int xx = x - 2; xx <= x + 2; xx++) + { + for (int zz = z - 2; zz <= z + 2; zz++) + { + placeBlock(level, xx, yy, zz, Tile::ironFence_Id, 0); + } + } + } + } + + shared_ptr enderCrystal = shared_ptr(new EnderCrystal(level)); + enderCrystal->moveTo(x + 0.5f, y + hh, z + 0.5f, random->nextFloat() * 360, 0); + level->addEntity(enderCrystal); + placeBlock(level, x, y + hh, z, Tile::unbreakable_Id, 0); + //level->setTile(x, y + hh, z, Tile::unbreakable_Id); + + return true; +} + diff --git a/Minecraft.World/SpikeFeature.h b/Minecraft.World/SpikeFeature.h new file mode 100644 index 00000000..9612b9fa --- /dev/null +++ b/Minecraft.World/SpikeFeature.h @@ -0,0 +1,14 @@ +#pragma once +#include "Feature.h" + +class SpikeFeature : public Feature +{ +private: + int tile; + //int m_iIndex; + +public: + SpikeFeature(int tile); + virtual bool place(Level *level, Random *random, int x, int y, int z); + virtual bool placeWithIndex(Level *level, Random *random, int x, int y, int z,int iIndex, int iRadius); +}; diff --git a/Minecraft.World/Sponge.cpp b/Minecraft.World/Sponge.cpp new file mode 100644 index 00000000..960589e6 --- /dev/null +++ b/Minecraft.World/Sponge.cpp @@ -0,0 +1,11 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" + +#include "Sponge.h" + +const int RANGE = 2; + + +Sponge::Sponge(int id) : Tile(id, Material::sponge) +{ +} \ No newline at end of file diff --git a/Minecraft.World/Sponge.h b/Minecraft.World/Sponge.h new file mode 100644 index 00000000..a488ce62 --- /dev/null +++ b/Minecraft.World/Sponge.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Tile.h" + +class Sponge : public Tile +{ + friend class Tile; +public: + static const int RANGE = 2; + +protected: + Sponge(int id); +}; \ No newline at end of file diff --git a/Minecraft.World/SpringFeature.cpp b/Minecraft.World/SpringFeature.cpp new file mode 100644 index 00000000..f26f0084 --- /dev/null +++ b/Minecraft.World/SpringFeature.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "SpringFeature.h" + +SpringFeature::SpringFeature(int tile) +{ + this->tile = tile; +} + +bool SpringFeature::place(Level *level, Random *random, int x, int y, int z) +{ + // 4J Stu Added to stop spring features generating areas previously place by game rule generation + if(app.getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + bool intersects = levelGenOptions->checkIntersects(x, y, z, x, y, z); + if(intersects) + { + //app.DebugPrintf("Skipping spring feature generation as it overlaps a game rule structure\n"); + return false; + } + } + + if (level->getTile(x, y + 1, z) != Tile::rock_Id) return false; + if (level->getTile(x, y - 1, z) != Tile::rock_Id) return false; + + if (level->getTile(x, y, z) != 0 && level->getTile(x, y, z) != Tile::rock_Id) return false; + + int rockCount = 0; + if (level->getTile(x - 1, y, z) == Tile::rock_Id) rockCount++; + if (level->getTile(x + 1, y, z) == Tile::rock_Id) rockCount++; + if (level->getTile(x, y, z - 1) == Tile::rock_Id) rockCount++; + if (level->getTile(x, y, z + 1) == Tile::rock_Id) rockCount++; + + int holeCount = 0; + if (level->isEmptyTile(x - 1, y, z)) holeCount++; + if (level->isEmptyTile(x + 1, y, z)) holeCount++; + if (level->isEmptyTile(x, y, z - 1)) holeCount++; + if (level->isEmptyTile(x, y, z + 1)) holeCount++; + + if (rockCount == 3 && holeCount == 1) + { + level->setTile(x, y, z, tile); + level->setInstaTick(true); + Tile::tiles[tile]->tick(level, x, y, z, random); + level->setInstaTick(false); + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/SpringFeature.h b/Minecraft.World/SpringFeature.h new file mode 100644 index 00000000..bb904eff --- /dev/null +++ b/Minecraft.World/SpringFeature.h @@ -0,0 +1,14 @@ +#pragma once +#include "Feature.h" + + +class SpringFeature : public Feature +{ +private: + int tile; + +public: + SpringFeature(int tile); + + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/SpringTile.cpp b/Minecraft.World/SpringTile.cpp new file mode 100644 index 00000000..5946fa8c --- /dev/null +++ b/Minecraft.World/SpringTile.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" + +#include "SpringTile.h" + + +SpringTile::SpringTile(int id, int liquidTileId) : Tile(id, Material::water) +{ + this->liquidTileId = liquidTileId; + this->setTicking(true); +} + +void SpringTile::onPlace(Level *level, int x, int y, int z) +{ + Tile::onPlace(level, x, y, z); + if (level->isEmptyTile(x-1, y, z)) level->setTile(x-1, y, z, liquidTileId); + if (level->isEmptyTile(x+1, y, z)) level->setTile(x+1, y, z, liquidTileId); + if (level->isEmptyTile(x, y, z-1)) level->setTile(x, y, z-1, liquidTileId); + if (level->isEmptyTile(x, y, z+1)) level->setTile(x, y, z+1, liquidTileId); +} + +void SpringTile::tick(Level *level, int x, int y, int z, Random *random) +{ + Tile::tick(level, x, y, z, random); + if (level->isEmptyTile(x-1, y, z)) level->setTile(x-1, y, z, liquidTileId); + if (level->isEmptyTile(x+1, y, z)) level->setTile(x+1, y, z, liquidTileId); + if (level->isEmptyTile(x, y, z-1)) level->setTile(x, y, z-1, liquidTileId); + if (level->isEmptyTile(x, y, z+1)) level->setTile(x, y, z+1, liquidTileId); +} \ No newline at end of file diff --git a/Minecraft.World/SpringTile.h b/Minecraft.World/SpringTile.h new file mode 100644 index 00000000..8c069559 --- /dev/null +++ b/Minecraft.World/SpringTile.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Tile.h" + +class SpringTile : public Tile +{ +private: + int liquidTileId; + +protected: + SpringTile(int id, int liquidTileId); + +public: + void onPlace(Level *level, int x, int y, int z); + + void tick(Level *level, int x, int y, int z, Random *random); +}; \ No newline at end of file diff --git a/Minecraft.World/SpruceFeature.cpp b/Minecraft.World/SpruceFeature.cpp new file mode 100644 index 00000000..39d628cf --- /dev/null +++ b/Minecraft.World/SpruceFeature.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "SpruceFeature.h" + +SpruceFeature::SpruceFeature(bool doUpdate) : Feature(doUpdate) +{ +} + +bool SpruceFeature::place(Level *level, Random *random, int x, int y, int z) +{ + // pines can be quite tall + int treeHeight = random->nextInt(4) + 6; + int trunkHeight = 1 + random->nextInt(2); + int topHeight = treeHeight - trunkHeight; + int leafRadius = 2 + random->nextInt(2); + + bool free = true; + // may not be outside of y boundaries + if (y < 1 || y + treeHeight + 1 > Level::maxBuildHeight) + { + return false; + } + + // 4J Stu Added to stop tree features generating areas previously place by game rule generation + if(app.getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + bool intersects = levelGenOptions->checkIntersects(x - leafRadius, y - 1, z - leafRadius, x + leafRadius, y + treeHeight, z + leafRadius); + if(intersects) + { + //app.DebugPrintf("Skipping reeds feature generation as it overlaps a game rule structure\n"); + return false; + } + } + + // make sure there is enough space + for (int yy = y; yy <= y + 1 + treeHeight && free; yy++) + { + int r = 1; + if ((yy - y) < trunkHeight) + { + r = 0; + } + else + { + r = leafRadius; + } + for (int xx = x - r; xx <= x + r && free; xx++) + { + for (int zz = z - r; zz <= z + r && free; zz++) + { + if (yy >= 0 && yy < Level::maxBuildHeight) + { + int tt = level->getTile(xx, yy, zz); + if (tt != 0 && tt != Tile::leaves_Id) free = false; + } + else + { + free = false; + } + } + } + } + + if (!free) return false; + + // must stand on ground + int belowTile = level->getTile(x, y - 1, z); + if ((belowTile != Tile::grass_Id && belowTile != Tile::dirt_Id) || y >= Level::maxBuildHeight - treeHeight - 1) return false; + + placeBlock(level, x, y - 1, z, Tile::dirt_Id); + + // place leaf top + int currentRadius = random->nextInt(2); + int maxRadius = 1; + int minRadius = 0; + for (int heightPos = 0; heightPos <= topHeight; heightPos++) + { + + const int yy = y + treeHeight - heightPos; + + for (int xx = x - currentRadius; xx <= x + currentRadius; xx++) + { + int xo = xx - (x); + for (int zz = z - currentRadius; zz <= z + currentRadius; zz++) + { + int zo = zz - (z); + if (abs(xo) == currentRadius && abs(zo) == currentRadius && currentRadius > 0) continue; + if (!Tile::solid[level->getTile(xx, yy, zz)]) placeBlock(level, xx, yy, zz, Tile::leaves_Id, LeafTile::EVERGREEN_LEAF); + } + } + + if (currentRadius >= maxRadius) + { + currentRadius = minRadius; + minRadius = 1; + maxRadius += 1; + if (maxRadius > leafRadius) + { + maxRadius = leafRadius; + } + } + else + { + currentRadius = currentRadius + 1; + } + } + int topOffset = random->nextInt(3); + for (int hh = 0; hh < treeHeight - topOffset; hh++) + { + int t = level->getTile(x, y + hh, z); + if (t == 0 || t == Tile::leaves_Id) placeBlock(level, x, y + hh, z, Tile::treeTrunk_Id, TreeTile::DARK_TRUNK); + } + return true; +} diff --git a/Minecraft.World/SpruceFeature.h b/Minecraft.World/SpruceFeature.h new file mode 100644 index 00000000..79918cc7 --- /dev/null +++ b/Minecraft.World/SpruceFeature.h @@ -0,0 +1,9 @@ +#pragma once +#include "Feature.h" + +class SpruceFeature : public Feature +{ +public: + SpruceFeature(bool doUpdate); + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; diff --git a/Minecraft.World/Squid.cpp b/Minecraft.World/Squid.cpp new file mode 100644 index 00000000..3e36777c --- /dev/null +++ b/Minecraft.World/Squid.cpp @@ -0,0 +1,189 @@ +using namespace std; + +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "SharedConstants.h" +#include "Squid.h" +#include "..\Minecraft.Client\Textures.h" + + + +void Squid::_init() +{ + xBodyRot = xBodyRotO = 0.0f; + zBodyRot = zBodyRotO = 0.0f; + + tentacleMovement = oldTentacleMovement = 0.0f; + tentacleAngle = oldTentacleAngle = 0.0f; + + speed = 0.0f; + tentacleSpeed = 0.0f; + rotateSpeed = 0.0f; + + tx = ty = tz = 0.0f; +} + +Squid::Squid(Level *level) : WaterAnimal( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + _init(); + this->textureIdx = TN_MOB_SQUID; // 4J - was L"/mob/squid.png"; + this->setSize(0.95f, 0.95f); + tentacleSpeed = 1 / (random->nextFloat() + 1) * 0.2f; +} + +int Squid::getMaxHealth() +{ + return 10; +} + +int Squid::getAmbientSound() +{ + return -1; +} + +int Squid::getHurtSound() +{ + return -1; +} + +int Squid::getDeathSound() +{ + return -1; +} + +float Squid::getSoundVolume() +{ + return 0.4f; +} + +int Squid::getDeathLoot() +{ + return 0; +} + +void Squid::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + int count = random->nextInt(3 + playerBonusLevel) + 1; + for (int i = 0; i < count; i++) + { + spawnAtLocation(shared_ptr( new ItemInstance(Item::dye_powder, 1, DyePowderItem::BLACK) ), 0.0f); + } +} + +bool Squid::isInWater() +{ + return level->checkAndHandleWater(bb->grow(0, -0.6f, 0), Material::water, shared_from_this() ); +} + +void Squid::aiStep() +{ + WaterAnimal::aiStep(); + + xBodyRotO = xBodyRot; + zBodyRotO = zBodyRot; + + oldTentacleMovement = tentacleMovement; + oldTentacleAngle = tentacleAngle; + + tentacleMovement += tentacleSpeed; + if (tentacleMovement > (float) PI * 2.0f) + { + tentacleMovement -= (float) PI * 2.0f; + if (random->nextInt(10) == 0) tentacleSpeed = 1 / (random->nextFloat() + 1) * 0.2f; + } + + if (isInWater()) + { + if (tentacleMovement < PI) + { + float tentacleScale = tentacleMovement / PI; + tentacleAngle = Mth::sin(tentacleScale * tentacleScale * PI) * PI * 0.25f; + + if (tentacleScale > .75) + { + speed = 1.0f; + rotateSpeed = 1.0f; + } + else + { + rotateSpeed = rotateSpeed * 0.8f; + } + } + else + { + tentacleAngle = 0.0f; + speed = speed * 0.9f; + rotateSpeed = rotateSpeed * 0.99f; + } + + if (!level->isClientSide) + { + xd = tx * speed; + yd = ty * speed; + zd = tz * speed; + } + + double horizontalMovement = sqrt(xd * xd + zd * zd); + + yBodyRot += ((-(float) atan2(this->xd, this->zd) * 180 / PI) - yBodyRot) * 0.1f; + yRot = yBodyRot; + zBodyRot = zBodyRot + (float) PI * rotateSpeed * 1.5f; + xBodyRot += ((-(float) atan2(horizontalMovement, this->yd) * 180 / PI) - xBodyRot) * 0.1f; + } + else + { + tentacleAngle = Mth::abs(Mth::sin(tentacleMovement)) * PI * 0.25f; + + if (!level->isClientSide) + { + // unable to move, apply gravity + xd = 0.0f; + yd -= 0.08; + yd *= 0.98f; + zd = 0.0f; + } + + // fall over + xBodyRot += (-90 - xBodyRot) * 0.02f; + } +} + +void Squid::travel(float xa, float ya) +{ + move(xd, yd, zd); +} + +void Squid::serverAiStep() +{ + noActionTime++; + + // ridiculous simple movement ai + if (noActionTime > SharedConstants::TICKS_PER_SECOND * 5) + { + tx = ty = tz = 0; + } + else if (random->nextInt(50) == 0 || !wasInWater || (tx == 0 && ty == 0 && tz == 0)) + { + float angle = random->nextFloat() * PI * 2.0f; + tx = Mth::cos(angle) * 0.2f; + ty = -0.1f + random->nextFloat() * 0.2f; + tz = Mth::sin(angle) * 0.2f; + } + checkDespawn(); // 4J - 1.7.0 fix +} + +bool Squid::canSpawn() +{ + return y > 45 && y < level->seaLevel && WaterAnimal::canSpawn(); +} diff --git a/Minecraft.World/Squid.h b/Minecraft.World/Squid.h new file mode 100644 index 00000000..d5dc584c --- /dev/null +++ b/Minecraft.World/Squid.h @@ -0,0 +1,52 @@ +#pragma once + +using namespace std; + +#include "WaterAnimal.h" + +class Player; + +class Squid : public WaterAnimal +{ +public: + eINSTANCEOF GetType() { return eTYPE_SQUID; } + static Entity *create(Level *level) { return new Squid(level); } + + void _init(); + + float xBodyRot, xBodyRotO; + float zBodyRot, zBodyRotO; + + float tentacleMovement, oldTentacleMovement; + float tentacleAngle, oldTentacleAngle; + +private: + float speed; + float tentacleSpeed; + float rotateSpeed; + + float tx, ty, tz; + +public: + Squid(Level *level); + virtual int getMaxHealth(); + +protected: + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual float getSoundVolume(); + virtual int getDeathLoot(); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + +public: + virtual bool isInWater(); + virtual void aiStep(); + virtual void travel(float xa, float ya); + +protected: + virtual void serverAiStep(); + +public: + virtual bool canSpawn(); +}; diff --git a/Minecraft.World/StairTile.cpp b/Minecraft.World/StairTile.cpp new file mode 100644 index 00000000..7a80e9ff --- /dev/null +++ b/Minecraft.World/StairTile.cpp @@ -0,0 +1,567 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.h" +#include "StairTile.h" + +int StairTile::DEAD_SPACES[8][2] = { + {2, 6}, {3, 7}, {2, 3}, {6, 7}, + {0, 4}, {1, 5}, {0, 1}, {4, 5} + }; + +StairTile::StairTile(int id, Tile *base,int basedata) : Tile(id, base->material, isSolidRender()) +{ + this->base = base; + this->basedata = basedata; + isClipping = false; + clipStep = 0; + setDestroyTime(base->destroySpeed); + setExplodeable(base->explosionResistance / 3); + setSoundType(base->soundType); + setLightBlock(255); +} + +void StairTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + if (isClipping) + { + setShape(0.5f * (clipStep % 2), 0.5f * (clipStep / 2 % 2), 0.5f * (clipStep / 4 % 2), 0.5f + 0.5f * (clipStep % 2), 0.5f + 0.5f * (clipStep / 2 % 2), 0.5f + 0.5f * (clipStep / 4 % 2)); + } + else + { + setShape(0, 0, 0, 1, 1, 1); + } +} + +bool StairTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool StairTile::isCubeShaped() +{ + return false; +} + +int StairTile::getRenderShape() +{ + return Tile::SHAPE_STAIRS; +} + +void StairTile::setBaseShape(LevelSource *level, int x, int y, int z) +{ + int data = level->getData(x, y, z); + + if ((data & UPSIDEDOWN_BIT) != 0) + { + setShape(0, .5f, 0, 1, 1, 1); + } + else + { + setShape(0, 0, 0, 1, .5f, 1); + } +} + +bool StairTile::isStairs(int id) +{ + StairTile *st = dynamic_cast(Tile::tiles[id]); + return id > 0 && st != NULL; //Tile::tiles[id] instanceof StairTile; +} + +bool StairTile::isLockAttached(LevelSource *level, int x, int y, int z, int data) +{ + int lockTile = level->getTile(x, y, z); + if (isStairs(lockTile) && level->getData(x, y, z) == data) + { + return true; + } + + return false; +} + +bool StairTile::setStepShape(LevelSource *level, int x, int y, int z) +{ + int data = level->getData(x, y, z); + int dir = data & 0x3; + + float bottom = 0.5f; + float top = 1.0f; + + if ((data & UPSIDEDOWN_BIT) != 0) + { + bottom = 0; + top = .5f; + } + + float west = 0; + float east = 1; + float north = 0; + float south = .5f; + + bool checkInnerPiece = true; + + if (dir == DIR_EAST) + { + west = .5f; + south = 1; + + int backTile = level->getTile(x + 1, y, z); + int backData = level->getData(x + 1, y, z); + if (isStairs(backTile) && ((data & UPSIDEDOWN_BIT) == (backData & UPSIDEDOWN_BIT))) + { + int backDir = backData & 0x3; + if (backDir == DIR_NORTH && !isLockAttached(level, x, y, z + 1, data)) + { + south = .5f; + checkInnerPiece = false; + } + else if (backDir == DIR_SOUTH && !isLockAttached(level, x, y, z - 1, data)) + { + north = .5f; + checkInnerPiece = false; + } + } + } + else if (dir == DIR_WEST) + { + east = .5f; + south = 1; + + int backTile = level->getTile(x - 1, y, z); + int backData = level->getData(x - 1, y, z); + if (isStairs(backTile) && ((data & UPSIDEDOWN_BIT) == (backData & UPSIDEDOWN_BIT))) + { + int backDir = backData & 0x3; + if (backDir == DIR_NORTH && !isLockAttached(level, x, y, z + 1, data)) + { + south = .5f; + checkInnerPiece = false; + } + else if (backDir == DIR_SOUTH && !isLockAttached(level, x, y, z - 1, data)) + { + north = .5f; + checkInnerPiece = false; + } + } + } + else if (dir == DIR_SOUTH) + { + north = .5f; + south = 1; + + int backTile = level->getTile(x, y, z + 1); + int backData = level->getData(x, y, z + 1); + if (isStairs(backTile) && ((data & UPSIDEDOWN_BIT) == (backData & UPSIDEDOWN_BIT))) + { + int backDir = backData & 0x3; + if (backDir == DIR_WEST && !isLockAttached(level, x + 1, y, z, data)) + { + east = .5f; + checkInnerPiece = false; + } + else if (backDir == DIR_EAST && !isLockAttached(level, x - 1, y, z, data)) + { + west = .5f; + checkInnerPiece = false; + } + } + } + else if (dir == DIR_NORTH) + { + int backTile = level->getTile(x, y, z - 1); + int backData = level->getData(x, y, z - 1); + if (isStairs(backTile) && ((data & UPSIDEDOWN_BIT) == (backData & UPSIDEDOWN_BIT))) + { + int backDir = backData & 0x3; + if (backDir == DIR_WEST && !isLockAttached(level, x + 1, y, z, data)) + { + east = .5f; + checkInnerPiece = false; + } + else if (backDir == DIR_EAST && !isLockAttached(level, x - 1, y, z, data)) + { + west = .5f; + checkInnerPiece = false; + } + } + } + + setShape(west, bottom, north, east, top, south); + return checkInnerPiece; +} + +/* +* This method adds an extra 1/8 block if the stairs can attach as an +* "inner corner." +*/ +bool StairTile::setInnerPieceShape(LevelSource *level, int x, int y, int z) +{ + int data = level->getData(x, y, z); + int dir = data & 0x3; + + float bottom = 0.5f; + float top = 1.0f; + + if ((data & UPSIDEDOWN_BIT) != 0) + { + bottom = 0; + top = .5f; + } + + float west = 0; + float east = .5f; + float north = .5f; + float south = 1.0f; + + bool hasInnerPiece = false; + + if (dir == DIR_EAST) + { + int frontTile = level->getTile(x - 1, y, z); + int frontData = level->getData(x - 1, y, z); + if (isStairs(frontTile) && ((data & UPSIDEDOWN_BIT) == (frontData & UPSIDEDOWN_BIT))) + { + int frontDir = frontData & 0x3; + if (frontDir == DIR_NORTH && !isLockAttached(level, x, y, z - 1, data)) + { + north = 0; + south = .5f; + hasInnerPiece = true; + } + else if (frontDir == DIR_SOUTH && !isLockAttached(level, x, y, z + 1, data)) + { + north = .5f; + south = 1; + hasInnerPiece = true; + } + } + } + else if (dir == DIR_WEST) + { + int frontTile = level->getTile(x + 1, y, z); + int frontData = level->getData(x + 1, y, z); + if (isStairs(frontTile) && ((data & UPSIDEDOWN_BIT) == (frontData & UPSIDEDOWN_BIT))) + { + west = .5f; + east = 1.0f; + int frontDir = frontData & 0x3; + if (frontDir == DIR_NORTH && !isLockAttached(level, x, y, z - 1, data)) + { + north = 0; + south = .5f; + hasInnerPiece = true; + } + else if (frontDir == DIR_SOUTH && !isLockAttached(level, x, y, z + 1, data)) + { + north = .5f; + south = 1; + hasInnerPiece = true; + } + } + } + else if (dir == DIR_SOUTH) + { + int frontTile = level->getTile(x, y, z - 1); + int frontData = level->getData(x, y, z - 1); + if (isStairs(frontTile) && ((data & UPSIDEDOWN_BIT) == (frontData & UPSIDEDOWN_BIT))) + { + north = 0; + south = .5f; + + int frontDir = frontData & 0x3; + if (frontDir == DIR_WEST && !isLockAttached(level, x - 1, y, z, data)) + { + hasInnerPiece = true; + } + else if (frontDir == DIR_EAST && !isLockAttached(level, x + 1, y, z, data)) + { + west = .5f; + east = 1.0f; + hasInnerPiece = true; + } + } + } + else if (dir == DIR_NORTH) + { + int frontTile = level->getTile(x, y, z + 1); + int frontData = level->getData(x, y, z + 1); + if (isStairs(frontTile) && ((data & UPSIDEDOWN_BIT) == (frontData & UPSIDEDOWN_BIT))) + { + int frontDir = frontData & 0x3; + if (frontDir == DIR_WEST && !isLockAttached(level, x - 1, y, z, data)) + { + hasInnerPiece = true; + } + else if (frontDir == DIR_EAST && !isLockAttached(level, x + 1, y, z, data)) + { + west = .5f; + east = 1.0f; + hasInnerPiece = true; + } + } + } + + if (hasInnerPiece) + { + setShape(west, bottom, north, east, top, south); + } + return hasInnerPiece; +} + +void StairTile::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source) +{ + setBaseShape(level, x, y, z); + Tile::addAABBs(level, x, y, z, box, boxes, source); + + bool checkInnerPiece = setStepShape(level, x, y, z); + Tile::addAABBs(level, x, y, z, box, boxes, source); + + if (checkInnerPiece) + { + if (setInnerPieceShape(level, x, y, z)) + { + Tile::addAABBs(level, x, y, z, box, boxes, source); + } + } + + //int data = level->getData(x, y, z); + //int dir = data & 0x3; + //float lowerPieceY0 = 0; + //float lowerPieceY1 = 0.5f; + //float upperPieceY0 = 0.5f; + //float upperPieceY1 = 1; + + //if ((data & UPSIDEDOWN_BIT) != 0) + //{ + // lowerPieceY0 = .5f; + // lowerPieceY1 = 1; + // upperPieceY0 = 0; + // upperPieceY1 = .5f; + //} + + //setShape(0, lowerPieceY0, 0, 1, lowerPieceY1, 1); + //Tile::addAABBs(level, x, y, z, box, boxes); + + //if (dir == 0) + //{ + // setShape(0.5f, upperPieceY0, 0, 1, upperPieceY1, 1); + // Tile::addAABBs(level, x, y, z, box, boxes); + //} + //else if (dir == 1) + //{ + // setShape(0, upperPieceY0, 0, .5f, upperPieceY1, 1); + // Tile::addAABBs(level, x, y, z, box, boxes); + //} + //else if (dir == 2) + //{ + // setShape(0, upperPieceY0, 0.5f, 1, upperPieceY1, 1); + // Tile::addAABBs(level, x, y, z, box, boxes); + //} + //else if (dir == 3) + //{ + // setShape(0, upperPieceY0, 0, 1, upperPieceY1, .5f); + // Tile::addAABBs(level, x, y, z, box, boxes); + //} + + setShape(0, 0, 0, 1, 1, 1); +} + +/** DELEGATES: **/ + + +void StairTile::addLights(Level *level, int x, int y, int z) +{ + base->addLights(level, x, y, z); +} + +void StairTile::animateTick(Level *level, int x, int y, int z, Random *random) +{ + base->animateTick(level, x, y, z, random); +} + +void StairTile::attack(Level *level, int x, int y, int z, shared_ptr player) +{ + base->attack(level, x, y, z, player); +} + +void StairTile::destroy(Level *level, int x, int y, int z, int data) +{ + base->destroy(level, x, y, z, data); +} + +// 4J - brought forward from 1.8.2 +int StairTile::getLightColor(LevelSource *level, int x, int y, int z, int tileId/*=-1*/) +{ + return base->getLightColor(level, x, y, z, tileId); +} + +float StairTile::getBrightness(LevelSource *level, int x, int y, int z) +{ + return base->getBrightness(level, x, y, z); +} + +float StairTile::getExplosionResistance(shared_ptr source) +{ + return base->getExplosionResistance(source); +} + +int StairTile::getRenderLayer() +{ + return base->getRenderLayer(); +} + +Icon *StairTile::getTexture(int face, int data) +{ + return base->getTexture(face, basedata); +} + +int StairTile::getTickDelay() +{ + return base->getTickDelay(); +} + +AABB *StairTile::getTileAABB(Level *level, int x, int y, int z) +{ + return base->getTileAABB(level, x, y, z); +} + +void StairTile::handleEntityInside(Level *level, int x, int y, int z, shared_ptr e, Vec3 *current) +{ + base->handleEntityInside(level, x, y, z, e, current); +} + +bool StairTile::mayPick() +{ + return base->mayPick(); +} + +bool StairTile::mayPick(int data, bool liquid) +{ + return base->mayPick(data, liquid); +} + +bool StairTile::mayPlace(Level *level, int x, int y, int z) +{ + return base->mayPlace(level, x, y, z); +} + +void StairTile::onPlace(Level *level, int x, int y, int z) +{ + neighborChanged(level, x, y, z, 0); + base->onPlace(level, x, y, z); +} + +void StairTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + base->onRemove(level, x, y, z, id, data); +} + +void StairTile::prepareRender(Level *level, int x, int y, int z) +{ + base->prepareRender(level, x, y, z); +} + +void StairTile::stepOn(Level *level, int x, int y, int z, shared_ptr entity) +{ + base->stepOn(level, x, y, z, entity); +} + +void StairTile::tick(Level *level, int x, int y, int z, Random *random) +{ + base->tick(level, x, y, z, random); +} + +// 4J-HEG - Removed this to prevent weird tooltips (place steak on stairs!?) +//// 4J-PB - Adding a TestUse for tooltip display +//bool StairTile::TestUse() +//{ +// return true; +//} + +bool StairTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if (soundOnly) return false; + return base->use(level, x, y, z, player, 0, 0, 0, 0); +} + +void StairTile::wasExploded(Level *level, int x, int y, int z) +{ + base->wasExploded(level, x, y, z); +} + +void StairTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int dir = ( Mth::floor(by->yRot * 4 / (360) + 0.5) ) & 3; + int usd = level->getData(x, y, z) & UPSIDEDOWN_BIT; + + if (dir == 0) level->setData(x, y, z, DIR_SOUTH | usd); + if (dir == 1) level->setData(x, y, z, DIR_WEST | usd); + if (dir == 2) level->setData(x, y, z, DIR_NORTH | usd); + if (dir == 3) level->setData(x, y, z, DIR_EAST | usd); +} + +int StairTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) +{ + if (face == Facing::DOWN || (face != Facing::UP && clickY > 0.5)) + { + return itemValue | UPSIDEDOWN_BIT; + } + return itemValue; +} + +HitResult *StairTile::clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b) +{ + HitResult *results[8]; + for(unsigned int i = 0; i < 8; ++i) + { + results[i] = NULL; + } + int data = level->getData(xt, yt, zt); + int dir = data & 0x3; + bool upsideDown = (data & UPSIDEDOWN_BIT) == UPSIDEDOWN_BIT; + int *deadSpaces = DEAD_SPACES[dir + (upsideDown ? 4 : 0)]; + + isClipping = true; + for (int i = 0; i < 8; i++) + { + clipStep = i; + + for(unsigned int j = 0; j < DEAD_SPACE_COLUMN_COUNT; ++j) + { + if (deadSpaces[j] == i) continue; + } + + results[i] = Tile::clip(level, xt, yt, zt, a, b); + } + + for(unsigned int j = 0; j < DEAD_SPACE_COLUMN_COUNT; ++j) + { + results[deadSpaces[j]] = NULL; + } + + HitResult *closest = NULL; + double closestDist = 0; + + for (unsigned int i = 0; i < 8; ++i) + { + HitResult *result = results[i]; + if (result != NULL) + { + double dist = result->pos->distanceToSqr(b); + + if (dist > closestDist) + { + closest = result; + closestDist = dist; + } + } + } + + return closest; +} + +void StairTile::registerIcons(IconRegister *iconRegister) +{ + // None +} diff --git a/Minecraft.World/StairTile.h b/Minecraft.World/StairTile.h new file mode 100644 index 00000000..d4797f04 --- /dev/null +++ b/Minecraft.World/StairTile.h @@ -0,0 +1,106 @@ +#pragma once + +#include "Tile.h" + +class Mob; +class Player; + +class StairTile : public Tile +{ + friend class Tile; + +private: + static const int DEAD_SPACE_COLUMN_COUNT = 2; + static int DEAD_SPACES[8][DEAD_SPACE_COLUMN_COUNT]; + +public: + static const int UPSIDEDOWN_BIT = 4; + + // the direction is the way going up (for normal non-upsidedown stairs) + static const int DIR_EAST = 0; + static const int DIR_WEST = 1; + static const int DIR_SOUTH = 2; + static const int DIR_NORTH = 3; + +private: + Tile *base; + int basedata; + bool isClipping; + int clipStep; + +protected: + StairTile(int id, Tile *base, int basedata); + +public: + void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + bool isSolidRender(bool isServerLevel = false); + + bool isCubeShaped(); + + int getRenderShape(); + + void setBaseShape(LevelSource *level, int x, int y, int z); + static bool isStairs(int id); + +private: + bool isLockAttached(LevelSource *level, int x, int y, int z, int data); + +public: + bool setStepShape(LevelSource *level, int x, int y, int z); + bool setInnerPieceShape(LevelSource *level, int x, int y, int z); + + void addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source); + + + /** DELEGATES: **/ +public: + void addLights(Level *level, int x, int y, int z); + + void animateTick(Level *level, int x, int y, int z, Random *random); + + void attack(Level *level, int x, int y, int z, shared_ptr player); + + void destroy(Level *level, int x, int y, int z, int data); + + int getLightColor(LevelSource *level, int x, int y, int z, int tileId = -1); + float getBrightness(LevelSource *level, int x, int y, int z); + + float getExplosionResistance(shared_ptr source); + + int getRenderLayer(); + + Icon *getTexture(int face, int data); + + int getTickDelay(); + + AABB *getTileAABB(Level *level, int x, int y, int z); + + void handleEntityInside(Level *level, int x, int y, int z, shared_ptr e, Vec3 *current); + + bool mayPick(); + + bool mayPick(int data, bool liquid); + + bool mayPlace(Level *level, int x, int y, int z); + + void onPlace(Level *level, int x, int y, int z); + + void onRemove(Level *level, int x, int y, int z, int id, int data); + + void prepareRender(Level *level, int x, int y, int z); + + void stepOn(Level *level, int x, int y, int z, shared_ptr entity); + + void tick(Level *level, int x, int y, int z, Random *random); + + bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + + void wasExploded(Level *level, int x, int y, int z); + + void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + + HitResult *clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b); + + virtual void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/Stat.cpp b/Minecraft.World/Stat.cpp new file mode 100644 index 00000000..868ff3bd --- /dev/null +++ b/Minecraft.World/Stat.cpp @@ -0,0 +1,115 @@ +#include "stdafx.h" +#include "NumberFormaters.h" +#include "StatFormatter.h" +#include "Stats.h" +#include "Stat.h" + +Stat::DefaultFormat *Stat::defaultFormatter = new DefaultFormat(); +Stat::TimeFormatter *Stat::timeFormatter = new TimeFormatter(); +Stat::DistanceFormatter *Stat::distanceFormatter = new DistanceFormatter(); + +// 4J Stu - Changed this to take in a printf format string instead +DecimalFormat *Stat::decimalFormat = new DecimalFormat(L"%0(3).2f"); + +void Stat::_init() +{ + awardLocallyOnly = false; +} + +Stat::Stat(int id, const wstring& name, StatFormatter *formatter) : id(id), name(name), formatter(formatter) +{ + _init(); +} + +Stat::Stat(int id, const wstring& name) : id(id), name(name), formatter(defaultFormatter) +{ + _init(); +} + +Stat *Stat::setAwardLocallyOnly() +{ + awardLocallyOnly = true; + return this; +} + +Stat *Stat::postConstruct() +{ + //if (Stats::statsById->containsKey(id)) + //{ + //throw new RuntimeException("Duplicate stat id: \"" + Stats::statsById->get(id)->name + "\" and \"" + name + "\" at id " + id); 4J - TODO + //} + Stats::all->push_back(this); + + pair id1(id,this); +#ifdef __PS3__ + Stats::statsById->emplace(id1 );// assert(0); // MGH - TODO - FIX - find out where this move function comes from +#else + Stats::statsById->emplace( move(id1) ); +#endif // __PS3__ + + return this; +} + +bool Stat::isAchievement() +{ + return false; +} + +wstring Stat::format(int value) +{ + return ((StatFormatter *)formatter)->format(value); +} + +wstring Stat::toString() +{ + return name; +} + +wstring Stat::TimeFormatter::format(int value) +{ + double seconds = value / 20.0; + double minutes = seconds / 60.0; + double hours = minutes / 60.0; + double days = hours / 24.0; + double years = days / 365.0; + + if (years > 0.5) + { + return decimalFormat->format(years) + L" y"; + } + else if (days > 0.5) + { + return decimalFormat->format(days) + L" d"; + } + else if (hours > 0.5) + { + return decimalFormat->format(hours) + L" h"; + } + else if (minutes > 0.5) + { + return decimalFormat->format(minutes) + L" m"; + } + + return _toString(seconds) + L" s"; +} + +wstring Stat::DefaultFormat::format(int value) +{ + return NumberFormat::format( value ); //numberFormat->format(value); +} + +wstring Stat::DistanceFormatter::format(int cm) +{ + double meters = cm / 100.0; + double kilometers = meters / 1000.0; + + if (kilometers > 0.5) + { + return decimalFormat->format(kilometers) + L" km"; + + } else if (meters > 0.5) + { + return decimalFormat->format(meters) + L" m"; + } + return _toString(cm) + L" cm"; +} diff --git a/Minecraft.World/Stat.h b/Minecraft.World/Stat.h new file mode 100644 index 00000000..3c2ab071 --- /dev/null +++ b/Minecraft.World/Stat.h @@ -0,0 +1,61 @@ +#pragma once +using namespace std; + +#include "StatFormatter.h" +#include "GenericStats.h" + +class DecimalFormat; + +class Stat +{ +public: + const int id; + const wstring name; + bool awardLocallyOnly; + +private: + const StatFormatter *formatter; + void _init(); + +public: + Stat(int id, const wstring& name, StatFormatter *formatter); + Stat(int id, const wstring& name); + Stat *setAwardLocallyOnly(); + + virtual Stat *postConstruct(); + virtual bool isAchievement(); + wstring format(int value); + +private: + //static NumberFormat *numberFormat; + +public: + class DefaultFormat : public StatFormatter + { + public: + wstring format(int value); + } static *defaultFormatter; + +private: + static DecimalFormat *decimalFormat; + +public: + + class TimeFormatter : public StatFormatter + { + public: + wstring format(int value); + } static *timeFormatter; + + class DistanceFormatter : public StatFormatter + { + public: + wstring format(int cm); + } static *distanceFormatter; + + wstring toString(); + +public: + // 4J-JEV, for Durango stats + virtual void handleParamBlob(shared_ptr plr, byteArray param) { app.DebugPrintf("'Stat.h', Unhandled AwardStat blob.\n"); return; } +}; diff --git a/Minecraft.World/StatFormatter.h b/Minecraft.World/StatFormatter.h new file mode 100644 index 00000000..bc502c27 --- /dev/null +++ b/Minecraft.World/StatFormatter.h @@ -0,0 +1,8 @@ +#pragma once +using namespace std; + +class StatFormatter +{ +public: + virtual wstring format(int value) = 0; +}; diff --git a/Minecraft.World/Stats.cpp b/Minecraft.World/Stats.cpp new file mode 100644 index 00000000..ce9ed8a7 --- /dev/null +++ b/Minecraft.World/Stats.cpp @@ -0,0 +1,557 @@ +#include "stdafx.h" +#include "net.minecraft.locale.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.item.crafting.h" +#include "Achievements.h" +#include "ItemStat.h" +#include "GeneralStat.h" +#include "Stats.h" +#include "..\Minecraft.Client\StatsCounter.h" + +const int Stats::BLOCKS_MINED_OFFSET = 0x1000000; +const int Stats::ITEMS_COLLECTED_OFFSET = 0x1010000; +const int Stats::ITEMS_CRAFTED_OFFSET = 0x1020000; +const int Stats::ADDITIONAL_STATS_OFFSET = 0x5010000; // Needs to be higher than Achievements::ACHIEVEMENT_OFFSET = 0x500000; + +unordered_map* Stats::statsById = new unordered_map; + +vector *Stats::all = new vector; +vector *Stats::generalStats = new vector; +vector *Stats::blocksMinedStats = new vector; +vector *Stats::itemsCollectedStats = new vector; +vector *Stats::itemsCraftedStats = new vector; + +#if (defined _EXTENDED_ACHIEVEMENTS) && (!defined _DURANGO) +vector *Stats::blocksPlacedStats = new vector; +#endif + +Stat *Stats::walkOneM = NULL; +Stat *Stats::swimOneM = NULL; +Stat *Stats::fallOneM = NULL; +Stat *Stats::climbOneM = NULL; +Stat *Stats::minecartOneM = NULL; +Stat *Stats::boatOneM = NULL; +Stat *Stats::pigOneM = NULL; +Stat *Stats::portalsCreated = NULL; +Stat *Stats::cowsMilked = NULL; +Stat *Stats::netherLavaCollected = NULL; +Stat *Stats::killsZombie = NULL; +Stat *Stats::killsSkeleton = NULL; +Stat *Stats::killsCreeper = NULL; +Stat *Stats::killsSpider = NULL; +Stat *Stats::killsSpiderJockey = NULL; +Stat *Stats::killsZombiePigman = NULL; +Stat *Stats::killsSlime = NULL; +Stat *Stats::killsGhast = NULL; +Stat *Stats::killsNetherZombiePigman = NULL; + +// 4J : WESTY : Added for new achievements. +Stat *Stats::befriendsWolf = NULL; +Stat *Stats::totalBlocksMined = NULL; +Stat *Stats::timePlayed = NULL; + +StatArray Stats::blocksMined; +StatArray Stats::itemsCollected; +StatArray Stats::itemsCrafted; + +#if (defined _EXTENDED_ACHIEVEMENTS) && (!defined _DURANGO) +StatArray Stats::blocksPlaced; +StatArray Stats::rainbowCollection; +StatArray Stats::biomesVisisted; +#endif + +Stat *Stats::killsEnderdragon = NULL; // The number of times this player has dealt the killing blow to the Enderdragon +Stat *Stats::completeTheEnd = NULL; // The number of times this player has been present when the Enderdragon has died + +void Stats::staticCtor() +{ + Stats::walkOneM = (new GeneralStat(2000, L"stat.walkOneM", (StatFormatter *) Stat::distanceFormatter))->setAwardLocallyOnly()->postConstruct(); + Stats::swimOneM = (new GeneralStat(2001, L"stat.swimOneM", (StatFormatter *) Stat::distanceFormatter))->setAwardLocallyOnly()->postConstruct(); + Stats::fallOneM = (new GeneralStat(2002, L"stat.fallOneM", (StatFormatter *) Stat::distanceFormatter))->setAwardLocallyOnly()->postConstruct(); + Stats::climbOneM = (new GeneralStat(2003, L"stat.climbOneM", (StatFormatter *) Stat::distanceFormatter))->setAwardLocallyOnly()->postConstruct(); + Stats::minecartOneM = (new GeneralStat(2004, L"stat.minecartOneM", (StatFormatter *) Stat::distanceFormatter))->setAwardLocallyOnly()->postConstruct(); + Stats::boatOneM = (new GeneralStat(2005, L"stat.boatOneM", (StatFormatter *) Stat::distanceFormatter))->setAwardLocallyOnly()->postConstruct(); + Stats::pigOneM = (new GeneralStat(2006, L"stat.pigOneM", (StatFormatter *) Stat::distanceFormatter))->setAwardLocallyOnly()->postConstruct(); + Stats::portalsCreated = (new GeneralStat(2007, L"stat.portalsUsed"))->postConstruct(); + Stats::cowsMilked = (new GeneralStat(2008, L"stat.cowsMilked"))->postConstruct(); + Stats::netherLavaCollected = (new GeneralStat(2009, L"stat.netherLavaCollected"))->postConstruct(); + Stats::killsZombie = (new GeneralStat(2010, L"stat.killsZombie"))->postConstruct(); + Stats::killsSkeleton = (new GeneralStat(2011, L"stat.killsSkeleton"))->postConstruct(); + Stats::killsCreeper = (new GeneralStat(2012, L"stat.killsCreeper"))->postConstruct(); + Stats::killsSpider = (new GeneralStat(2013, L"stat.killsSpider"))->postConstruct(); + Stats::killsSpiderJockey = (new GeneralStat(2014, L"stat.killsSpiderJockey"))->postConstruct(); + Stats::killsZombiePigman = (new GeneralStat(2015, L"stat.killsZombiePigman"))->postConstruct(); + Stats::killsSlime = (new GeneralStat(2016, L"stat.killsSlime"))->postConstruct(); + Stats::killsGhast = (new GeneralStat(2017, L"stat.killsGhast"))->postConstruct(); + Stats::killsNetherZombiePigman = (new GeneralStat(2018, L"stat.killsNetherZombiePigman"))->postConstruct(); + + // 4J : WESTY : Added for new achievements. + Stats::befriendsWolf = (new GeneralStat(2019, L"stat.befriendsWolf"))->postConstruct(); + Stats::totalBlocksMined = (new GeneralStat(2020, L"stat.totalBlocksMined"))->postConstruct(); + + // 4J-PB - don't want the time played going to the server + Stats::timePlayed = (new GeneralStat(2021, L"stat.timePlayed"))->setAwardLocallyOnly()->postConstruct(); + +// WARNING: NO NEW STATS CAN BE ADDED HERE +// These stats are directly followed by the achievemnts in the profile data, so cannot be changed without migrating the profile data + + buildBlockStats(); + + Achievements::init(); + Achievements::staticCtor(); + + // 4J Stu - Added this function to allow us to add news stats from TU9 onwards + buildAdditionalStats(); +} + +void Stats::init() +{ +} + +bool Stats::blockStatsLoaded = false; + +// WARNING: NO NEW STATS CAN BE ADDED HERE +// These stats are directly followed by the achievemnts in the profile data, so cannot be changed without migrating the profile data +void Stats::buildBlockStats() +{ + blocksMined = StatArray(32000); + + ItemStat* newStat = new ItemStat(BLOCKS_MINED_OFFSET + 0, L"mineBlock.dirt", Tile::dirt->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::dirt->id] = newStat; + blocksMined[Tile::grass->id] = newStat; + blocksMined[Tile::farmland->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 1, L"mineBlock.stone", Tile::stoneBrick->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::stoneBrick->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 2, L"mineBlock.sand", Tile::sand->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::sand->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 3, L"mineBlock.cobblestone", Tile::rock->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::rock->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 4, L"mineBlock.gravel", Tile::gravel->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::gravel->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 5, L"mineBlock.clay", Tile::clay->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::clay->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 6, L"mineBlock.obsidian", Tile::obsidian->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::obsidian->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 7, L"mineBlock.coal", Tile::coalOre->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::coalOre->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 8, L"mineBlock.iron", Tile::ironOre->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::ironOre->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 9, L"mineBlock.gold", Tile::goldOre->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::goldOre->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 10, L"mineBlock.diamond", Tile::diamondOre->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::diamondOre->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 11, L"mineBlock.redstone", Tile::redStoneOre->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::redStoneOre->id] = newStat; + blocksMined[Tile::redStoneOre_lit->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 12, L"mineBlock.lapisLazuli", Tile::lapisOre->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::lapisOre->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 13, L"mineBlock.netherrack", Tile::hellRock->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::hellRock->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 14, L"mineBlock.soulSand", Tile::hellSand->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::hellSand->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 15, L"mineBlock.glowstone", Tile::lightGem->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::lightGem->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 16, L"mineBlock.wood", Tile::treeTrunk->id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::treeTrunk->id] = newStat; + newStat->postConstruct(); + +// WARNING: NO NEW STATS CAN BE ADDED HERE +// These stats are directly followed by the achievemnts in the profile data, so cannot be changed without migrating the profile data + + + blockStatsLoaded = true; + buildCraftableStats(); +} + +bool Stats::itemStatsLoaded = false; + +void Stats::buildItemStats() +{ + itemStatsLoaded = true; + buildCraftableStats(); +} + +bool Stats::craftableStatsLoaded = false; + + +// WARNING: NO NEW STATS CAN BE ADDED HERE +// These stats are directly followed by the achievemnts in the profile data, so cannot be changed without migrating the profile data +void Stats::buildCraftableStats() +{ + if (!blockStatsLoaded || !itemStatsLoaded || craftableStatsLoaded) + { + // still waiting for the JVM to load stuff + //Or stats already loaded + return; + } + + craftableStatsLoaded = true; + + //Collected stats + + itemsCollected = StatArray(32000); + + ItemStat* newStat = new ItemStat(ITEMS_COLLECTED_OFFSET + 0, L"collectItem.egg", Item::egg->id); + itemsCollectedStats->push_back(newStat); + itemsCollected[Item::egg->id] = newStat; + newStat->postConstruct(); + + // 4J Stu - The following stats were added as it was too easy to cheat the leaderboards by dropping and picking up these items + // They are now changed to mining the block which involves a tiny bit more effort + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 18, L"mineBlock.wheat", Tile::crops_Id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::crops_Id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 19, L"mineBlock.mushroom1", Tile::mushroom1_Id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::mushroom1_Id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(BLOCKS_MINED_OFFSET + 17, L"mineBlock.sugar", Tile::reeds_Id); + blocksMinedStats->push_back(newStat); + blocksMined[Tile::reeds_Id] = newStat; + newStat->postConstruct(); +#if 0 + newStat = new ItemStat(ITEMS_COLLECTED_OFFSET + 1, L"collectItem.wheat", Item::wheat->id); + itemsCollectedStats->push_back(newStat); + itemsCollected[Item::wheat->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_COLLECTED_OFFSET + 2, L"collectItem.mushroom", Tile::mushroom1->id); + itemsCollectedStats->push_back(newStat); + itemsCollected[Tile::mushroom1->id] = newStat; + itemsCollected[Tile::mushroom2->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_COLLECTED_OFFSET + 3, L"collectItem.sugarCane", Item::reeds->id); + itemsCollectedStats->push_back(newStat); + itemsCollected[Item::reeds->id] = newStat; + newStat->postConstruct(); +#endif + + newStat = new ItemStat(ITEMS_COLLECTED_OFFSET + 4, L"collectItem.pumpkin", Tile::pumpkin->id); + itemsCollectedStats->push_back(newStat); + itemsCollected[Tile::pumpkin->id] = newStat; + itemsCollected[Tile::litPumpkin->id] = newStat; + newStat->postConstruct(); + + //Crafted stats + + itemsCrafted = StatArray(32000); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 0, L"craftItem.plank", Tile::wood->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Tile::wood->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 1, L"craftItem.workbench", Tile::workBench->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Tile::workBench->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 2, L"craftItem.stick", Item::stick->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::stick->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 3, L"craftItem.woodenShovel", Item::shovel_wood->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::shovel_wood->id] = newStat; + newStat->postConstruct(); + + // 4J : WESTY : Added for new achievements. + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 4, L"craftItem.woodenPickAxe", Item::pickAxe_wood->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::pickAxe_wood->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 5, L"craftItem.stonePickAxe", Item::pickAxe_stone->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::pickAxe_stone->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 6, L"craftItem.ironPickAxe", Item::pickAxe_iron->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::pickAxe_iron->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 7, L"craftItem.diamondPickAxe", Item::pickAxe_diamond->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::pickAxe_diamond->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 8, L"craftItem.goldPickAxe", Item::pickAxe_gold->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::pickAxe_gold->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 9, L"craftItem.stoneShovel", Item::shovel_stone->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::shovel_stone->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 10, L"craftItem.ironShovel", Item::shovel_iron->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::shovel_iron->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 11, L"craftItem.diamondShovel", Item::shovel_diamond->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::shovel_diamond->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 12, L"craftItem.goldShovel", Item::shovel_gold->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::shovel_gold->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 13, L"craftItem.woodenAxe", Item::hatchet_wood->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::hatchet_wood->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 14, L"craftItem.stoneAxe", Item::hatchet_stone->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::hatchet_stone->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 15, L"craftItem.ironAxe", Item::hatchet_iron->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::hatchet_iron->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 16, L"craftItem.diamondAxe", Item::hatchet_diamond->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::hatchet_diamond->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 17, L"craftItem.goldAxe", Item::hatchet_gold->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::hatchet_gold->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 18, L"craftItem.woodenHoe", Item::hoe_wood->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::hoe_wood->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 19, L"craftItem.stoneHoe", Item::hoe_stone->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::hoe_stone->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 20, L"craftItem.ironHoe", Item::hoe_iron->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::hoe_iron->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 21, L"craftItem.diamondHoe", Item::hoe_diamond->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::hoe_diamond->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 22, L"craftItem.goldHoe", Item::hoe_gold->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::hoe_gold->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 23, L"craftItem.glowstone", Tile::lightGem_Id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Tile::lightGem_Id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 24, L"craftItem.tnt", Tile::tnt_Id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Tile::tnt_Id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 25, L"craftItem.bowl", Item::bowl->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::bowl->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 26, L"craftItem.bucket", Item::bucket_empty->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::bucket_empty->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 27, L"craftItem.flintAndSteel", Item::flintAndSteel->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::flintAndSteel->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 28, L"craftItem.fishingRod", Item::fishingRod->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::fishingRod->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 29, L"craftItem.clock", Item::clock->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::clock->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 30, L"craftItem.compass", Item::compass->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::compass->id] = newStat; + newStat->postConstruct(); + + newStat = new ItemStat(ITEMS_CRAFTED_OFFSET + 31, L"craftItem.map", Item::map->id); + itemsCraftedStats->push_back(newStat); + itemsCrafted[Item::map->id] = newStat; + newStat->postConstruct(); + +// WARNING: NO NEW STATS CAN BE ADDED HERE +// These stats are directly followed by the achievemnts in the profile data, so cannot be changed without migrating the profile data + + //This sets up a static list of stat/leaderboard pairings, used to tell which leaderboards need an update + StatsCounter::setupStatBoards(); +} + +// 4J Stu - Added this function to allow us to add news stats from TU9 onwards +void Stats::buildAdditionalStats() +{ + int offset = ADDITIONAL_STATS_OFFSET; + + // The order of these stats should not be changed, as the map directly to bits in the profile data + + // The number of times this player has dealt the killing blow to the Enderdragon + Stats::killsEnderdragon = (new GeneralStat(offset++, L"stat.killsEnderdragon"))->postConstruct(); + + // The number of times this player has been present when the Enderdragon has died + Stats::completeTheEnd = (new GeneralStat(offset++, L"stat.completeTheEnd"))->postConstruct(); + + +#if (defined _EXTENDED_ACHIEVEMENTS) && (!defined _DURANGO) + { + ItemStat *itemStat = new ItemStat(offset++, L"craftItem.flowerPot", Item::flowerPot_Id); + itemsCraftedStats->push_back(itemStat); + itemsCrafted[itemStat->getItemId()] = itemStat; + itemStat->postConstruct(); + + itemStat = new ItemStat(offset++, L"craftItem.sign", Item::sign_Id); + itemsCraftedStats->push_back(itemStat); + itemsCrafted[itemStat->getItemId()] = itemStat; + itemStat->postConstruct(); + + itemStat = new ItemStat(offset++, L"mineBlock.emerald", Tile::emeraldOre_Id); + blocksMinedStats->push_back(itemStat); + blocksMined[itemStat->getItemId()] = itemStat; + itemStat->postConstruct(); + + // 4J-JEV: We don't need itemsCollected(emerald) so I'm using it to + // stor itemsBought(emerald) so I don't have to make yet another massive + // StatArray for Items Bought. + itemStat = new ItemStat(offset++, L"itemsBought.emerald", Item::emerald_Id); + itemsCollectedStats->push_back(itemStat); + itemsCollected[itemStat->getItemId()] = itemStat; + itemStat->postConstruct(); + + // 4J-JEV: WHY ON EARTH DO THESE ARRAYS HAVE TO BE SO PAINFULLY LARGE WHEN THEY ARE GOING TO BE MOSTLY EMPTY!!! + // Either way, I'm making this one smaller because we don't need those record items (and we only need 2). + blocksPlaced = StatArray(1000); + + itemStat = new ItemStat(offset++, L"blockPlaced.flowerPot", Tile::flowerPot_Id); + blocksPlacedStats->push_back(itemStat); + blocksPlaced[itemStat->getItemId()] = itemStat; + itemStat->postConstruct(); + + itemStat = new ItemStat(offset++, L"blockPlaced.sign", Tile::sign_Id); + blocksPlacedStats->push_back(itemStat); + blocksPlaced[itemStat->getItemId()] = itemStat; + itemStat->postConstruct(); + + itemStat = new ItemStat(offset++, L"blockPlaced.wallsign", Tile::wallSign_Id); + blocksPlacedStats->push_back(itemStat); + blocksPlaced[itemStat->getItemId()] = itemStat; + itemStat->postConstruct(); + + GeneralStat *generalStat = NULL; + + rainbowCollection = StatArray(16); + for (unsigned int i = 0; i < 16; i++) + { + generalStat = new GeneralStat(offset++, L"rainbowCollection." + _toString(i)); + generalStats->push_back(generalStat); + rainbowCollection[i] = generalStat; + generalStat->postConstruct(); + } + + biomesVisisted = StatArray(23); + for (unsigned int i = 0; i < 23; i++) + { + generalStat = new GeneralStat(offset++, L"biomesVisited." + _toString(i)); + generalStats->push_back(generalStat); + biomesVisisted[i] = generalStat; + generalStat->postConstruct(); + } + + itemStat = new ItemStat(offset++, L"itemCrafted.porkchop", Item::porkChop_cooked_Id); + itemsCraftedStats->push_back(itemStat); + itemsCrafted[itemStat->getItemId()] = itemStat; + itemStat->postConstruct(); + + itemStat = new ItemStat(offset++, L"itemEaten.porkchop", Item::porkChop_cooked_Id); + blocksPlacedStats->push_back(itemStat); + blocksPlaced[itemStat->getItemId()] = itemStat; + itemStat->postConstruct(); + } +#endif + +} + +Stat *Stats::get(int key) +{ + return statsById->at(key); +} diff --git a/Minecraft.World/Stats.h b/Minecraft.World/Stats.h new file mode 100644 index 00000000..efbeb1ea --- /dev/null +++ b/Minecraft.World/Stats.h @@ -0,0 +1,94 @@ +#pragma once +using namespace std; + +#include "Stat.h" + +class ItemStat; + +class Stats +{ + friend class Stat; + +private: + static const int BLOCKS_MINED_OFFSET; + static const int ITEMS_COLLECTED_OFFSET; + static const int ITEMS_CRAFTED_OFFSET; + static const int ADDITIONAL_STATS_OFFSET; + +protected: + static unordered_map* statsById; + +public: + static vector *all; + static vector *generalStats; + static vector *blocksMinedStats; + static vector *itemsCollectedStats; + static vector *itemsCraftedStats; + +#if (defined _EXTENDED_ACHIEVEMENTS) && (!defined _DURANGO) + static vector *blocksPlacedStats; +#endif + + static Stat *walkOneM; + static Stat *swimOneM; + static Stat *fallOneM; + static Stat *climbOneM; + static Stat *minecartOneM; + static Stat *boatOneM; + static Stat *pigOneM; + static Stat *portalsCreated; + static Stat *cowsMilked; + static Stat *netherLavaCollected; + + static Stat *killsZombie; + static Stat *killsSkeleton; + static Stat *killsCreeper; + static Stat *killsSpider; + static Stat *killsSpiderJockey; + static Stat *killsZombiePigman; + static Stat *killsSlime; + static Stat *killsGhast; + static Stat *killsNetherZombiePigman; + + // 4J : WESTY : Added for new achievements. + static Stat *befriendsWolf; + static Stat *totalBlocksMined; + static Stat *timePlayed; // Game time, recored as ticks, with TICKS_PER_DAY ticks per day! Stored as large stat so it doesn't max out before reaching 100 days ( 2,400,000 ticks ). + + //static StatArray mobsKilled; + static StatArray blocksMined; + static StatArray itemsCollected; + static StatArray itemsCrafted; + +#if (defined _EXTENDED_ACHIEVEMENTS) && (!defined _DURANGO) + static StatArray blocksPlaced; +#endif + + // Added TU9 + static Stat *killsEnderdragon; // The number of times this player has dealt the killing blow to the Enderdragon + static Stat *completeTheEnd; // The number of times this player has been present when the Enderdragon has died + +#if (defined _EXTENDED_ACHIEVEMENTS) && (!defined _DURANGO) + static StatArray biomesVisisted; + static StatArray rainbowCollection; +#endif + + static void staticCtor(); + + static void init(); + +private: + static bool blockStatsLoaded; + static bool itemStatsLoaded; + static bool craftableStatsLoaded; + +public: + static void buildBlockStats(); + static void buildItemStats(); + static void buildCraftableStats(); + + // 4J Stu - Added this function to allow us to add news stats from TU9 onwards + static void buildAdditionalStats(); + + static Stat *get(int key); +}; diff --git a/Minecraft.World/StemTile.cpp b/Minecraft.World/StemTile.cpp new file mode 100644 index 00000000..15c412eb --- /dev/null +++ b/Minecraft.World/StemTile.cpp @@ -0,0 +1,234 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\Common\Colours\ColourTable.h" +#include "StemTile.h" + +const wstring StemTile::TEXTURE_ANGLED = L"stem_bent"; + +StemTile::StemTile(int id, Tile *fruit) : Bush(id) +{ + this->fruit = fruit; + + setTicking(true); + float ss = 0.125f; + this->setShape(0.5f - ss, 0, 0.5f - ss, 0.5f + ss, 0.25f, 0.5f + ss); + + iconAngled = NULL; +} + +bool StemTile::mayPlaceOn(int tile) +{ + return tile == Tile::farmland_Id; +} + +void StemTile::tick(Level *level, int x, int y, int z, Random *random) +{ + Tile::tick(level, x, y, z, random); + if (level->getRawBrightness(x, y + 1, z) >= Level::MAX_BRIGHTNESS - 6) + { + + float growthSpeed = getGrowthSpeed(level, x, y, z); + + // 4J Stu - Brought forward change from 1.2.3 to make fruit more likely to grow + if (random->nextInt((int) (25 / growthSpeed) + 1) == 0) + { + int age = level->getData(x, y, z); + if (age < 7) + { + age++; + level->setData(x, y, z, age); + } + else + { + if (level->getTile(x - 1, y, z) == fruit->id) return; + if (level->getTile(x + 1, y, z) == fruit->id) return; + if (level->getTile(x, y, z - 1) == fruit->id) return; + if (level->getTile(x, y, z + 1) == fruit->id) return; + + int dir = random->nextInt(4); + int xx = x; + int zz = z; + if (dir == 0) xx--; + if (dir == 1) xx++; + if (dir == 2) zz--; + if (dir == 3) zz++; + // 4J Stu - Brought forward change from 1.2.3 to not require farmland to grow fruits + int below = level->getTile(xx, y - 1, zz); + if (level->getTile(xx, y, zz) == 0 && (below == Tile::farmland_Id || below == Tile::dirt_Id || below == Tile::grass_Id)) + { + level->setTile(xx, y, zz, fruit->id); + } + + } + } + } + +} + +void StemTile::growCropsToMax(Level *level, int x, int y, int z) +{ + level->setData(x, y, z, 7); +} + +float StemTile::getGrowthSpeed(Level *level, int x, int y, int z) +{ + float speed = 1; + + int n = level->getTile(x, y, z - 1); + int s = level->getTile(x, y, z + 1); + int w = level->getTile(x - 1, y, z); + int e = level->getTile(x + 1, y, z); + + int d0 = level->getTile(x - 1, y, z - 1); + int d1 = level->getTile(x + 1, y, z - 1); + int d2 = level->getTile(x + 1, y, z + 1); + int d3 = level->getTile(x - 1, y, z + 1); + + bool horizontal = w == this->id || e == this->id; + bool vertical = n == this->id || s == this->id; + bool diagonal = d0 == this->id || d1 == this->id || d2 == this->id || d3 == this->id; + + for (int xx = x - 1; xx <= x + 1; xx++) + for (int zz = z - 1; zz <= z + 1; zz++) + { + int t = level->getTile(xx, y - 1, zz); + + float tileSpeed = 0; + if (t == Tile::farmland_Id) + { + tileSpeed = 1; + if (level->getData(xx, y - 1, zz) > 0) tileSpeed = 3; + } + + if (xx != x || zz != z) tileSpeed /= 4; + + speed += tileSpeed; + } + + if (diagonal || (horizontal && vertical)) speed /= 2; + + return speed; +} + +int StemTile::getColor(int data) +{ + //int r = data * 32; + //int g = 255 - data * 8; + //int b = data * 4; + //return r << 16 | g << 8 | b; + + int colour = 0; + + unsigned int minColour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Tile_StemMin ); + unsigned int maxColour = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Tile_StemMax ); + + byte redComponent = ((minColour>>16)&0xFF) + (( (maxColour>>16)&0xFF - (minColour>>16)&0xFF)*( data/7.0f)); + byte greenComponent = ((minColour>>8)&0xFF) + (( (maxColour>>8)&0xFF - (minColour>>8)&0xFF)*( data/7.0f)); + byte blueComponent = ((minColour)&0xFF) + (( (maxColour)&0xFF - (minColour)&0xFF)*( data/7.0f)); + + colour = redComponent<<16 | greenComponent<<8 | blueComponent; + return colour; +} + +int StemTile::getColor(LevelSource *level, int x, int y, int z) +{ + return getColor(level->getData(x, y, z)); +} + +void StemTile::updateDefaultShape() +{ + float ss = 0.125f; + this->setShape(0.5f - ss, 0, 0.5f - ss, 0.5f + ss, 0.25f, 0.5f + ss); +} + +void StemTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + tls->yy1 = (level->getData(x, y, z) * 2 + 2) / 16.0f; + float ss = 0.125f; + this->setShape(0.5f - ss, 0, 0.5f - ss, 0.5f + ss, (float) tls->yy1, 0.5f + ss); +} + +int StemTile::getRenderShape() +{ + return Tile::SHAPE_STEM; +} + +int StemTile::getConnectDir(LevelSource *level, int x, int y, int z) + { + int d = level->getData(x, y, z); + if (d < 7) return -1; + if (level->getTile(x - 1, y, z) == fruit->id) return 0; + if (level->getTile(x + 1, y, z) == fruit->id) return 1; + if (level->getTile(x, y, z - 1) == fruit->id) return 2; + if (level->getTile(x, y, z + 1) == fruit->id) return 3; + return -1; +} + +/** + * Using this method instead of destroy() to determine if seeds should be + * dropped + */ +void StemTile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus) +{ + Tile::spawnResources(level, x, y, z, data, odds, playerBonus); + + if (level->isClientSide) + { + return; + } + + Item *seed = NULL; + if (fruit == Tile::pumpkin) seed = Item::seeds_pumpkin; + if (fruit == Tile::melon) seed = Item::seeds_melon; + for (int i = 0; i < 3; i++) + { + if (level->random->nextInt(5 * 3) > data) continue; + float s = 0.7f; + float xo = level->random->nextFloat() * s + (1 - s) * 0.5f; + float yo = level->random->nextFloat() * s + (1 - s) * 0.5f; + float zo = level->random->nextFloat() * s + (1 - s) * 0.5f; + shared_ptr item = shared_ptr(new ItemEntity(level, x + xo, y + yo, z + zo, shared_ptr(new ItemInstance(seed)))); + item->throwTime = 10; + level->addEntity(item); + } +} + +int StemTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return -1; +} + +int StemTile::getResourceCount(Random *random) +{ + return 1; +} + +int StemTile::cloneTileId(Level *level, int x, int y, int z) +{ + if (fruit == Tile::pumpkin) + { + return Item::seeds_pumpkin_Id; + } + else if (fruit == Tile::melon) + { + return Item::seeds_melon_Id; + } + + return 0; +} + +void StemTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"stem_straight"); + iconAngled = iconRegister->registerIcon(TEXTURE_ANGLED); +} + +Icon *StemTile::getAngledTexture() +{ + return iconAngled; +} diff --git a/Minecraft.World/StemTile.h b/Minecraft.World/StemTile.h new file mode 100644 index 00000000..ac4fc40c --- /dev/null +++ b/Minecraft.World/StemTile.h @@ -0,0 +1,48 @@ +#pragma once +#include "Tile.h" +#include "Bush.h" + +class ChunkRebuildData; +class StemTile : public Bush +{ + friend class ChunkRebuildData; +public: + static const wstring TEXTURE_ANGLED; + +private: + Tile *fruit; + Icon *iconAngled; + +public: + StemTile(int id, Tile *fruit); + + virtual bool mayPlaceOn(int tile); +public: + virtual void tick(Level *level, int x, int y, int z, Random *random); + void growCropsToMax(Level *level, int x, int y, int z); +private: + float getGrowthSpeed(Level *level, int x, int y, int z); + +public: + using Tile::getColor; + int getColor(int data); + + virtual int getColor(LevelSource *level, int x, int y, int z); + virtual void updateDefaultShape(); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual int getRenderShape(); + + int getConnectDir(LevelSource *level, int x, int y, int z); + + /** + * Using this method instead of destroy() to determine if seeds should be + * dropped + */ + virtual void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonus); + + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int getResourceCount(Random *random); + virtual int cloneTileId(Level *level, int x, int y, int z); + void registerIcons(IconRegister *iconRegister); + Icon *getAngledTexture(); +}; diff --git a/Minecraft.World/StoneMonsterTile.cpp b/Minecraft.World/StoneMonsterTile.cpp new file mode 100644 index 00000000..d3d6a846 --- /dev/null +++ b/Minecraft.World/StoneMonsterTile.cpp @@ -0,0 +1,114 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.level.h" +#include "StoneMonsterTile.h" + +const unsigned int StoneMonsterTile::STONE_MONSTER_NAMES[STONE_MONSTER_NAMES_LENGTH] = { IDS_TILE_STONE_SILVERFISH, + IDS_TILE_STONE_SILVERFISH_COBBLESTONE, + IDS_TILE_STONE_SILVERFISH_STONE_BRICK, + }; + +StoneMonsterTile::StoneMonsterTile(int id) : Tile(id, Material::clay) +{ + setDestroyTime(0); +} + +Icon *StoneMonsterTile::getTexture(int face, int data) +{ + if (data == HOST_COBBLE) + { + return Tile::stoneBrick->getTexture(face); + } + if (data == HOST_STONEBRICK) + { + return Tile::stoneBrickSmooth->getTexture(face); + } + return Tile::rock->getTexture(face); +} + +void StoneMonsterTile::registerIcons(IconRegister *iconRegister) +{ + // None +} + +void StoneMonsterTile::destroy(Level *level, int x, int y, int z, int data) +{ + if (!level->isClientSide) + { + // 4J - limit total amount of monsters. The normal map spawning limits these to 50, and mobspawning tiles limit to 60, so give ourselves a bit of headroom here to also be able to make silverfish + if(level->countInstanceOf( eTYPE_MONSTER, false) < 70 ) + { + // Also limit the amount of silverfish specifically + if(level->countInstanceOf( eTYPE_SILVERFISH, true) < 15 ) + { + shared_ptr silverfish = shared_ptr(new Silverfish(level)); + silverfish->moveTo(x + .5, y, z + .5, 0, 0); + level->addEntity(silverfish); + + silverfish->spawnAnim(); + } + } + } + Tile::destroy(level, x, y, z, data); +} + +int StoneMonsterTile::getResourceCount(Random *random) +{ + return 0; +} + +bool StoneMonsterTile::isCompatibleHostBlock(int block) +{ + return block == Tile::rock_Id || block == Tile::stoneBrick_Id || block == Tile::stoneBrickSmooth_Id; +} + +int StoneMonsterTile::getDataForHostBlock(int block) +{ + if (block == Tile::stoneBrick_Id) + { + return HOST_COBBLE; + } + if (block == Tile::stoneBrickSmooth_Id) + { + return HOST_STONEBRICK; + } + return HOST_ROCK; +} + +Tile *StoneMonsterTile::getHostBlockForData(int data) +{ + switch (data) + { + case HOST_COBBLE: + return Tile::stoneBrick; + case HOST_STONEBRICK: + return Tile::stoneBrickSmooth; + default: + return Tile::rock; + } +} + +shared_ptr StoneMonsterTile::getSilkTouchItemInstance(int data) +{ + Tile *tile = Tile::rock; + if (data == HOST_COBBLE) + { + tile = Tile::stoneBrick; + } + if (data == HOST_STONEBRICK) + { + tile = Tile::stoneBrickSmooth; + } + return shared_ptr(new ItemInstance(tile)); +} + +int StoneMonsterTile::cloneTileData(Level *level, int x, int y, int z) +{ + return level->getData(x, y, z); +} + +unsigned int StoneMonsterTile::getDescriptionId(int iData /*= -1*/) +{ + if(iData < 0 ) iData = 0; + return StoneMonsterTile::STONE_MONSTER_NAMES[iData]; +} \ No newline at end of file diff --git a/Minecraft.World/StoneMonsterTile.h b/Minecraft.World/StoneMonsterTile.h new file mode 100644 index 00000000..9c0b58fd --- /dev/null +++ b/Minecraft.World/StoneMonsterTile.h @@ -0,0 +1,38 @@ +#pragma once +#include "Tile.h" + +class Random; + +class StoneMonsterTile : public Tile +{ +public: + static const int HOST_ROCK = 0; + static const int HOST_COBBLE = 1; + static const int HOST_STONEBRICK = 2; + + static const int STONE_MONSTER_NAMES_LENGTH = 3; + + static const unsigned int STONE_MONSTER_NAMES[STONE_MONSTER_NAMES_LENGTH]; + + // 4J Stu - I don't know why this is protected in Java +//protected: +public: + StoneMonsterTile(int id); +public: + virtual Icon *getTexture(int face, int data); + void registerIcons(IconRegister *iconRegister); + virtual void destroy(Level *level, int x, int y, int z, int data); + virtual int getResourceCount(Random *random); + + static bool isCompatibleHostBlock(int block); + static int getDataForHostBlock(int block); + static Tile *getHostBlockForData(int data); + + virtual unsigned int getDescriptionId(int iData = -1); + +protected: + virtual shared_ptr getSilkTouchItemInstance(int data); + +public: + int cloneTileData(Level *level, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/StoneMonsterTileItem.cpp b/Minecraft.World/StoneMonsterTileItem.cpp new file mode 100644 index 00000000..4ecdf627 --- /dev/null +++ b/Minecraft.World/StoneMonsterTileItem.cpp @@ -0,0 +1,30 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.h" +#include "StoneMonsterTileItem.h" + +StoneMonsterTileItem::StoneMonsterTileItem(int id) : TileItem(id) +{ + setMaxDamage(0); + setStackedByData(true); +} + +int StoneMonsterTileItem::getLevelDataForAuxValue(int auxValue) +{ + return auxValue; +} + +Icon *StoneMonsterTileItem::getIcon(int itemAuxValue) +{ + return Tile::monsterStoneEgg->getTexture(0, itemAuxValue); +} + +unsigned int StoneMonsterTileItem::getDescriptionId(shared_ptr instance) +{ + int auxValue = instance->getAuxValue(); + if (auxValue < 0 || auxValue >= StoneMonsterTile::STONE_MONSTER_NAMES_LENGTH) + { + auxValue = 0; + } + return StoneMonsterTile::STONE_MONSTER_NAMES[auxValue]; +} \ No newline at end of file diff --git a/Minecraft.World/StoneMonsterTileItem.h b/Minecraft.World/StoneMonsterTileItem.h new file mode 100644 index 00000000..bb12d2e1 --- /dev/null +++ b/Minecraft.World/StoneMonsterTileItem.h @@ -0,0 +1,16 @@ +#pragma once +using namespace std; + +#include "TileItem.h" + +// 4J Stu - Class brought forward from 12w36 to fix stacking problem with silverfish stones + +class StoneMonsterTileItem : public TileItem +{ +public: + StoneMonsterTileItem(int id); + + virtual int getLevelDataForAuxValue(int auxValue); + virtual Icon *getIcon(int itemAuxValue); + virtual unsigned int getDescriptionId(shared_ptr instance); +}; \ No newline at end of file diff --git a/Minecraft.World/StoneSlabTile.cpp b/Minecraft.World/StoneSlabTile.cpp new file mode 100644 index 00000000..5c8d8b07 --- /dev/null +++ b/Minecraft.World/StoneSlabTile.cpp @@ -0,0 +1,84 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.h" +#include "StoneSlabTile.h" + + +const unsigned int StoneSlabTile::SLAB_NAMES[SLAB_NAMES_LENGTH] = { IDS_TILE_STONESLAB_STONE, + IDS_TILE_STONESLAB_SAND, + IDS_TILE_STONESLAB_WOOD, + IDS_TILE_STONESLAB_COBBLE, + IDS_TILE_STONESLAB_BRICK, + IDS_TILE_STONESLAB_SMOOTHBRICK, + IDS_TILE_STONESLAB_NETHERBRICK, + IDS_TILE_STONESLAB_QUARTZ, + }; + +StoneSlabTile::StoneSlabTile(int id, bool fullSize) : HalfSlabTile(id, fullSize, Material::stone) +{ +} + +Icon *StoneSlabTile::getTexture(int face, int data) +{ + int type = data & TYPE_MASK; + if (fullSize && (data & TOP_SLOT_BIT) != 0) + { + face = Facing::UP; + } + switch(type) + { + case STONE_SLAB: + if (face == Facing::UP || face == Facing::DOWN) return icon; + return iconSide; + break; + case SAND_SLAB: + return Tile::sandStone->getTexture(face); + case WOOD_SLAB: + return Tile::wood->getTexture(face); + case COBBLESTONE_SLAB: + return Tile::stoneBrick->getTexture(face); + case BRICK_SLAB: + return Tile::redBrick->getTexture(face); + case SMOOTHBRICK_SLAB: + return Tile::stoneBrickSmooth->getTexture(face, SmoothStoneBrickTile::TYPE_DEFAULT); + case NETHERBRICK_SLAB: + return Tile::netherBrick->getTexture(Facing::UP); + case QUARTZ_SLAB: + return Tile::quartzBlock->getTexture(face); + } + + return icon; +} + +void StoneSlabTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"stoneslab_top"); + iconSide = iconRegister->registerIcon(L"stoneslab_side"); +} + +int StoneSlabTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::stoneSlabHalf_Id; +} + +unsigned int StoneSlabTile::getDescriptionId(int iData /*= -1*/) +{ + if(iData < 0 ) iData = 0; + return StoneSlabTile::SLAB_NAMES[iData]; +} + +int StoneSlabTile::getAuxName(int auxValue) +{ + if (auxValue < 0 || auxValue >= SLAB_NAMES_LENGTH) + { + auxValue = 0; + } + return SLAB_NAMES[auxValue];//super.getDescriptionId() + "." + SLAB_NAMES[auxValue]; +} + +shared_ptr StoneSlabTile::getSilkTouchItemInstance(int data) +{ + return shared_ptr(new ItemInstance(Tile::stoneSlabHalf_Id, 2, data & TYPE_MASK)); +} diff --git a/Minecraft.World/StoneSlabTile.h b/Minecraft.World/StoneSlabTile.h new file mode 100644 index 00000000..4380b04c --- /dev/null +++ b/Minecraft.World/StoneSlabTile.h @@ -0,0 +1,39 @@ +#pragma once +using namespace std; + +#include "HalfSlabTile.h" + +class ChunkRebuildData; + +class StoneSlabTile : public HalfSlabTile +{ + friend ChunkRebuildData; +public: + static const int STONE_SLAB = 0; + static const int SAND_SLAB = 1; + static const int WOOD_SLAB = 2; + static const int COBBLESTONE_SLAB = 3; + static const int BRICK_SLAB = 4; + static const int SMOOTHBRICK_SLAB = 5; + static const int NETHERBRICK_SLAB = 6; + static const int QUARTZ_SLAB = 7; + + static const int SLAB_NAMES_LENGTH = 8; + + static const unsigned int SLAB_NAMES[SLAB_NAMES_LENGTH]; + +private: + Icon *iconSide; + +public: + StoneSlabTile(int id, bool fullSize); + + virtual Icon *getTexture(int face, int data); + + void registerIcons(IconRegister *iconRegister); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual unsigned int getDescriptionId(int iData = -1); + virtual int getAuxName(int auxValue); +protected: + virtual shared_ptr getSilkTouchItemInstance(int data); +}; \ No newline at end of file diff --git a/Minecraft.World/StoneSlabTileItem.cpp b/Minecraft.World/StoneSlabTileItem.cpp new file mode 100644 index 00000000..ea636f65 --- /dev/null +++ b/Minecraft.World/StoneSlabTileItem.cpp @@ -0,0 +1,140 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.h" +#include "StoneSlabTileItem.h" + +StoneSlabTileItem::StoneSlabTileItem(int id, HalfSlabTile *halfTile, HalfSlabTile *fullTile, bool full) : TileItem(id) +{ + this->halfTile = halfTile; + this->fullTile = fullTile; + + this->isFull = full; + setMaxDamage(0); + setStackedByData(true); +} + +Icon *StoneSlabTileItem::getIcon(int itemAuxValue) +{ + return Tile::tiles[id]->getTexture(2, itemAuxValue); +} + +int StoneSlabTileItem::getLevelDataForAuxValue(int auxValue) +{ + return auxValue; +} + +unsigned int StoneSlabTileItem::getDescriptionId(shared_ptr instance) +{ + return halfTile->getAuxName(instance->getAuxValue()); +} + +bool StoneSlabTileItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + if (isFull) + { + return TileItem::useOn(instance, player, level, x, y, z, face, clickX, clickY, clickZ, bTestUseOnOnly); + } + + if (instance->count == 0) return false; + if (!player->mayBuild(x, y, z)) return false; + + int currentTile = level->getTile(x, y, z); + int currentData = level->getData(x, y, z); + int slabType = currentData & HalfSlabTile::TYPE_MASK; + bool isUpper = (currentData & HalfSlabTile::TOP_SLOT_BIT) != 0; + + if (((face == Facing::UP && !isUpper) || (face == Facing::DOWN && isUpper)) && currentTile == halfTile->id && slabType == instance->getAuxValue()) + { + if(bTestUseOnOnly) + { + return true; + } + + if (level->isUnobstructed(fullTile->getAABB(level, x, y, z)) && level->setTileAndData(x, y, z, fullTile->id, slabType)) + { +// level.playSound(x + 0.5f, y + 0.5f, z + 0.5f, fullTile.soundType.getPlaceSound(), (fullTile.soundType.getVolume() + 1) / 2, fullTile.soundType.getPitch() * 0.8f); +// instance.count--; + level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, fullTile->soundType->getStepSound(), (fullTile->soundType->getVolume() + 1) / 2, fullTile->soundType->getPitch() * 0.8f); + instance->count--; + } + return true; + } + else if (tryConvertTargetTile(instance, player, level, x, y, z, face, bTestUseOnOnly)) + { + return true; + } + else + { + return TileItem::useOn(instance, player, level, x, y, z, face, clickX, clickY, clickZ, bTestUseOnOnly); + } +} + + +bool StoneSlabTileItem::mayPlace(Level *level, int x, int y, int z, int face,shared_ptr player, shared_ptr item) +{ + int ox = x, oy = y, oz = z; + + int currentTile = level->getTile(x, y, z); + int currentData = level->getData(x, y, z); + int slabType = currentData & HalfSlabTile::TYPE_MASK; + boolean isUpper = (currentData & HalfSlabTile::TOP_SLOT_BIT) != 0; + + if (((face == Facing::UP && !isUpper) || (face == Facing::DOWN && isUpper)) && currentTile == halfTile->id && slabType == item->getAuxValue()) + { + return true; + } + + if (face == 0) y--; + if (face == 1) y++; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + + currentTile = level->getTile(x, y, z); + currentData = level->getData(x, y, z); + slabType = currentData & HalfSlabTile::TYPE_MASK; + isUpper = (currentData & HalfSlabTile::TOP_SLOT_BIT) != 0; + + if (currentTile == halfTile->id && slabType == item->getAuxValue()) + { + return true; + } + + return TileItem::mayPlace(level, ox, oy, oz, face, player, item); +} + +bool StoneSlabTileItem::tryConvertTargetTile(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, bool bTestUseOnOnly) +{ + if (face == 0) y--; + if (face == 1) y++; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + + int currentTile = level->getTile(x, y, z); + int currentData = level->getData(x, y, z); + int slabType = currentData & HalfSlabTile::TYPE_MASK; + + if (currentTile == halfTile->id && slabType == instance->getAuxValue()) + { + if(bTestUseOnOnly) + { + return true; + } + if (level->isUnobstructed(fullTile->getAABB(level, x, y, z)) && level->setTileAndData(x, y, z, fullTile->id, slabType)) + { + //level.playSound(x + 0.5f, y + 0.5f, z + 0.5f, fullTile.soundType.getPlaceSound(), (fullTile.soundType.getVolume() + 1) / 2, fullTile.soundType.getPitch() * 0.8f); + level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, fullTile->soundType->getStepSound(), (fullTile->soundType->getVolume() + 1) / 2, fullTile->soundType->getPitch() * 0.8f); + instance->count--; + } + return true; + } + + return false; +} \ No newline at end of file diff --git a/Minecraft.World/StoneSlabTileItem.h b/Minecraft.World/StoneSlabTileItem.h new file mode 100644 index 00000000..417dcba0 --- /dev/null +++ b/Minecraft.World/StoneSlabTileItem.h @@ -0,0 +1,24 @@ +#pragma once +using namespace std; + +#include "TileItem.h" +#include "HalfSlabTile.h" + +class StoneSlabTileItem : public TileItem +{ +private: + bool isFull; + HalfSlabTile *halfTile; + HalfSlabTile *fullTile; +public: + StoneSlabTileItem(int id, HalfSlabTile *halfTile, HalfSlabTile *fullTile, bool full); + + virtual Icon *getIcon(int itemAuxValue); + virtual int getLevelDataForAuxValue(int auxValue); + virtual unsigned int getDescriptionId(shared_ptr instance); + virtual bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + + virtual bool mayPlace(Level *level, int x, int y, int z, int face,shared_ptr player, shared_ptr item); +private: + bool tryConvertTargetTile(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, bool bTestUseOnOnly); +}; \ No newline at end of file diff --git a/Minecraft.World/StoneTile.cpp b/Minecraft.World/StoneTile.cpp new file mode 100644 index 00000000..330cc087 --- /dev/null +++ b/Minecraft.World/StoneTile.cpp @@ -0,0 +1,11 @@ +#include "stdafx.h" +#include "StoneTile.h" + +StoneTile::StoneTile(int id) : Tile(id, Material::stone) +{ +} + +int StoneTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::stoneBrick_Id; +} \ No newline at end of file diff --git a/Minecraft.World/StoneTile.h b/Minecraft.World/StoneTile.h new file mode 100644 index 00000000..7d004309 --- /dev/null +++ b/Minecraft.World/StoneTile.h @@ -0,0 +1,11 @@ +#pragma once +#include "Tile.h" + +class Random; + +class StoneTile : public Tile +{ +public: + StoneTile(int id); + virtual int getResource(int data, Random *random, int playerBonusLevel); +}; \ No newline at end of file diff --git a/Minecraft.World/StringHelpers.cpp b/Minecraft.World/StringHelpers.cpp new file mode 100644 index 00000000..f305c185 --- /dev/null +++ b/Minecraft.World/StringHelpers.cpp @@ -0,0 +1,133 @@ +#include "stdafx.h" + +wstring toLower(const wstring& a) +{ + wstring out = wstring(a); + std::transform(out.begin(), out.end(), out.begin(), ::tolower); + return out; +} + +wstring trimString(const wstring& a) +{ + wstring b; + int start = (int)a.find_first_not_of(L" \t\n\r"); + int end = (int)a.find_last_not_of(L" \t\n\r"); + if( start == wstring::npos ) start = 0; + if( end == wstring::npos ) end = (int)a.size()-1; + b = a.substr(start,(end-start)+1); + return b; +} + +wstring replaceAll(const wstring& in, const wstring& replace, const wstring& with) +{ + wstring out = in; + size_t pos = 0; + while( ( pos = out.find(replace, pos) ) != wstring::npos ) + { + out.replace( pos, replace.length(), with ); + pos++; + } + return out; +} + +bool equalsIgnoreCase(const wstring& a, const wstring& b) +{ + bool out; + wstring c = toLower(a); + wstring d = toLower(b); + out = c.compare(d) == 0; + return out; +} + +wstring convStringToWstring(const string& converting) +{ + wstring converted(converting.length(), L' '); + copy(converting.begin(), converting.end(), converted.begin()); + return converted; +} + +// Convert for filename wstrings to a straight character pointer for Xbox APIs. The returned string is only valid until +// this function is called again, and it isn't thread-safe etc. as I'm just storing the returned name in a local static +// to save having to clear it up everywhere this is used. +const char *wstringtofilename(const wstring& name) +{ + static char buf[256]; + assert(name.length()<256); + for(unsigned int i = 0; i < name.length(); i++ ) + { + wchar_t c = name[i]; +#if defined __PS3__ || defined __ORBIS__ + if(c=='\\') c='/'; +#else + if(c=='/') c='\\'; +#endif + assert(c<128); // Will we have to do any conversion of non-ASCII characters in filenames? + buf[i] = (char)c; + } + buf[name.length()] = 0; + return buf; +} + +wstring filenametowstring(const char *name) +{ + return convStringToWstring(name); +} + +std::vector &stringSplit(const std::wstring &s, wchar_t delim, std::vector &elems) +{ + std::wstringstream ss(s); + std::wstring item; + while(std::getline(ss, item, delim)) + { + elems.push_back(item); + } + return elems; +} + + +std::vector stringSplit(const std::wstring &s, wchar_t delim) +{ + std::vector elems; + return stringSplit(s, delim, elems); +} + +bool BothAreSpaces(wchar_t lhs, wchar_t rhs) { return (lhs == rhs) && (lhs == L' '); } + +void stripWhitespaceForHtml(wstring &string, bool bRemoveNewline) +{ + // Strip newline chars + if(bRemoveNewline) + { + string.erase(std::remove(string.begin(), string.end(), '\n'), string.end()); + string.erase(std::remove(string.begin(), string.end(), '\r'), string.end()); + } + + string.erase(std::remove(string.begin(), string.end(), '\t'), string.end()); + + // Strip duplicate spaces + string.erase(std::unique(string.begin(), string.end(), BothAreSpaces), string.end()); + + string = trimString(string); +} + +wstring escapeXML(const wstring &in) +{ + wstring out = in; + out = replaceAll(out, L"&", L"&"); + //out = replaceAll(out, L"\"", L"""); + //out = replaceAll(out, L"'", L"'"); + out = replaceAll(out, L"<", L"<"); + out = replaceAll(out, L">", L">"); + return out; +} + +wstring parseXMLSpecials(const wstring &in) +{ + wstring out = in; + out = replaceAll(out, L"&", L"&"); + //out = replaceAll(out, L"\"", L"""); + //out = replaceAll(out, L"'", L"'"); + out = replaceAll(out, L"<", L"<"); + out = replaceAll(out, L">", L">"); + return out; +} \ No newline at end of file diff --git a/Minecraft.World/StringHelpers.h b/Minecraft.World/StringHelpers.h new file mode 100644 index 00000000..609fdf5a --- /dev/null +++ b/Minecraft.World/StringHelpers.h @@ -0,0 +1,40 @@ +#pragma once +using namespace std; + +wstring toLower(const wstring& a); +wstring trimString(const wstring& a); +wstring replaceAll(const wstring& in, const wstring& replace, const wstring& with); + +bool equalsIgnoreCase(const wstring& a, const wstring& b); +// 4J-PB - for use in the ::toString +template std::wstring _toString(T t) +{ + std::wostringstream oss; + oss << std::dec << t; + return oss.str(); +} +template T _fromString(const std::wstring& s) +{ + std::wistringstream stream (s); + T t; + stream >> t; + return t; +} +template T _fromHEXString(const std::wstring& s) +{ + std::wistringstream stream (s); + T t; + stream >> std::hex >> t; + return t; +} + +wstring convStringToWstring(const string& converting); +const char *wstringtofilename(const wstring& name); +wstring filenametowstring(const char *name); + +std::vector &stringSplit(const std::wstring &s, wchar_t delim, std::vector &elems); +std::vector stringSplit(const std::wstring &s, wchar_t delim); + +void stripWhitespaceForHtml(wstring &string, bool bRemoveNewline=true); +wstring escapeXML(const wstring &in); +wstring parseXMLSpecials(const wstring &in); diff --git a/Minecraft.World/StringTag.h b/Minecraft.World/StringTag.h new file mode 100644 index 00000000..badd53e6 --- /dev/null +++ b/Minecraft.World/StringTag.h @@ -0,0 +1,42 @@ +#pragma once +#include "Tag.h" + +class StringTag : public Tag +{ +public: + wstring data; + StringTag(const wstring &name) : Tag(name) {} + StringTag(const wstring &name, const wstring& data) : Tag(name) {this->data = data; } + + void write(DataOutput *dos) + { + dos->writeUTF(data); + } + + void load(DataInput *dis) + { + data = dis->readUTF(); + } + + byte getId() { return TAG_String; } + + wstring toString() + { + return data; + } + + Tag *copy() + { + return new StringTag(getName(), data); + } + + bool equals(Tag *obj) + { + if (Tag::equals(obj)) + { + StringTag *o = (StringTag *) obj; + return ((data.empty() && o->data.empty()) || (!data.empty() && data.compare(o->data) == 0)); + } + return false; + } +}; diff --git a/Minecraft.World/StrongholdFeature.cpp b/Minecraft.World/StrongholdFeature.cpp new file mode 100644 index 00000000..5372f4f9 --- /dev/null +++ b/Minecraft.World/StrongholdFeature.cpp @@ -0,0 +1,224 @@ +#include "stdafx.h" +#include "StrongholdFeature.h" +#include "StrongholdPieces.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.dimension.h" +#include "FileHeader.h" +#include "JavaMath.h" + +vector StrongholdFeature::allowedBiomes; + +void StrongholdFeature::staticCtor() +{ + allowedBiomes.push_back(Biome::desert); + allowedBiomes.push_back(Biome::forest); + allowedBiomes.push_back(Biome::extremeHills); + allowedBiomes.push_back(Biome::swampland); + allowedBiomes.push_back(Biome::taiga); + allowedBiomes.push_back(Biome::iceFlats); + allowedBiomes.push_back(Biome::iceMountains); + allowedBiomes.push_back(Biome::desertHills); + allowedBiomes.push_back(Biome::forestHills); + allowedBiomes.push_back(Biome::smallerExtremeHills); + allowedBiomes.push_back(Biome::taigaHills); + allowedBiomes.push_back(Biome::jungle); + allowedBiomes.push_back(Biome::jungleHills); +}; + + +StrongholdFeature::StrongholdFeature() : StructureFeature() +{ + // 4J added initialisers + for (int i = 0; i < strongholdPos_length; i++) + { + strongholdPos[i] = NULL; + } + isSpotSelected = false; +} + +StrongholdFeature::~StrongholdFeature() +{ + for (int i = 0; i < strongholdPos_length; i++) + { + delete strongholdPos[i]; + } +} + +bool StrongholdFeature::isFeatureChunk(int x, int z,bool bIsSuperflat) +{ + if (!isSpotSelected) + { + Random random; + + random.setSeed(level->getSeed()); + double angle = random.nextDouble() * PI * 2.0; + + // 4J Stu - Changed so that we keep trying more until we have found somewhere in the world to place a stronghold + bool hasFoundValidPos = false; + int findAttempts = 0; + do + { + for (int i = 0; i < strongholdPos_length; i++) + { + double dist = 0.0; +#ifdef _LARGE_WORLDS + if(level->dimension->getXZSize() < (2.25f * 32.0f) ) + { + // Xbox360/PS3 distances + dist = (1.25 + random.nextDouble()) * (3 + random.nextInt(4)); + } + else + { + // Original Java + dist = (1.25 + random.nextDouble()) * 32.0; + } +#else + // 4J Stu - Design change: Original spawns at *32 chunks rather than *10 chunks from (0,0) but that is outside our world + // double dist = (1.25 + random->nextDouble()) * 32.0; + // The max of the first part is 2.25, and we have 27 chunks in each direction + // Therefore 27/2.25 = 12, which should be the max of the second part + // The constant part and random part can be tuned to move the strongholds further from the spawn + // 4J Stu - The original (pre-TU9) calculation for selecting a start point could put the stronghold very close to the edge + // of the world, causing some parts to fail to generate. If the save is a newer save then we bring that generation in + if(level->getOriginalSaveVersion() >= SAVE_FILE_VERSION_MOVED_STRONGHOLD) + { + // Post TU9 + // The stronghold cannot extend more than 7 chunks in any direction from the start position + // Therefore as long as the the start x/z are less than 20 it will be fully contained + dist = (1.25 + random.nextDouble()) * (3 + random.nextInt(4)); + } + else + { + // Pre TU9 + dist = (1.25 + random.nextDouble()) * (5.0 + random.nextInt(7)); + } +#endif + + int selectedX = (int) (Math::round(cos(angle) * dist)); + int selectedZ = (int) (Math::round(sin(angle) * dist)); + + TilePos *position = level->getBiomeSource()->findBiome((selectedX << 4) + 8, (selectedZ << 4) + 8, 7 << 4, allowedBiomes, &random); + if (position != NULL) + { + selectedX = position->x >> 4; + selectedZ = position->z >> 4; + +#ifndef _CONTENT_PACKAGE + if(position->x > 2560 || position->x < -2560 || position->z > 2560 || position->z < -2560) + { + __debugbreak(); + } +#endif + + app.DebugPrintf("Placed stronghold in valid biome at (%d, %d), (%d, %d)\n", selectedX, selectedZ, position->x, position->z); + // 4J added + app.AddTerrainFeaturePosition(eTerrainFeature_Stronghold,selectedX,selectedZ); + + // 4J Added + hasFoundValidPos = true; + delete position; + } + else + { + app.DebugPrintf("Placed stronghold in INVALID biome at (%d, %d)\n", selectedX, selectedZ); + } + + delete strongholdPos[i]; + strongholdPos[i] = new ChunkPos(selectedX, selectedZ); + + angle += PI * 2.0 / (double) strongholdPos_length; + } + + // 4J Stu - We want to make sure that we have at least one stronghold in this world + ++findAttempts; + + // 4J Stu - Randomise the angles for retries as well +#ifdef _LARGE_WORLDS + angle = random.nextDouble() * PI * 2.0; +#endif + } + while(!hasFoundValidPos && findAttempts < MAX_STRONGHOLD_ATTEMPTS); + + if(!hasFoundValidPos) + { + // Even if it's not a valid position we are still creating the last one we tried, so store it in the save so Eye of Ender works + // Fix for #81933 - GAMEPLAY: The Eye of Ender occasionally does not appear when used to try and locate the End Portal. + app.AddTerrainFeaturePosition(eTerrainFeature_Stronghold,strongholdPos[0]->x,strongholdPos[0]->z); + } + + isSpotSelected = true; + } + + for (int i = 0; i < strongholdPos_length; i++) + { + bool forcePlacement = false; + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + if( levelGenOptions != NULL ) + { + forcePlacement = levelGenOptions->isFeatureChunk(x,z,eFeature_Stronghold); + } + + ChunkPos *pos = strongholdPos[i]; + if (forcePlacement || (pos && x == pos->x && z == pos->z) ) + { + return true; + } + } + return false; +} + +vector *StrongholdFeature::getGuesstimatedFeaturePositions() +{ + vector *positions = new vector(); + for( int i = 0; i < strongholdPos_length; i++ ) + { + ChunkPos *chunkPos = strongholdPos[i]; + if (chunkPos != NULL) + { + positions->push_back(chunkPos->getMiddleBlockPosition(64)); + } + } + return positions; +} + +StructureStart *StrongholdFeature::createStructureStart(int x, int z) +{ + + StrongholdStart *start = new StrongholdStart(level, random, x, z); + + // 4J - front() was get(0) + while (start->getPieces()->empty() || ((StrongholdPieces::StartPiece *) start->getPieces()->front())->portalRoomPiece == NULL) + { + delete start; + // regenerate stronghold without changing seed + start = new StrongholdStart(level, random, x, z); + } + + return start; + + // System.out.println("Creating stronghold at (" + x + ", " + z + ")"); + // return new StrongholdStart(level, random, x, z); +} + +StrongholdFeature::StrongholdStart::StrongholdStart(Level *level, Random *random, int chunkX, int chunkZ) : StructureStart() +{ + StrongholdPieces::resetPieces(); + + StrongholdPieces::StartPiece *startRoom = new StrongholdPieces::StartPiece(0, random, (chunkX << 4) + 2, (chunkZ << 4) + 2, level); + pieces.push_back(startRoom); + startRoom->addChildren(startRoom, &pieces, random); + + vector *pendingChildren = &startRoom->pendingChildren; + while (!pendingChildren->empty()) + { + int pos = random->nextInt((int)pendingChildren->size()); + AUTO_VAR(it, pendingChildren->begin() + pos); + StructurePiece *structurePiece = *it; + pendingChildren->erase(it); + structurePiece->addChildren(startRoom, &pieces, random); + } + + calculateBoundingBox(); + moveBelowSeaLevel(level, random, 10); +} \ No newline at end of file diff --git a/Minecraft.World/StrongholdFeature.h b/Minecraft.World/StrongholdFeature.h new file mode 100644 index 00000000..c96e9933 --- /dev/null +++ b/Minecraft.World/StrongholdFeature.h @@ -0,0 +1,45 @@ +#pragma once + +class Biome; +#include "StructureFeature.h" + +#include "StructureStart.h" + + +#include "ChunkPos.h" + +// 4J Stu Added +// We can get away with a few more attempts on new-gen consoles +#ifdef _LARGE_WORLDS +#define MAX_STRONGHOLD_ATTEMPTS 30 +#else +#define MAX_STRONGHOLD_ATTEMPTS 10 +#endif + +class StrongholdFeature : public StructureFeature +{ +public: + static void staticCtor(); +private: + static vector allowedBiomes; + + bool isSpotSelected; + static const int strongholdPos_length = 1;// Java game has 3, but xbox game only has 1 because of the world size; // 4J added + ChunkPos *strongholdPos[strongholdPos_length]; + +public: + StrongholdFeature(); + ~StrongholdFeature(); + +protected: + virtual bool isFeatureChunk(int x, int z, bool bIsSuperflat=false); + vector *getGuesstimatedFeaturePositions(); + virtual StructureStart *createStructureStart(int x, int z); + +private: + class StrongholdStart : public StructureStart + { + public: + StrongholdStart(Level *level, Random *random, int chunkX, int chunkZ); + }; +}; diff --git a/Minecraft.World/StrongholdPieces.cpp b/Minecraft.World/StrongholdPieces.cpp new file mode 100644 index 00000000..0168b2ba --- /dev/null +++ b/Minecraft.World/StrongholdPieces.cpp @@ -0,0 +1,1517 @@ +#include "stdafx.h" +#include "StrongholdPieces.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.level.storage.h" +#include "net.minecraft.world.level.levelgen.h" +#include "net.minecraft.world.item.h" +#include "WeighedTreasure.h" +#include "FileHeader.h" +#include "Facing.h" + +int StrongholdPieces::totalWeight = 0; +list StrongholdPieces::currentPieces; +StrongholdPieces::EPieceClass StrongholdPieces::imposedPiece; +const bool StrongholdPieces::CHECK_AIR = true; + +StrongholdPieces::PieceWeight::PieceWeight(EPieceClass pieceClass, int weight, int maxPlaceCount) : weight(weight) +{ + this->placeCount = 0; // 4J added initialiser + this->pieceClass = pieceClass; + this->maxPlaceCount = maxPlaceCount; +} + +bool StrongholdPieces::PieceWeight::doPlace(int depth) +{ + return maxPlaceCount == 0 || placeCount < maxPlaceCount; +} + +bool StrongholdPieces::PieceWeight::isValid() +{ + return maxPlaceCount == 0 || placeCount < maxPlaceCount; +} + +void StrongholdPieces::resetPieces() +{ + for( AUTO_VAR(it, currentPieces.begin()); it != currentPieces.end(); it++ ) + { + delete (*it); + } + currentPieces.clear(); + + currentPieces.push_back( new PieceWeight(EPieceClass_Straight, 40, 0) ); + currentPieces.push_back( new PieceWeight(EPieceClass_PrisonHall, 5, 5) ); + currentPieces.push_back( new PieceWeight(EPieceClass_LeftTurn, 20, 0) ); + currentPieces.push_back( new PieceWeight(EPieceClass_RightTurn, 20, 0) ); + currentPieces.push_back( new PieceWeight(EPieceClass_RoomCrossing, 10, 6) ); + currentPieces.push_back( new PieceWeight(EPieceClass_StraightStairsDown, 5, 5) ); + currentPieces.push_back( new PieceWeight(EPieceClass_StairsDown, 5, 5) ); + currentPieces.push_back( new PieceWeight(EPieceClass_FiveCrossing, 5, 4) ); + currentPieces.push_back( new PieceWeight(EPieceClass_ChestCorridor, 5, 4) ); + currentPieces.push_back( new PieceWeight_Library(EPieceClass_Library, 10, 2) ); + currentPieces.push_back( new PieceWeight_PortalRoom(EPieceClass_PortalRoom, 20, 1) ); + + imposedPiece = EPieceClass_NULL; +} + +bool StrongholdPieces::updatePieceWeight() +{ + bool hasAnyPieces = false; + totalWeight = 0; + for( AUTO_VAR(it, currentPieces.begin()); it != currentPieces.end(); it++ ) + { + PieceWeight *piece = *it; + if (piece->maxPlaceCount > 0 && piece->placeCount < piece->maxPlaceCount) + { + hasAnyPieces = true; + } + totalWeight += piece->weight; + } + return hasAnyPieces; +} + +StrongholdPieces::StrongholdPiece *StrongholdPieces::findAndCreatePieceFactory(EPieceClass pieceClass, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth) +{ + StrongholdPiece *strongholdPiece = NULL; + + if (pieceClass == EPieceClass_Straight) + { + strongholdPiece = Straight::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_PrisonHall) + { + strongholdPiece = PrisonHall::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_LeftTurn) + { + strongholdPiece = LeftTurn::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_RightTurn) + { + strongholdPiece = RightTurn::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_RoomCrossing) + { + strongholdPiece = RoomCrossing::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_StraightStairsDown) + { + strongholdPiece = StraightStairsDown::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_StairsDown) + { + strongholdPiece = StairsDown::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_FiveCrossing) + { + strongholdPiece = FiveCrossing::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_ChestCorridor) + { + strongholdPiece = ChestCorridor::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_Library) + { + strongholdPiece = Library::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == EPieceClass_PortalRoom) + { + strongholdPiece = PortalRoom::createPiece(pieces, random, footX, footY, footZ, direction, depth); + } + + + return strongholdPiece; +} + +StrongholdPieces::StrongholdPiece *StrongholdPieces::generatePieceFromSmallDoor(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth) +{ + if (!updatePieceWeight()) + { + return NULL; + } + + if (imposedPiece != EPieceClass_NULL) + { + StrongholdPiece *strongholdPiece = findAndCreatePieceFactory(imposedPiece, pieces, random, footX, footY, footZ, direction, depth); + imposedPiece = EPieceClass_NULL; + + if (strongholdPiece != NULL) + { + return strongholdPiece; + } + } + + int numAttempts = 0; + while (numAttempts < 5) + { + numAttempts++; + + int weightSelection = random->nextInt(totalWeight); + for( AUTO_VAR(it, currentPieces.begin()); it != currentPieces.end(); it++ ) + { + PieceWeight *piece = *it; + weightSelection -= piece->weight; + if (weightSelection < 0) + { + if (!piece->doPlace(depth) || piece == startPiece->previousPiece) + { + break; + } + + StrongholdPiece *strongholdPiece = findAndCreatePieceFactory(piece->pieceClass, pieces, random, footX, footY, footZ, direction, depth); + if (strongholdPiece != NULL) + { + piece->placeCount++; + startPiece->previousPiece = piece; + + if (!piece->isValid()) + { + currentPieces.remove(piece); + } + return strongholdPiece; + } + } + } + } + { + BoundingBox *box = FillerCorridor::findPieceBox(pieces, random, footX, footY, footZ, direction); + if (box != NULL && box->y0 > 1) + { + return new FillerCorridor(depth, random, box, direction); + } + if(box != NULL) delete box; + } + + return NULL; +} + +StructurePiece *StrongholdPieces::generateAndAddPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth) +{ + if (depth > MAX_DEPTH) + { + return NULL; + } + if (abs(footX - startPiece->getBoundingBox()->x0) > 3 * 16 || abs(footZ - startPiece->getBoundingBox()->z0) > 3 * 16) + { + // Force attempt at spawning a portal room + if(startPiece->m_level->getOriginalSaveVersion() >= SAVE_FILE_VERSION_MOVED_STRONGHOLD && !startPiece->m_level->getLevelData()->getHasStrongholdEndPortal()) + { + for( AUTO_VAR(it, currentPieces.begin()); it != currentPieces.end(); it++ ) + { + PieceWeight *piece = *it; + + if(piece->pieceClass != EPieceClass_PortalRoom) continue; + +#ifndef _CONTENT_PACKAGE + printf("Portal room forcing attempt\n"); +#endif + StrongholdPiece *strongholdPiece = PortalRoom::createPiece(pieces, random, footX, footY, footZ, direction, depth); + if (strongholdPiece != NULL) + { + piece->placeCount++; + startPiece->previousPiece = piece; + + if (!piece->isValid()) + { + currentPieces.remove(piece); + } +#ifndef _CONTENT_PACKAGE + printf("Success\n"); +#endif + return strongholdPiece; + } + } + } + return NULL; + } + + StructurePiece *newPiece = generatePieceFromSmallDoor(startPiece, pieces, random, footX, footY, footZ, direction, depth + 1); + if (newPiece != NULL) + { + pieces->push_back(newPiece); + startPiece->pendingChildren.push_back(newPiece); +// newPiece.addChildren(startPiece, pieces, random, depth + 1); + } + return newPiece; +} + +StrongholdPieces::StrongholdPiece::StrongholdPiece(int genDepth) : StructurePiece(genDepth) +{ +} + +void StrongholdPieces::StrongholdPiece::generateSmallDoor(Level *level, Random *random, BoundingBox *chunkBB, StrongholdPieces::StrongholdPiece::SmallDoorType doorType, int footX, int footY, int footZ) +{ + switch (doorType) + { + default: + case OPENING: + generateBox(level, chunkBB, footX, footY, footZ, footX + SMALL_DOOR_WIDTH - 1, footY + SMALL_DOOR_HEIGHT - 1, footZ, 0, 0, false); + break; + case WOOD_DOOR: + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX, footY, footZ, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX, footY + 1, footZ, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX, footY + 2, footZ, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 1, footY + 2, footZ, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 2, footY + 2, footZ, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 2, footY + 1, footZ, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 2, footY, footZ, chunkBB); + placeBlock(level, Tile::door_wood_Id, 0, footX + 1, footY, footZ, chunkBB); + placeBlock(level, Tile::door_wood_Id, DoorTile::UPPER_BIT, footX + 1, footY + 1, footZ, chunkBB); + break; + case GRATES: + placeBlock(level, 0, 0, footX + 1, footY, footZ, chunkBB); + placeBlock(level, 0, 0, footX + 1, footY + 1, footZ, chunkBB); + placeBlock(level, Tile::ironFence_Id, 0, footX, footY, footZ, chunkBB); + placeBlock(level, Tile::ironFence_Id, 0, footX, footY + 1, footZ, chunkBB); + placeBlock(level, Tile::ironFence_Id, 0, footX, footY + 2, footZ, chunkBB); + placeBlock(level, Tile::ironFence_Id, 0, footX + 1, footY + 2, footZ, chunkBB); + placeBlock(level, Tile::ironFence_Id, 0, footX + 2, footY + 2, footZ, chunkBB); + placeBlock(level, Tile::ironFence_Id, 0, footX + 2, footY + 1, footZ, chunkBB); + placeBlock(level, Tile::ironFence_Id, 0, footX + 2, footY, footZ, chunkBB); + break; + case IRON_DOOR: + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX, footY, footZ, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX, footY + 1, footZ, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX, footY + 2, footZ, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 1, footY + 2, footZ, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 2, footY + 2, footZ, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 2, footY + 1, footZ, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, footX + 2, footY, footZ, chunkBB); + placeBlock(level, Tile::door_iron_Id, 0, footX + 1, footY, footZ, chunkBB); + placeBlock(level, Tile::door_iron_Id, DoorTile::UPPER_BIT, footX + 1, footY + 1, footZ, chunkBB); + placeBlock(level, Tile::button_stone_Id, getOrientationData(Tile::button_stone_Id, 4), footX + 2, footY + 1, footZ + 1, chunkBB); + placeBlock(level, Tile::button_stone_Id, getOrientationData(Tile::button_stone_Id, 3), footX + 2, footY + 1, footZ - 1, chunkBB); + break; + } + +} + +StrongholdPieces::StrongholdPiece::SmallDoorType StrongholdPieces::StrongholdPiece::randomSmallDoor(Random *random) +{ + int selection = random->nextInt(5); + switch (selection) + { + default: + case 0: + case 1: + return OPENING; + case 2: + return WOOD_DOOR; + case 3: + return GRATES; + case 4: + return IRON_DOOR; + } +} + +StructurePiece *StrongholdPieces::StrongholdPiece::generateSmallDoorChildForward(StartPiece *startPiece, list *pieces, Random *random, int xOff, int yOff) +{ + switch (orientation) + { + case Direction::NORTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + xOff, boundingBox->y0 + yOff, boundingBox->z0 - 1, orientation, getGenDepth()); + case Direction::SOUTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + xOff, boundingBox->y0 + yOff, boundingBox->z1 + 1, orientation, getGenDepth()); + case Direction::WEST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + yOff, boundingBox->z0 + xOff, orientation, getGenDepth()); + case Direction::EAST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + yOff, boundingBox->z0 + xOff, orientation, getGenDepth()); + } + return NULL; +} + +StructurePiece *StrongholdPieces::StrongholdPiece::generateSmallDoorChildLeft(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff) +{ + switch (orientation) + { + case Direction::NORTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::WEST, getGenDepth()); + case Direction::SOUTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::WEST, getGenDepth()); + case Direction::WEST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z0 - 1, Direction::NORTH, getGenDepth()); + case Direction::EAST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z0 - 1, Direction::NORTH, getGenDepth()); + } + return NULL; +} + +StructurePiece *StrongholdPieces::StrongholdPiece::generateSmallDoorChildRight(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff) +{ + switch (orientation) + { + case Direction::NORTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::EAST, getGenDepth()); + case Direction::SOUTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::EAST, getGenDepth()); + case Direction::WEST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z1 + 1, Direction::SOUTH, getGenDepth()); + case Direction::EAST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z1 + 1, Direction::SOUTH, getGenDepth()); + } + return NULL; +} + +bool StrongholdPieces::StrongholdPiece::isOkBox(BoundingBox *box, StartPiece *startRoom) +{ + //return box != NULL && box->y0 > LOWEST_Y_POSITION; + + bool bIsOk = false; + + if(box != NULL) + { + if( box->y0 > LOWEST_Y_POSITION ) bIsOk = true; + + if( startRoom != NULL && startRoom->m_level->getOriginalSaveVersion() >= SAVE_FILE_VERSION_MOVED_STRONGHOLD ) + { + int xzSize = startRoom->m_level->getLevelData()->getXZSize(); + int blockMin = -( (xzSize << 4) / 2) + 1; + int blockMax = ( (xzSize << 4) / 2 ) - 1; + + if(box->x0 <= blockMin) bIsOk = false; + if(box->z0 <= blockMin) bIsOk = false; + if(box->x1 >= blockMax) bIsOk = false; + if(box->z1 >= blockMax) bIsOk = false; + } + } + + return bIsOk; +} + +StrongholdPieces::FillerCorridor::FillerCorridor(int genDepth, Random *random, BoundingBox *corridorBox, int direction) : StrongholdPiece(genDepth), + steps((direction == Direction::NORTH || direction == Direction::SOUTH) ? corridorBox->getZSpan() : corridorBox->getXSpan()) +{ + orientation = direction; + boundingBox = corridorBox; +} + +BoundingBox *StrongholdPieces::FillerCorridor::findPieceBox(list *pieces, Random *random, int footX, int footY, int footZ, int direction) +{ + const int maxLength = 3; + + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, 5, 5, maxLength + 1, direction); + + StructurePiece *collisionPiece = StructurePiece::findCollisionPiece(pieces, box); + + if (collisionPiece == NULL) + { + delete box; + // the filler must collide with something in order to be + // generated + return NULL; + } + + if (collisionPiece->getBoundingBox()->y0 == box->y0) + { + delete box; + // attempt to make a smaller piece until it fits + for (int depth = maxLength; depth >= 1; depth--) + { + box = BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, 5, 5, depth - 1, direction); + if (!collisionPiece->getBoundingBox()->intersects(box)) + { + delete box; + // the corridor has shrunk enough to fit, but make it + // one step too big to build an entrance into the other + // block + return BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, 5, 5, depth, direction); + } + delete box; + } + } + + return NULL; +} + +bool StrongholdPieces::FillerCorridor::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // filler corridor + for (int i = 0; i < steps; i++) + { + // row 0 + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 0, 0, i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 0, i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 2, 0, i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, 0, i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 4, 0, i, chunkBB); + // row 1-3 + for (int y = 1; y <= 3; y++) + { + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 0, y, i, chunkBB); + placeBlock(level, 0, 0, 1, y, i, chunkBB); + placeBlock(level, 0, 0, 2, y, i, chunkBB); + placeBlock(level, 0, 0, 3, y, i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 4, y, i, chunkBB); + } + // row 4 + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 0, 4, i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 4, i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 2, 4, i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, 4, i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 4, 4, i, chunkBB); + } + + return true; +} + +StrongholdPieces::StairsDown::StairsDown(int genDepth, Random *random, int west, int north) : StrongholdPiece(genDepth), isSource(true), entryDoor(OPENING) +{ + orientation = random->nextInt(4); + + switch (orientation) + { + case Direction::NORTH: + case Direction::SOUTH: + boundingBox = new BoundingBox(west, 64, north, west + width - 1, 64 + height - 1, north + depth - 1); + break; + default: + boundingBox = new BoundingBox(west, 64, north, west + depth - 1, 64 + height - 1, north + width - 1); + break; + } +} + +StrongholdPieces::StairsDown::StairsDown(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), isSource(false), entryDoor(randomSmallDoor(random)) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void StrongholdPieces::StairsDown::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + if( isSource ) + { + imposedPiece = EPieceClass_FiveCrossing; + } + generateSmallDoorChildForward((StartPiece *) startPiece, pieces, random, 1, 1); +} + +StrongholdPieces::StairsDown *StrongholdPieces::StairsDown::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, 4 - height, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new StairsDown(genDepth, random, box, direction); +} + +bool StrongholdPieces::StairsDown::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // bounding walls + generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector); + // entry door + generateSmallDoor(level, random, chunkBB, entryDoor, 1, height - SMALL_DOOR_HEIGHT - 1, 0); + // exit door + generateSmallDoor(level, random, chunkBB, OPENING, 1, 1, depth - 1); + + // stair steps + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 2, 6, 1, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 5, 1, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::STONE_SLAB, 1, 6, 1, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 5, 2, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 4, 3, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::STONE_SLAB, 1, 5, 3, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 2, 4, 3, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, 3, 3, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::STONE_SLAB, 3, 4, 3, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, 3, 2, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, 2, 1, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::STONE_SLAB, 3, 3, 1, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 2, 2, 1, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 1, 1, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::STONE_SLAB, 1, 2, 1, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, 1, 2, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::STONE_SLAB, 1, 1, 3, chunkBB); + + return true; +} + +StrongholdPieces::StartPiece::StartPiece(int genDepth, Random *random, int west, int north, Level *level) : StairsDown(0, random, west, north) +{ + // 4J added initialisers + isLibraryAdded = false; + previousPiece = NULL; + portalRoomPiece = NULL; + + m_level = level; +} + +TilePos *StrongholdPieces::StartPiece::getLocatorPosition() +{ + if( portalRoomPiece != NULL ) + { + return portalRoomPiece->getLocatorPosition(); + } + return StairsDown::getLocatorPosition(); +} + +StrongholdPieces::Straight::Straight(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), + entryDoor(randomSmallDoor(random)), + leftChild(random->nextInt(2) == 0), + rightChild(random->nextInt(2) == 0) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void StrongholdPieces::Straight::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateSmallDoorChildForward((StartPiece *) startPiece, pieces, random, 1, 1); + if (leftChild) generateSmallDoorChildLeft((StartPiece *) startPiece, pieces, random, 1, 2); + if (rightChild) generateSmallDoorChildRight((StartPiece *) startPiece, pieces, random, 1, 2); +} + +StrongholdPieces::Straight *StrongholdPieces::Straight::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new Straight(genDepth, random, box, direction); +} + +bool StrongholdPieces::Straight::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // bounding walls + generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector); + // entry door + generateSmallDoor(level, random, chunkBB, entryDoor, 1, height - SMALL_DOOR_HEIGHT - 1, 0); + // exit door + generateSmallDoor(level, random, chunkBB, OPENING, 1, 1, depth - 1); + + maybeGenerateBlock(level, chunkBB, random, .1f, 1, 2, 1, Tile::torch_Id, 0); + maybeGenerateBlock(level, chunkBB, random, .1f, 3, 2, 1, Tile::torch_Id, 0); + maybeGenerateBlock(level, chunkBB, random, .1f, 1, 2, 5, Tile::torch_Id, 0); + maybeGenerateBlock(level, chunkBB, random, .1f, 3, 2, 5, Tile::torch_Id, 0); + + if (leftChild) + { + generateBox(level, chunkBB, 0, 1, 2, 0, 3, 4, 0, 0, false); + } + if (rightChild) + { + generateBox(level, chunkBB, 4, 1, 2, 4, 3, 4, 0, 0, false); + } + + return true; +} + +WeighedTreasure *StrongholdPieces::ChestCorridor::treasureItems[TREASURE_ITEMS_COUNT] = +{ + new WeighedTreasure(Item::enderPearl_Id, 0, 1, 1, 10), + new WeighedTreasure(Item::diamond_Id, 0, 1, 3, 3), + new WeighedTreasure(Item::ironIngot_Id, 0, 1, 5, 10), + new WeighedTreasure(Item::goldIngot_Id, 0, 1, 3, 5), + new WeighedTreasure(Item::redStone_Id, 0, 4, 9, 5), + new WeighedTreasure(Item::bread_Id, 0, 1, 3, 15), + new WeighedTreasure(Item::apple_Id, 0, 1, 3, 15), + new WeighedTreasure(Item::pickAxe_iron_Id, 0, 1, 1, 5), + new WeighedTreasure(Item::sword_iron_Id, 0, 1, 1, 5), + new WeighedTreasure(Item::chestplate_iron_Id, 0, 1, 1, 5), + new WeighedTreasure(Item::helmet_iron_Id, 0, 1, 1, 5), + new WeighedTreasure(Item::leggings_iron_Id, 0, 1, 1, 5), + new WeighedTreasure(Item::boots_iron_Id, 0, 1, 1, 5), + new WeighedTreasure(Item::apple_gold_Id, 0, 1, 1, 1) +}; + +StrongholdPieces::ChestCorridor::ChestCorridor(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), entryDoor(randomSmallDoor(random)) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void StrongholdPieces::ChestCorridor::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateSmallDoorChildForward((StartPiece *) startPiece, pieces, random, 1, 1); +} + +StrongholdPieces::ChestCorridor *StrongholdPieces::ChestCorridor::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new ChestCorridor(genDepth, random, box, direction); +} + +bool StrongholdPieces::ChestCorridor::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // bounding walls + generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector); + // entry door + generateSmallDoor(level, random, chunkBB, entryDoor, 1, height - SMALL_DOOR_HEIGHT - 1, 0); + // exit door + generateSmallDoor(level, random, chunkBB, OPENING, 1, 1, depth - 1); + + // chest placement + generateBox(level, chunkBB, 3, 1, 2, 3, 1, 4, Tile::stoneBrickSmooth_Id, Tile::stoneBrickSmooth_Id, false); + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::SMOOTHBRICK_SLAB, 3, 1, 1, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::SMOOTHBRICK_SLAB, 3, 1, 5, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::SMOOTHBRICK_SLAB, 3, 2, 2, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::SMOOTHBRICK_SLAB, 3, 2, 4, chunkBB); + for (int z = 2; z <= 4; z++) + { + placeBlock(level, Tile::stoneSlabHalf_Id, StoneSlabTile::SMOOTHBRICK_SLAB, 2, 1, z, chunkBB); + } + + if (!hasPlacedChest) + { + int y = getWorldY(2); + int x = getWorldX(3, 3), z = getWorldZ(3, 3); + if (chunkBB->isInside(x, y, z)) + { + hasPlacedChest = true; + createChest(level, chunkBB, random, 3, 2, 3, WeighedTreasure::addToTreasure(WeighedTreasureArray(treasureItems,TREASURE_ITEMS_COUNT), Item::enchantedBook->createForRandomTreasure(random)), 2 + random->nextInt(2)); + } + } + + return true; +} + +StrongholdPieces::StraightStairsDown::StraightStairsDown(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), entryDoor(randomSmallDoor(random)) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void StrongholdPieces::StraightStairsDown::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateSmallDoorChildForward((StartPiece *) startPiece, pieces, random, 1, 1); +} + +StrongholdPieces::StraightStairsDown *StrongholdPieces::StraightStairsDown::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, 4 - height, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new StraightStairsDown(genDepth, random, box, direction); +} + +bool StrongholdPieces::StraightStairsDown::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // bounding walls + generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector); + // entry door + generateSmallDoor(level, random, chunkBB, entryDoor, 1, height - SMALL_DOOR_HEIGHT - 1, 0); + // exit door + generateSmallDoor(level, random, chunkBB, OPENING, 1, 1, depth - 1); + + // stairs + int orientationData = getOrientationData(Tile::stairs_stone_Id, 2); + for (int i = 0; i < 6; i++) + { + placeBlock(level, Tile::stairs_stone_Id, orientationData, 1, height - 5 - i, 1 + i, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, orientationData, 2, height - 5 - i, 1 + i, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, orientationData, 3, height - 5 - i, 1 + i, chunkBB); + if (i < 5) + { + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 1, height - 6 - i, 1 + i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 2, height - 6 - i, 1 + i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, height - 6 - i, 1 + i, chunkBB); + } + } + + return true; +} + +StrongholdPieces::LeftTurn::LeftTurn(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), entryDoor(randomSmallDoor(random)) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void StrongholdPieces::LeftTurn::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + if (orientation == Direction::NORTH || orientation == Direction::EAST) + { + generateSmallDoorChildLeft((StartPiece *) startPiece, pieces, random, 1, 1); + } + else + { + generateSmallDoorChildRight((StartPiece *) startPiece, pieces, random, 1, 1); + } +} + +StrongholdPieces::LeftTurn *StrongholdPieces::LeftTurn::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new LeftTurn(genDepth, random, box, direction); +} + +bool StrongholdPieces::LeftTurn::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // bounding walls + generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector); + // entry door + generateSmallDoor(level, random, chunkBB, entryDoor, 1, height - SMALL_DOOR_HEIGHT - 1, 0); + // exit opening + if (orientation == Direction::NORTH || orientation == Direction::EAST) + { + generateBox(level, chunkBB, 0, 1, 1, 0, 3, 3, 0, 0, false); + } + else + { + generateBox(level, chunkBB, 4, 1, 1, 4, 3, 3, 0, 0, false); + } + + return true; +} + +StrongholdPieces::RightTurn::RightTurn(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : LeftTurn(genDepth, random, stairsBox, direction) +{ +} + +void StrongholdPieces::RightTurn::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + if (orientation == Direction::NORTH || orientation == Direction::EAST) + { + generateSmallDoorChildRight((StartPiece *) startPiece, pieces, random, 1, 1); + } + else + { + generateSmallDoorChildLeft((StartPiece *) startPiece, pieces, random, 1, 1); + } +} + +bool StrongholdPieces::RightTurn::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // bounding walls + generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector); + // entry door + generateSmallDoor(level, random, chunkBB, entryDoor, 1, height - SMALL_DOOR_HEIGHT - 1, 0); + // exit opening + if (orientation == Direction::NORTH || orientation == Direction::EAST) + { + generateBox(level, chunkBB, 4, 1, 1, 4, 3, 3, 0, 0, false); + } + else + { + generateBox(level, chunkBB, 0, 1, 1, 0, 3, 3, 0, 0, false); + } + + return true; +} + + +StrongholdPieces::RoomCrossing::RoomCrossing(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), entryDoor(randomSmallDoor(random)), type(random->nextInt(5)) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void StrongholdPieces::RoomCrossing::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateSmallDoorChildForward((StartPiece*) startPiece, pieces, random, 4, 1); + generateSmallDoorChildLeft((StartPiece*) startPiece, pieces, random, 1, 4); + generateSmallDoorChildRight((StartPiece*) startPiece, pieces, random, 1, 4); +} + +StrongholdPieces::RoomCrossing *StrongholdPieces::RoomCrossing::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -4, -1, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new RoomCrossing(genDepth, random, box, direction); +} + +WeighedTreasure *StrongholdPieces::RoomCrossing::smallTreasureItems[SMALL_TREASURE_ITEMS_COUNT] = +{ + new WeighedTreasure(Item::ironIngot_Id, 0, 1, 5, 10), + new WeighedTreasure(Item::goldIngot_Id, 0, 1, 3, 5), + new WeighedTreasure(Item::redStone_Id, 0, 4, 9, 5), + new WeighedTreasure(Item::coal_Id, CoalItem::STONE_COAL, 3, 8, 10), + new WeighedTreasure(Item::bread_Id, 0, 1, 3, 15), + new WeighedTreasure(Item::apple_Id, 0, 1, 3, 15), + new WeighedTreasure(Item::pickAxe_iron_Id, 0, 1, 1, 1), +}; + +bool StrongholdPieces::RoomCrossing::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // bounding walls + generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector); + // entry door + generateSmallDoor(level, random, chunkBB, entryDoor, 4, 1, 0); + // exit openings + generateBox(level, chunkBB, 4, 1, depth - 1, 6, 3, depth - 1, 0, 0, false); + generateBox(level, chunkBB, 0, 1, 4, 0, 3, 6, 0, 0, false); + generateBox(level, chunkBB, width - 1, 1, 4, width - 1, 3, 6, 0, 0, false); + + switch (type) + { + default: + break; + case 0: + // middle torch pillar + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 5, 1, 5, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 5, 2, 5, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 5, 3, 5, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 4, 3, 5, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 6, 3, 5, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 5, 3, 4, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 5, 3, 6, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, 0, 4, 1, 4, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, 0, 4, 1, 5, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, 0, 4, 1, 6, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, 0, 6, 1, 4, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, 0, 6, 1, 5, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, 0, 6, 1, 6, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, 0, 5, 1, 4, chunkBB); + placeBlock(level, Tile::stoneSlabHalf_Id, 0, 5, 1, 6, chunkBB); + break; + case 1: + { + for (int i = 0; i < 5; i++) + { + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3, 1, 3 + i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 7, 1, 3 + i, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3 + i, 1, 3, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 3 + i, 1, 7, chunkBB); + } + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 5, 1, 5, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 5, 2, 5, chunkBB); + placeBlock(level, Tile::stoneBrickSmooth_Id, 0, 5, 3, 5, chunkBB); + placeBlock(level, Tile::water_Id, 0, 5, 4, 5, chunkBB); + } + break; + case 2: + { + for (int z = 1; z <= 9; z++) + { + placeBlock(level, Tile::stoneBrick_Id, 0, 1, 3, z, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 9, 3, z, chunkBB); + } + for (int x = 1; x <= 9; x++) + { + placeBlock(level, Tile::stoneBrick_Id, 0, x, 3, 1, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, x, 3, 9, chunkBB); + } + placeBlock(level, Tile::stoneBrick_Id, 0, 5, 1, 4, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 5, 1, 6, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 5, 3, 4, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 5, 3, 6, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 4, 1, 5, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 6, 1, 5, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 4, 3, 5, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 6, 3, 5, chunkBB); + for (int y = 1; y <= 3; y++) + { + placeBlock(level, Tile::stoneBrick_Id, 0, 4, y, 4, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 6, y, 4, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 4, y, 6, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 6, y, 6, chunkBB); + } + placeBlock(level, Tile::torch_Id, 0, 5, 3, 5, chunkBB); + for (int z = 2; z <= 8; z++) + { + placeBlock(level, Tile::wood_Id, 0, 2, 3, z, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 3, 3, z, chunkBB); + if (z <= 3 || z >= 7) + { + placeBlock(level, Tile::wood_Id, 0, 4, 3, z, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 5, 3, z, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 6, 3, z, chunkBB); + } + placeBlock(level, Tile::wood_Id, 0, 7, 3, z, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 8, 3, z, chunkBB); + } + placeBlock(level, Tile::ladder_Id, getOrientationData(Tile::ladder_Id, Facing::WEST), 9, 1, 3, chunkBB); + placeBlock(level, Tile::ladder_Id, getOrientationData(Tile::ladder_Id, Facing::WEST), 9, 2, 3, chunkBB); + placeBlock(level, Tile::ladder_Id, getOrientationData(Tile::ladder_Id, Facing::WEST), 9, 3, 3, chunkBB); + + createChest(level, chunkBB, random, 3, 4, 8, WeighedTreasure::addToTreasure(WeighedTreasureArray(smallTreasureItems,SMALL_TREASURE_ITEMS_COUNT), Item::enchantedBook->createForRandomTreasure(random)), 1 + random->nextInt(4)); + // System.out.println("Created chest at " + getWorldX(3, 8) + + // "," + getWorldY(4) + "," + getWorldZ(3, 8)); + + } + break; + } + return true; +} + +StrongholdPieces::PrisonHall::PrisonHall(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), entryDoor(randomSmallDoor(random)) +{ + orientation = direction; + boundingBox = stairsBox; +} + +void StrongholdPieces::PrisonHall::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateSmallDoorChildForward((StartPiece *) startPiece, pieces, random, 1, 1); +} + +StrongholdPieces::PrisonHall *StrongholdPieces::PrisonHall::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, -1, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new PrisonHall(genDepth, random, box, direction); +} + +bool StrongholdPieces::PrisonHall::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // bounding walls + generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector); + // entry door + generateSmallDoor(level, random, chunkBB, entryDoor, 1, 1, 0); + // exit openings + generateBox(level, chunkBB, 1, 1, depth - 1, 3, 3, depth - 1, 0, 0, false); + + // door pillars + generateBox(level, chunkBB, 4, 1, 1, 4, 3, 1, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, 4, 1, 3, 4, 3, 3, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, 4, 1, 7, 4, 3, 7, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, 4, 1, 9, 4, 3, 9, false, random, (BlockSelector *)smoothStoneSelector); + + // grates + generateBox(level, chunkBB, 4, 1, 4, 4, 3, 6, Tile::ironFence_Id, Tile::ironFence_Id, false); + generateBox(level, chunkBB, 5, 1, 5, 7, 3, 5, Tile::ironFence_Id, Tile::ironFence_Id, false); + + // doors + placeBlock(level, Tile::ironFence_Id, 0, 4, 3, 2, chunkBB); + placeBlock(level, Tile::ironFence_Id, 0, 4, 3, 8, chunkBB); + placeBlock(level, Tile::door_iron_Id, getOrientationData(Tile::door_iron_Id, 3), 4, 1, 2, chunkBB); + placeBlock(level, Tile::door_iron_Id, getOrientationData(Tile::door_iron_Id, 3) + DoorTile::UPPER_BIT, 4, 2, 2, chunkBB); + placeBlock(level, Tile::door_iron_Id, getOrientationData(Tile::door_iron_Id, 3), 4, 1, 8, chunkBB); + placeBlock(level, Tile::door_iron_Id, getOrientationData(Tile::door_iron_Id, 3) + DoorTile::UPPER_BIT, 4, 2, 8, chunkBB); + + return true; + +} + +StrongholdPieces::Library::Library(int genDepth, Random *random, BoundingBox *roomBox, int direction) : StrongholdPiece(genDepth), + entryDoor(randomSmallDoor(random)), + isTall(roomBox->getYSpan() > height) +{ + orientation = direction; + boundingBox = roomBox; +} + +StrongholdPieces::Library *StrongholdPieces::Library::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + // attempt to make a tall library first + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -4, -1, 0, width, tallHeight, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + // make a short library + box = BoundingBox::orientBox(footX, footY, footZ, -4, -1, 0, width, height, depth, direction); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + } + + return new Library(genDepth, random, box, direction); +} + +WeighedTreasure *StrongholdPieces::Library::libraryTreasureItems[LIBRARY_TREASURE_ITEMS_COUNT] = +{ + new WeighedTreasure(Item::book_Id, 0, 1, 3, 20), + new WeighedTreasure(Item::paper_Id, 0, 2, 7, 20), + new WeighedTreasure(Item::map_Id, 0, 1, 1, 1), + new WeighedTreasure(Item::compass_Id, 0, 1, 1, 1), +}; + +bool StrongholdPieces::Library::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + int currentHeight = tallHeight; + if (!isTall) + { + currentHeight = height; + } + + // bounding walls + generateBox(level, chunkBB, 0, 0, 0, width - 1, currentHeight - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector); + // entry door + generateSmallDoor(level, random, chunkBB, entryDoor, 4, 1, 0); + + // place sparse cob webs + generateMaybeBox(level, chunkBB, random, .07f, 2, 1, 1, width - 1 - 2, height - 2, depth - 2, Tile::web_Id, Tile::web_Id, false); + + const int bookLeft = 1; + const int bookRight = width - 2; + + // place library walls + for (int d = 1; d <= depth - 2; d++) { + if (((d - 1) % 4) == 0) { + generateBox(level, chunkBB, bookLeft, 1, d, bookLeft, 4, d, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, bookRight, 1, d, bookRight, 4, d, Tile::wood_Id, Tile::wood_Id, false); + + placeBlock(level, Tile::torch_Id, 0, 2, 3, d, chunkBB); + placeBlock(level, Tile::torch_Id, 0, width - 3, 3, d, chunkBB); + + if (isTall) + { + generateBox(level, chunkBB, bookLeft, 6, d, bookLeft, 9, d, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, bookRight, 6, d, bookRight, 9, d, Tile::wood_Id, Tile::wood_Id, false); + } + } + else + { + generateBox(level, chunkBB, bookLeft, 1, d, bookLeft, 4, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false); + generateBox(level, chunkBB, bookRight, 1, d, bookRight, 4, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false); + + if (isTall) + { + generateBox(level, chunkBB, bookLeft, 6, d, bookLeft, 9, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false); + generateBox(level, chunkBB, bookRight, 6, d, bookRight, 9, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false); + } + } + } + + // place book shelves + for (int d = 3; d < depth - 3; d += 2) + { + generateBox(level, chunkBB, 3, 1, d, 4, 3, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false); + generateBox(level, chunkBB, 6, 1, d, 7, 3, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false); + generateBox(level, chunkBB, 9, 1, d, 10, 3, d, Tile::bookshelf_Id, Tile::bookshelf_Id, false); + } + + if (isTall) + { + // create balcony + generateBox(level, chunkBB, 1, 5, 1, 3, 5, depth - 2, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, width - 4, 5, 1, width - 2, 5, depth - 2, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 4, 5, 1, width - 5, 5, 2, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 4, 5, depth - 3, width - 5, 5, depth - 2, Tile::wood_Id, Tile::wood_Id, false); + + placeBlock(level, Tile::wood_Id, 0, width - 5, 5, depth - 4, chunkBB); + placeBlock(level, Tile::wood_Id, 0, width - 6, 5, depth - 4, chunkBB); + placeBlock(level, Tile::wood_Id, 0, width - 5, 5, depth - 5, chunkBB); + + // balcony fences + generateBox(level, chunkBB, 3, 6, 2, 3, 6, depth - 3, Tile::fence_Id, Tile::fence_Id, false); + generateBox(level, chunkBB, width - 4, 6, 2, width - 4, 6, depth - 5, Tile::fence_Id, Tile::fence_Id, false); + generateBox(level, chunkBB, 4, 6, 2, width - 5, 6, 2, Tile::fence_Id, Tile::fence_Id, false); + generateBox(level, chunkBB, 4, 6, depth - 3, 8, 6, depth - 3, Tile::fence_Id, Tile::fence_Id, false); + placeBlock(level, Tile::fence_Id, 0, width - 5, 6, depth - 4, chunkBB); + placeBlock(level, Tile::fence_Id, 0, width - 6, 6, depth - 4, chunkBB); + placeBlock(level, Tile::fence_Id, 0, width - 5, 6, depth - 5, chunkBB); + + // ladder + int orientationData = getOrientationData(Tile::ladder_Id, 3); + placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 1, depth - 2, chunkBB); + placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 2, depth - 2, chunkBB); + placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 3, depth - 2, chunkBB); + placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 4, depth - 2, chunkBB); + placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 5, depth - 2, chunkBB); + placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 6, depth - 2, chunkBB); + placeBlock(level, Tile::ladder_Id, orientationData, width - 4, 7, depth - 2, chunkBB); + + // chandelier + int x = width / 2; + int z = depth / 2; + placeBlock(level, Tile::fence_Id, 0, x - 1, tallHeight - 2, z, chunkBB); + placeBlock(level, Tile::fence_Id, 0, x, tallHeight - 2, z, chunkBB); + placeBlock(level, Tile::fence_Id, 0, x - 1, tallHeight - 3, z, chunkBB); + placeBlock(level, Tile::fence_Id, 0, x, tallHeight - 3, z, chunkBB); + placeBlock(level, Tile::fence_Id, 0, x - 1, tallHeight - 4, z, chunkBB); + placeBlock(level, Tile::fence_Id, 0, x, tallHeight - 4, z, chunkBB); + + placeBlock(level, Tile::fence_Id, 0, x - 2, tallHeight - 4, z, chunkBB); + placeBlock(level, Tile::fence_Id, 0, x + 1, tallHeight - 4, z, chunkBB); + placeBlock(level, Tile::fence_Id, 0, x - 1, tallHeight - 4, z - 1, chunkBB); + placeBlock(level, Tile::fence_Id, 0, x - 1, tallHeight - 4, z + 1, chunkBB); + placeBlock(level, Tile::fence_Id, 0, x, tallHeight - 4, z - 1, chunkBB); + placeBlock(level, Tile::fence_Id, 0, x, tallHeight - 4, z + 1, chunkBB); + + placeBlock(level, Tile::torch_Id, 0, x - 2, tallHeight - 3, z, chunkBB); + placeBlock(level, Tile::torch_Id, 0, x + 1, tallHeight - 3, z, chunkBB); + placeBlock(level, Tile::torch_Id, 0, x - 1, tallHeight - 3, z - 1, chunkBB); + placeBlock(level, Tile::torch_Id, 0, x - 1, tallHeight - 3, z + 1, chunkBB); + placeBlock(level, Tile::torch_Id, 0, x, tallHeight - 3, z - 1, chunkBB); + placeBlock(level, Tile::torch_Id, 0, x, tallHeight - 3, z + 1, chunkBB); + } + + // place chests + createChest(level, chunkBB, random, 3, 3, 5, WeighedTreasure::addToTreasure(WeighedTreasureArray(libraryTreasureItems,LIBRARY_TREASURE_ITEMS_COUNT), Item::enchantedBook->createForRandomTreasure(random, 1, 5, 2)), 1 + random->nextInt(4)); + if (isTall) + { + placeBlock(level, 0, 0, width - 2, tallHeight - 2, 1, chunkBB); + createChest(level, chunkBB, random, width - 2, tallHeight - 3, 1, WeighedTreasure::addToTreasure(WeighedTreasureArray(libraryTreasureItems,LIBRARY_TREASURE_ITEMS_COUNT), Item::enchantedBook->createForRandomTreasure(random, 1, 5, 2)), 1 + random->nextInt(4)); + } + + return true; + +} + +StrongholdPieces::FiveCrossing::FiveCrossing(int genDepth, Random *random, BoundingBox *stairsBox, int direction) : StrongholdPiece(genDepth), entryDoor(randomSmallDoor(random)) +{ + orientation = direction; + boundingBox = stairsBox; + + leftLow = random->nextBoolean(); + leftHigh = random->nextBoolean(); + rightLow = random->nextBoolean(); + rightHigh = random->nextInt(3) > 0; +} + +void StrongholdPieces::FiveCrossing::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + int zOffA = 3; + int zOffB = 5; + // compensate for weird negative-facing behaviour + if (orientation == Direction::WEST || orientation == Direction::NORTH) + { + zOffA = depth - 3 - zOffA; + zOffB = depth - 3 - zOffB; + } + + generateSmallDoorChildForward((StartPiece *) startPiece, pieces, random, 5, 1); + if (leftLow) generateSmallDoorChildLeft((StartPiece *) startPiece, pieces, random, zOffA, 1); + if (leftHigh) generateSmallDoorChildLeft((StartPiece *) startPiece, pieces, random, zOffB, 7); + if (rightLow) generateSmallDoorChildRight((StartPiece *) startPiece, pieces, random, zOffA, 1); + if (rightHigh) generateSmallDoorChildRight((StartPiece *) startPiece, pieces, random, zOffB, 7); +} + +StrongholdPieces::FiveCrossing *StrongholdPieces::FiveCrossing::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -4, -3, 0, width, height, depth, direction); + + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new FiveCrossing(genDepth, random, box, direction); +} + +bool StrongholdPieces::FiveCrossing::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (edgesLiquid(level, chunkBB)) + { + return false; + } + + // bounding walls + generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, CHECK_AIR, random, (BlockSelector *)smoothStoneSelector); + // entry door + generateSmallDoor(level, random, chunkBB, entryDoor, 4, 3, 0); + + // exit openings + if (leftLow) generateBox(level, chunkBB, 0, 3, 1, 0, 5, 3, 0, 0, false); + if (rightLow) generateBox(level, chunkBB, 9, 3, 1, 9, 5, 3, 0, 0, false); + if (leftHigh) generateBox(level, chunkBB, 0, 5, 7, 0, 7, 9, 0, 0, false); + if (rightHigh) generateBox(level, chunkBB, 9, 5, 7, 9, 7, 9, 0, 0, false); + generateBox(level, chunkBB, 5, 1, 10, 7, 3, 10, 0, 0, false); + + // main floor + generateBox(level, chunkBB, 1, 2, 1, 8, 2, 6, false, random, (BlockSelector *)smoothStoneSelector); + // side walls + generateBox(level, chunkBB, 4, 1, 5, 4, 4, 9, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, 8, 1, 5, 8, 4, 9, false, random, (BlockSelector *)smoothStoneSelector); + // upper floor + generateBox(level, chunkBB, 1, 4, 7, 3, 4, 9, false, random, (BlockSelector *)smoothStoneSelector); + + // left stairs + generateBox(level, chunkBB, 1, 3, 5, 3, 3, 6, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, 1, 3, 4, 3, 3, 4, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false); + generateBox(level, chunkBB, 1, 4, 6, 3, 4, 6, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false); + + // lower stairs + generateBox(level, chunkBB, 5, 1, 7, 7, 1, 8, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, 5, 1, 9, 7, 1, 9, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false); + generateBox(level, chunkBB, 5, 2, 7, 7, 2, 7, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false); + + // bridge + generateBox(level, chunkBB, 4, 5, 7, 4, 5, 9, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false); + generateBox(level, chunkBB, 8, 5, 7, 8, 5, 9, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false); + generateBox(level, chunkBB, 5, 5, 7, 7, 5, 9, Tile::stoneSlab_Id, Tile::stoneSlab_Id, false); + placeBlock(level, Tile::torch_Id, 0, 6, 5, 6, chunkBB); + + return true; + +} + +StrongholdPieces::PortalRoom::PortalRoom(int genDepth, Random *random, BoundingBox *box, int direction) : StrongholdPiece(genDepth) +{ + hasPlacedMobSpawner = false; + orientation = direction; + boundingBox = box; +} + +void StrongholdPieces::PortalRoom::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + if (startPiece != NULL) + { + ((StartPiece *) startPiece)->portalRoomPiece = this; + } +} + +StrongholdPieces::PortalRoom *StrongholdPieces::PortalRoom::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -4, -1, 0, width, height, depth, direction); + + // 4J Added so that we can check that Portals stay within the bounds of the world (which they ALWAYS should anyway) + StartPiece *startPiece = NULL; + if(pieces != NULL) startPiece = ((StrongholdPieces::StartPiece *) pieces->front()); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new PortalRoom(genDepth, random, box, direction); +} + +bool StrongholdPieces::PortalRoom::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + // bounding walls + generateBox(level, chunkBB, 0, 0, 0, width - 1, height - 1, depth - 1, false, random, (BlockSelector *)smoothStoneSelector); + // entry door + generateSmallDoor(level, random, chunkBB, GRATES, 4, 1, 0); + + // inner roof row + int y = height - 2; + generateBox(level, chunkBB, 1, y, 1, 1, y, depth - 2, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, width - 2, y, 1, width - 2, y, depth - 2, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, 2, y, 1, width - 3, y, 2, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, 2, y, depth - 2, width - 3, y, depth - 2, false, random, (BlockSelector *)smoothStoneSelector); + + // entrance lava pools + generateBox(level, chunkBB, 1, 1, 1, 2, 1, 4, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, width - 3, 1, 1, width - 2, 1, 4, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, 1, 1, 1, 1, 1, 3, Tile::lava_Id, Tile::lava_Id, false); + generateBox(level, chunkBB, width - 2, 1, 1, width - 2, 1, 3, Tile::lava_Id, Tile::lava_Id, false); + + // portal lava pool + generateBox(level, chunkBB, 3, 1, 8, 7, 1, 12, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, 4, 1, 9, 6, 1, 11, Tile::lava_Id, Tile::lava_Id, false); + + // wall decorations + for (int z = 3; z < depth - 2; z += 2) + { + generateBox(level, chunkBB, 0, 3, z, 0, 4, z, Tile::ironFence_Id, Tile::ironFence_Id, false); + generateBox(level, chunkBB, width - 1, 3, z, width - 1, 4, z, Tile::ironFence_Id, Tile::ironFence_Id, false); + } + for (int x = 2; x < width - 2; x += 2) + { + generateBox(level, chunkBB, x, 3, depth - 1, x, 4, depth - 1, Tile::ironFence_Id, Tile::ironFence_Id, false); + } + + // stair + int orientationData = getOrientationData(Tile::stairs_stoneBrickSmooth_Id, 3); + generateBox(level, chunkBB, 4, 1, 5, 6, 1, 7, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, 4, 2, 6, 6, 2, 7, false, random, (BlockSelector *)smoothStoneSelector); + generateBox(level, chunkBB, 4, 3, 7, 6, 3, 7, false, random, (BlockSelector *)smoothStoneSelector); + for (int x = 4; x <= 6; x++) + { + placeBlock(level, Tile::stairs_stoneBrickSmooth_Id, orientationData, x, 1, 4, chunkBB); + placeBlock(level, Tile::stairs_stoneBrickSmooth_Id, orientationData, x, 2, 5, chunkBB); + placeBlock(level, Tile::stairs_stoneBrickSmooth_Id, orientationData, x, 3, 6, chunkBB); + } + + int north = Direction::NORTH; + int south = Direction::SOUTH; + int east = Direction::EAST; + int west = Direction::WEST; + + switch (orientation) + { + case Direction::SOUTH: + north = Direction::SOUTH; + south = Direction::NORTH; + break; + case Direction::EAST: + north = Direction::EAST; + south = Direction::WEST; + east = Direction::SOUTH; + west = Direction::NORTH; + break; + case Direction::WEST: + north = Direction::WEST; + south = Direction::EAST; + east = Direction::SOUTH; + west = Direction::NORTH; + break; + } + + // 4J-PB - Removed for Christmas update since we don't have The End + + // 4J-PB - not going to remove it, so that maps generated will have it in, but it can't be activated + placeBlock(level, Tile::endPortalFrameTile_Id, north + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 4, 3, 8, chunkBB); + placeBlock(level, Tile::endPortalFrameTile_Id, north + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 5, 3, 8, chunkBB); + placeBlock(level, Tile::endPortalFrameTile_Id, north + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 6, 3, 8, chunkBB); + placeBlock(level, Tile::endPortalFrameTile_Id, south + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 4, 3, 12, chunkBB); + placeBlock(level, Tile::endPortalFrameTile_Id, south + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 5, 3, 12, chunkBB); + placeBlock(level, Tile::endPortalFrameTile_Id, south + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 6, 3, 12, chunkBB); + placeBlock(level, Tile::endPortalFrameTile_Id, east + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 3, 3, 9, chunkBB); + placeBlock(level, Tile::endPortalFrameTile_Id, east + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 3, 3, 10, chunkBB); + placeBlock(level, Tile::endPortalFrameTile_Id, east + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 3, 3, 11, chunkBB); + placeBlock(level, Tile::endPortalFrameTile_Id, west + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 7, 3, 9, chunkBB); + placeBlock(level, Tile::endPortalFrameTile_Id, west + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 7, 3, 10, chunkBB); + placeBlock(level, Tile::endPortalFrameTile_Id, west + ((random->nextFloat() > 0.9f) ? TheEndPortalFrameTile::EYE_BIT : 0), 7, 3, 11, chunkBB); + + + if (!hasPlacedMobSpawner) + { + y = getWorldY(3); + int x = getWorldX(5, 6), z = getWorldZ(5, 6); + if (chunkBB->isInside(x, y, z)) + { + // 4J Stu - The mob spawner location is close enough for the map icon display, and this ensures that we only need to set the position once + app.AddTerrainFeaturePosition(eTerrainFeature_StrongholdEndPortal,x,z); + level->getLevelData()->setXStrongholdEndPortal(x); + level->getLevelData()->setZStrongholdEndPortal(z); + level->getLevelData()->setHasStrongholdEndPortal(); + + hasPlacedMobSpawner = true; + level->setTile(x, y, z, Tile::mobSpawner_Id); + shared_ptr entity = dynamic_pointer_cast(level->getTileEntity(x, y, z)); + if (entity != NULL) entity->setEntityId(L"Silverfish"); + } + } + + return true; + +} + +void StrongholdPieces::SmoothStoneSelector::next(Random *random, int worldX, int worldY, int worldZ, bool isEdge) +{ + if (isEdge) + { + nextId = Tile::stoneBrickSmooth_Id; + + float selection = random->nextFloat(); + if (selection < 0.2f) + { + nextData = SmoothStoneBrickTile::TYPE_CRACKED; + } + else if (selection < 0.5f) + { + nextData = SmoothStoneBrickTile::TYPE_MOSSY; + } + else if (selection < 0.55f) + { + nextId = Tile::monsterStoneEgg_Id; + nextData = StoneMonsterTile::HOST_STONEBRICK; + } + else + { + nextData = 0; + } + } + else + { + nextId = 0; + nextData = 0; + } +} + +const StrongholdPieces::SmoothStoneSelector *StrongholdPieces::smoothStoneSelector = new SmoothStoneSelector(); diff --git a/Minecraft.World/StrongholdPieces.h b/Minecraft.World/StrongholdPieces.h new file mode 100644 index 00000000..c9751bae --- /dev/null +++ b/Minecraft.World/StrongholdPieces.h @@ -0,0 +1,395 @@ +#pragma once +#include "StructurePiece.h" + +class StrongholdPieces +{ + +private: + static const int SMALL_DOOR_WIDTH = 3; + static const int SMALL_DOOR_HEIGHT = 3; + + static const int MAX_DEPTH = 50; + // the dungeon starts at 64 and traverses downwards to this point + static const int LOWEST_Y_POSITION = 10; + static const bool CHECK_AIR; + + // 4J - added to replace use of Class within this class + enum EPieceClass + { + EPieceClass_NULL, + EPieceClass_Straight, + EPieceClass_PrisonHall, + EPieceClass_LeftTurn, + EPieceClass_RightTurn, + EPieceClass_RoomCrossing, + EPieceClass_StraightStairsDown, + EPieceClass_StairsDown, + EPieceClass_FiveCrossing, + EPieceClass_ChestCorridor, + EPieceClass_Library, + EPieceClass_PortalRoom + }; + + class PieceWeight + { + public: + EPieceClass pieceClass; // 4J - was Class + const int weight; + int placeCount; + int maxPlaceCount; + + PieceWeight(EPieceClass pieceClass, int weight, int maxPlaceCount); + virtual bool doPlace(int depth); + bool isValid(); + }; + + // 4J - added, java uses a local specialisation of these classes when instancing to achieve the same thing + class PieceWeight_Library : public PieceWeight + { + public: + PieceWeight_Library(EPieceClass pieceClass, int weight, int maxPlaceCount) : PieceWeight(pieceClass, weight, maxPlaceCount) {} + virtual bool doPlace(int depth) { return PieceWeight::doPlace(depth) && depth > 4; } + }; + + class PieceWeight_PortalRoom : public PieceWeight + { + public: + PieceWeight_PortalRoom(EPieceClass pieceClass, int weight, int maxPlaceCount) : PieceWeight(pieceClass, weight, maxPlaceCount) {} + virtual bool doPlace(int depth) { return PieceWeight::doPlace(depth) && depth > 5; } + }; + + static list currentPieces; + static EPieceClass imposedPiece; + static int totalWeight; + +public: + static void resetPieces(); + class StartPiece; +private: + class StrongholdPiece; + static bool updatePieceWeight(); + static StrongholdPiece *findAndCreatePieceFactory(EPieceClass pieceClass, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth); + static StrongholdPiece *generatePieceFromSmallDoor(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth); + static StructurePiece *generateAndAddPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth); + + + /** + * + * + */ +private: + class StrongholdPiece : public StructurePiece + { + + protected: + StrongholdPiece(int genDepth); + + enum SmallDoorType + { + OPENING, WOOD_DOOR, GRATES, IRON_DOOR, + }; + + void generateSmallDoor(Level *level, Random *random, BoundingBox *chunkBB, SmallDoorType doorType, int footX, int footY, int footZ); + SmallDoorType randomSmallDoor(Random *random); + StructurePiece *generateSmallDoorChildForward(StartPiece *startPiece, list *pieces, Random *random, int xOff, int yOff); + StructurePiece *generateSmallDoorChildLeft(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff); + StructurePiece *generateSmallDoorChildRight(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff); + + static bool isOkBox(BoundingBox *box, StartPiece *startRoom); // 4J added startRoom param + }; + + /** + * Corridor pieces that connects unconnected ends. + * + */ +public: + class FillerCorridor : public StrongholdPiece + { + private: + const int steps; + + public: + FillerCorridor(int genDepth, Random *random, BoundingBox *corridorBox, int direction); + + static BoundingBox *findPieceBox(list *pieces, Random *random, int footX, int footY, int footZ, int direction); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ +public: + class StairsDown : public StrongholdPiece + { + private: + static const int width = 5; + static const int height = 11; + static const int depth = 5; + + const bool isSource; + const SmallDoorType entryDoor; + + public: + StairsDown(int genDepth, Random *random, int west, int north); + StairsDown(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static StairsDown *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + +public: + class PortalRoom; + + class StartPiece : public StairsDown + { + public: + bool isLibraryAdded; + PieceWeight *previousPiece; + PortalRoom *portalRoomPiece; + Level *m_level; // 4J added + + // this queue is used so that the addChildren calls are + // called in a random order + vector pendingChildren; + + StartPiece(int genDepth, Random *random, int west, int north, Level *level); // 4J Added level param + virtual TilePos *getLocatorPosition(); + }; + + /** + * + * + */ +public: + class Straight : public StrongholdPiece + { + private: + static const int width = 5; + static const int height = 5; + static const int depth = 7; + + const SmallDoorType entryDoor; + const bool leftChild; + const bool rightChild; + + public: + Straight(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static Straight *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + + }; + + /** + * + * + */ + + class ChestCorridor : public StrongholdPiece + { + private: + static const int width = 5; + static const int height = 5; + static const int depth = 7; + static const int TREASURE_ITEMS_COUNT = 14; + static WeighedTreasure *treasureItems[TREASURE_ITEMS_COUNT]; + + const SmallDoorType entryDoor; + boolean hasPlacedChest; + + public: + ChestCorridor(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static ChestCorridor *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ +public: + class StraightStairsDown : public StrongholdPiece + { + private: + static const int width = 5; + static const int height = 11; + static const int depth = 8; + + const SmallDoorType entryDoor; + + public: + StraightStairsDown(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static StraightStairsDown *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ +public: + class LeftTurn : public StrongholdPiece + { + + protected: + static const int width = 5; + static const int height = 5; + static const int depth = 5; + + const SmallDoorType entryDoor; + + public: + LeftTurn(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static LeftTurn *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ +public: + class RightTurn : public LeftTurn + { + public: + RightTurn(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ +public: + class RoomCrossing : public StrongholdPiece + { + private: + static const int SMALL_TREASURE_ITEMS_COUNT = 7; // 4J added + static WeighedTreasure *smallTreasureItems[SMALL_TREASURE_ITEMS_COUNT]; + + protected: + static const int width = 11; + static const int height = 7; + static const int depth = 11; + + protected: + const SmallDoorType entryDoor; + const int type; + public: + RoomCrossing(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static RoomCrossing *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ +public: + class PrisonHall : public StrongholdPiece + { + protected: + static const int width = 9; + static const int height = 5; + static const int depth = 11; + + const SmallDoorType entryDoor; + + public: + PrisonHall(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static PrisonHall *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ +public: + class Library : public StrongholdPiece + { + private: + static const int LIBRARY_TREASURE_ITEMS_COUNT = 4; // 4J added + static WeighedTreasure *libraryTreasureItems[LIBRARY_TREASURE_ITEMS_COUNT]; + + protected: + static const int width = 14; + static const int height = 6; + static const int tallHeight = 11; + static const int depth = 15; + + const SmallDoorType entryDoor; + private: + const bool isTall; + + public: + Library(int genDepth, Random *random, BoundingBox *roomBox, int direction); + static Library *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + + }; + + /** + * + * + */ +public: + class FiveCrossing : public StrongholdPiece + { + protected: + static const int width = 10; + static const int height = 9; + static const int depth = 11; + + const SmallDoorType entryDoor; + + private: + bool leftLow, leftHigh, rightLow, rightHigh; + + public: + FiveCrossing(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static FiveCrossing *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ + + class PortalRoom : public StrongholdPiece + { + protected: + static const int width = 11; + static const int height = 8; + static const int depth = 16; + + private: + bool hasPlacedMobSpawner; + + public: + PortalRoom(int genDepth, Random *random, BoundingBox *stairsBox, int direction); + void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static PortalRoom *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + +private: + + class SmoothStoneSelector : public StructurePiece::BlockSelector + { + public: + virtual void next(Random *random, int worldX, int worldY, int worldZ, bool isEdge); + }; + + static const SmoothStoneSelector *smoothStoneSelector; +}; diff --git a/Minecraft.World/StructureFeature.cpp b/Minecraft.World/StructureFeature.cpp new file mode 100644 index 00000000..5e8489f0 --- /dev/null +++ b/Minecraft.World/StructureFeature.cpp @@ -0,0 +1,208 @@ +#include "stdafx.h" +#include "StructureFeature.h" +#include "StructureStart.h" +#include "StructurePiece.h" +#include "ChunkPos.h" +#include "BoundingBox.h" +#include "net.minecraft.world.level.h" +#include "LevelData.h" + +StructureFeature::~StructureFeature() +{ + for( AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); it++ ) + { + delete it->second; + } +} + +void StructureFeature::addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks) +{ + // this method is called for each chunk within 8 chunk's distance from + // the chunk being generated, but not all chunks are the sources of + // structures + + if (cachedStructures.find(ChunkPos::hashCode(x, z)) != cachedStructures.end()) + { + return; + } + + // clear random key + random->nextInt(); + // 4J-PB - want to know if it's a superflat land, so we don't generate so many villages - we've changed the distance required between villages on the xbox + if (isFeatureChunk(x, z,level->getLevelData()->getGenerator() == LevelType::lvl_flat)) + { + StructureStart *start = createStructureStart(x, z); + cachedStructures[ChunkPos::hashCode(x, z)] = start; + } +} + +bool StructureFeature::postProcess(Level *level, Random *random, int chunkX, int chunkZ) +{ + // 4J Stu - The x and z used to be offset by (+8) here, but that means we can miss out half structures on the edge of the world + // Normal feature generation offsets generation by half a chunk to ensure that it can generate the entire feature in chunks already created + // Structure features don't need this, as the PlaceBlock function only places blocks inside the BoundingBox specified, and parts + // of a struture piece can be added in more than one post-process call + int cx = (chunkX << 4); // + 8; + int cz = (chunkZ << 4); // + 8; + + bool intersection = false; + for( AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); it++ ) + { + StructureStart *structureStart = it->second; + + if (structureStart->isValid()) + { + if (structureStart->getBoundingBox()->intersects(cx, cz, cx + 15, cz + 15)) + { + BoundingBox *bb = new BoundingBox(cx, cz, cx + 15, cz + 15); + structureStart->postProcess(level, random, bb); + delete bb; + intersection = true; + } + } + } + + return intersection; +} + +bool StructureFeature::isIntersection(int cellX, int cellZ) +{ + for( AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); it++ ) + { + StructureStart *structureStart = it->second; + if (structureStart->isValid()) + { + if (structureStart->getBoundingBox()->intersects(cellX, cellZ, cellX, cellZ)) + { + AUTO_VAR(it2, structureStart->getPieces()->begin()); + while( it2 != structureStart->getPieces()->end() ) + { + StructurePiece *next = *it2++; + if (next->getBoundingBox()->intersects(cellX, cellZ, cellX, cellZ)) + { + return true; + } + } + } + } + } + return false; +} + +/////////////////////////////////////////// +// 4J-PB - Below functions added from 1.2.3 +/////////////////////////////////////////// +bool StructureFeature::isInsideFeature(int cellX, int cellY, int cellZ) +{ + //for (StructureStart structureStart : cachedStructures.values()) + for(AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); ++it) + { + StructureStart *pStructureStart = it->second; + + if (pStructureStart->isValid()) + { + if (pStructureStart->getBoundingBox()->intersects(cellX, cellZ, cellX, cellZ)) + { + /* + Iterator it = structureStart.getPieces().iterator(); + while (it.hasNext()) { + StructurePiece next = it.next(); + if (next.getBoundingBox().isInside(cellX, cellY, cellZ)) { + return true; + } + */ + list *pieces=pStructureStart->getPieces(); + + for ( AUTO_VAR(it2, pieces->begin()); it2 != pieces->end(); it2++ ) + { + StructurePiece* piece = *it2; + if ( piece->getBoundingBox()->isInside(cellX, cellY, cellZ) ) + { + return true; + } + } + } + } + } + return false; +} + +TilePos *StructureFeature::getNearestGeneratedFeature(Level *level, int cellX, int cellY, int cellZ) +{ + + // this is a hack that will "force" the feature to generate positions + // even if the player hasn't generated new chunks yet + this->level = level; + + random->setSeed(level->getSeed()); + __int64 xScale = random->nextLong(); + __int64 zScale = random->nextLong(); + __int64 xx = (cellX >> 4) * xScale; + __int64 zz = (cellZ >> 4) * zScale; + random->setSeed(xx ^ zz ^ level->getSeed()); + + addFeature(level, cellX >> 4, cellZ >> 4, 0, 0, byteArray()); + + double minDistance = DBL_MAX; + TilePos *selected = NULL; + + for(AUTO_VAR(it, cachedStructures.begin()); it != cachedStructures.end(); ++it) + { + StructureStart *pStructureStart = it->second; + + if (pStructureStart->isValid()) + { + + //StructurePiece *pStructurePiece = pStructureStart->getPieces().get(0); + StructurePiece* pStructurePiece = * pStructureStart->getPieces()->begin(); + TilePos *locatorPosition = pStructurePiece->getLocatorPosition(); + + int dx = locatorPosition->x - cellX; + int dy = locatorPosition->y - cellY; + int dz = locatorPosition->z - cellZ; + double dist = dx + dx * dy * dy + dz * dz; + + if (dist < minDistance) + { + minDistance = dist; + selected = locatorPosition; + } + } + } + if (selected != NULL) + { + return selected; + } + else + { + vector *guesstimatedFeaturePositions = getGuesstimatedFeaturePositions(); + if (guesstimatedFeaturePositions != NULL) + { + TilePos *pSelectedPos = new TilePos(0,0,0); + + for(AUTO_VAR(it, guesstimatedFeaturePositions->begin()); it != guesstimatedFeaturePositions->end(); ++it) + { + int dx = (*it).x - cellX; + int dy = (*it).y - cellY; + int dz = (*it).z - cellZ; + double dist = dx + dx * dy * dy + dz * dz; + + if (dist < minDistance) + { + minDistance = dist; + pSelectedPos->x = (*it).x; + pSelectedPos->y = (*it).y; + pSelectedPos->z = (*it).z; + } + } + delete guesstimatedFeaturePositions; + return pSelectedPos; + } + } + return NULL; +} + +vector *StructureFeature::getGuesstimatedFeaturePositions() +{ + return NULL; +} diff --git a/Minecraft.World/StructureFeature.h b/Minecraft.World/StructureFeature.h new file mode 100644 index 00000000..642e6aad --- /dev/null +++ b/Minecraft.World/StructureFeature.h @@ -0,0 +1,58 @@ +#pragma once +#include "LargeFeature.h" +class StructureStart; + +class StructureFeature : public LargeFeature +{ +public: + // 4J added - Maps to values in the game rules xml + enum EFeatureTypes + { + eFeature_Mineshaft, + eFeature_NetherBridge, + eFeature_Temples, + eFeature_Stronghold, + eFeature_Village, + }; + +protected: + unordered_map<__int64, StructureStart *> cachedStructures; + +public: + ~StructureFeature(); + + virtual void addFeature(Level *level, int x, int z, int xOffs, int zOffs, byteArray blocks); + + bool postProcess(Level *level, Random *random, int chunkX, int chunkZ); + bool isIntersection(int cellX, int cellZ); + + bool isInsideFeature(int cellX, int cellY, int cellZ); + TilePos *getNearestGeneratedFeature(Level *level, int cellX, int cellY, int cellZ); +protected: + vector *getGuesstimatedFeaturePositions(); + + /** + * Returns true if the given chunk coordinates should hold a structure + * source. + * + * @param x + * chunk x + * @param z + * chunk z + * @return + */ +protected: + virtual bool isFeatureChunk(int x, int z, bool bIsSuperflat=false) = 0; + + /** + * Creates a new instance of a structure source at the given chunk + * coordinates. + * + * @param x + * chunk x + * @param z + * chunk z + * @return + */ + virtual StructureStart *createStructureStart(int x, int z) = 0; +}; diff --git a/Minecraft.World/StructurePiece.cpp b/Minecraft.World/StructurePiece.cpp new file mode 100644 index 00000000..1d41a608 --- /dev/null +++ b/Minecraft.World/StructurePiece.cpp @@ -0,0 +1,822 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.entity.h" +#include "WeighedTreasure.h" +#include "StructurePiece.h" +#include "BoundingBox.h" +#include "Direction.h" +#include "JavaMath.h" +#include "Facing.h" +#include "DoorItem.h" + +/** +* +* A structure piece is a construction or room, located somewhere in the world +* with a given orientatino (out of Direction.java). Structure pieces have a +* bounding box that says where the piece is located and its bounds, and the +* orientation is used to translate local coordinates into world coordinates. +*

+* The default orientation is Direction.UNDEFINED, in which case no translation +* will occur. If the orientation is Direction::NORTH, coordinate (0, 0, 0) will +* be at (boundingBox.x0, boundingBox.y0, boundingBox.z1). In other words, (1, +* 1, 1) will be translated to (boundingBox.x0 + 1, boundingBox.y0 + 1, +* boundingBox.z1 - 1). +*

+* When using Direction::SOUTH, the x coordinate will be the same, and the z +* coordinate will be flipped. In other words, the bounding box is NOT rotated! +* It is only flipped along the z axis. Also note that the bounding box is in +* world coordinates, so the local drawing must never reach outside of this. +*

+* When using east and west coordinates, the local z coordinate will be swapped +* with the local x coordinate. For example, (0, 0, 0) is (boundingBox.z1, +* boundingBox.y0, boundingBox.z0), and (1, 1, 1) becomes (boundingBox.x1 - 1, +* boundingBox.y0 + 1, boundingBox.z0 + 1) when using Direction::WEST. +*

+* When-ever a structure piece is placing blocks, it is VERY IMPORTANT to always +* make sure that all getTile and setTile calls are within the chunk's bounding +* box. Failing to check this will cause the level generator to create new +* chunks, leading to infinite loops and other errors. +*/ + +StructurePiece::StructurePiece( int genDepth ) +{ + boundingBox = NULL; + this->genDepth = genDepth; + orientation = Direction::UNDEFINED; +} + +StructurePiece::~StructurePiece() +{ + if(boundingBox != NULL) delete boundingBox; +} + +void StructurePiece::addChildren( StructurePiece* startPiece, list< StructurePiece* > *pieces, Random* random ) +{ +} + +BoundingBox* StructurePiece::getBoundingBox() +{ + return boundingBox; +} + +int StructurePiece::getGenDepth() +{ + return genDepth; +} + +bool StructurePiece::isInChunk( ChunkPos* pos ) +{ + int cx = ( pos->x << 4 ); + int cz = ( pos->z << 4 ); + + return boundingBox->intersects( cx, cz, cx + 15, cz + 15 ); +} + +StructurePiece* StructurePiece::findCollisionPiece( list< StructurePiece* > *pieces, BoundingBox* box ) +{ + for ( AUTO_VAR(it, pieces->begin()); it != pieces->end(); it++ ) + { + StructurePiece* piece = *it; + if ( piece->getBoundingBox() != NULL && piece->getBoundingBox()->intersects( box ) ) + { + return piece; + } + } + return NULL; +} + +// 4J-PB - Added from 1.2.3 +TilePos *StructurePiece::getLocatorPosition() +{ + return new TilePos(boundingBox->getXCenter(), boundingBox->getYCenter(), boundingBox->getZCenter()); +} + +bool StructurePiece::edgesLiquid( Level* level, BoundingBox* chunkBB ) +{ + int x0 = Math::_max( boundingBox->x0 - 1, chunkBB->x0 ); + int y0 = Math::_max( boundingBox->y0 - 1, chunkBB->y0 ); + int z0 = Math::_max( boundingBox->z0 - 1, chunkBB->z0 ); + int x1 = Math::_min( boundingBox->x1 + 1, chunkBB->x1 ); + int y1 = Math::_min( boundingBox->y1 + 1, chunkBB->y1 ); + int z1 = Math::_min( boundingBox->z1 + 1, chunkBB->z1 ); + + // roof and floor + for ( int x = x0; x <= x1; x++ ) + { + for ( int z = z0; z <= z1; z++ ) + { + int tile = level->getTile( x, y0, z ); + if ( tile > 0 && Tile::tiles[tile]->material->isLiquid() ) + { + return true; + } + tile = level->getTile( x, y1, z ); + if ( tile > 0 && Tile::tiles[tile]->material->isLiquid() ) + { + return true; + } + } + } + // north and south + for ( int x = x0; x <= x1; x++ ) + { + for ( int y = y0; y <= y1; y++ ) + { + int tile = level->getTile( x, y, z0 ); + if ( tile > 0 && Tile::tiles[tile]->material->isLiquid() ) + { + return true; + } + tile = level->getTile( x, y, z1 ); + if ( tile > 0 && Tile::tiles[tile]->material->isLiquid() ) + { + return true; + } + } + } + // east and west + for ( int z = z0; z <= z1; z++ ) + { + for ( int y = y0; y <= y1; y++ ) + { + int tile = level->getTile( x0, y, z ); + if ( tile > 0 && Tile::tiles[tile]->material->isLiquid() ) + { + return true; + } + tile = level->getTile( x1, y, z ); + if ( tile > 0 && Tile::tiles[tile]->material->isLiquid() ) + { + return true; + } + } + } + return false; + +} + +int StructurePiece::getWorldX( int x, int z ) +{ + switch ( orientation ) + { + case Direction::NORTH: + case Direction::SOUTH: + return boundingBox->x0 + x; + case Direction::WEST: + return boundingBox->x1 - z; + case Direction::EAST: + return boundingBox->x0 + z; + default: + return x; + } +} + +int StructurePiece::getWorldY( int y ) +{ + if ( orientation == Direction::UNDEFINED ) + { + return y; + } + return y + boundingBox->y0; +} + +int StructurePiece::getWorldZ( int x, int z ) +{ + switch ( orientation ) + { + case Direction::NORTH: + return boundingBox->z1 - z; + case Direction::SOUTH: + return boundingBox->z0 + z; + case Direction::WEST: + case Direction::EAST: + return boundingBox->z0 + x; + default: + return z; + } +} + +int StructurePiece::getOrientationData( int tile, int data ) +{ + if ( tile == Tile::rail->id ) + { + if ( orientation == Direction::WEST || orientation == Direction::EAST ) + { + if ( data == RailTile::DIR_FLAT_X ) + { + return RailTile::DIR_FLAT_Z; + } + else + { + return RailTile::DIR_FLAT_X; + } + } + } + else if ( tile == Tile::door_wood_Id || tile == Tile::door_iron_Id ) + { + if ( orientation == Direction::SOUTH ) + { + if ( data == 0 ) + { + return 2; + } + if ( data == 2 ) + { + return 0; + } + } + else if ( orientation == Direction::WEST ) + { + // 0 = 1 + // 1 = 2 + // 2 = 3 + // 3 = 0 + return ( data + 1 ) & 3; + } + else if ( orientation == Direction::EAST ) + { + // 0 = 3 + // 1 = 0 + // 2 = 1 + // 3 = 2 + return ( data + 3 ) & 3; + } + } + else if ( tile == Tile::stairs_stone_Id || tile == Tile::stairs_wood_Id || tile == Tile::stairs_netherBricks_Id || tile == Tile::stairs_stoneBrickSmooth_Id || tile == Tile::stairs_sandstone_Id) + { + if ( orientation == Direction::SOUTH ) + { + if ( data == 2 ) + { + return 3; + } + if ( data == 3 ) + { + return 2; + } + } + else if ( orientation == Direction::WEST ) + { + if ( data == 0 ) + { + return 2; + } + if ( data == 1 ) + { + return 3; + } + if ( data == 2 ) + { + return 0; + } + if ( data == 3 ) + { + return 1; + } + } + else if ( orientation == Direction::EAST ) + { + if ( data == 0 ) + { + return 2; + } + if ( data == 1 ) + { + return 3; + } + if ( data == 2 ) + { + return 1; + } + if ( data == 3 ) + { + return 0; + } + } + } + else if ( tile == Tile::ladder->id ) + { + if ( orientation == Direction::SOUTH ) + { + if ( data == Facing::NORTH ) + { + return Facing::SOUTH; + } + if ( data == Facing::SOUTH ) + { + return Facing::NORTH; + } + } + else if ( orientation == Direction::WEST ) + { + if ( data == Facing::NORTH ) + { + return Facing::WEST; + } + if ( data == Facing::SOUTH ) + { + return Facing::EAST; + } + if ( data == Facing::WEST ) + { + return Facing::NORTH; + } + if ( data == Facing::EAST ) + { + return Facing::SOUTH; + } + } + else if ( orientation == Direction::EAST ) + { + if ( data == Facing::NORTH ) + { + return Facing::EAST; + } + if ( data == Facing::SOUTH ) + { + return Facing::WEST; + } + if ( data == Facing::WEST ) + { + return Facing::NORTH; + } + if ( data == Facing::EAST ) + { + return Facing::SOUTH; + } + } + + } + else if ( tile == Tile::button->id ) + { + if ( orientation == Direction::SOUTH ) + { + if ( data == 3 ) + { + return 4; + } + if ( data == 4 ) + { + return 3; + } + } + else if ( orientation == Direction::WEST ) + { + if ( data == 3 ) + { + return 1; + } + if ( data == 4 ) + { + return 2; + } + if ( data == 2 ) + { + return 3; + } + if ( data == 1 ) + { + return 4; + } + } + else if ( orientation == Direction::EAST ) + { + if ( data == 3 ) + { + return 2; + } + if ( data == 4 ) + { + return 1; + } + if ( data == 2 ) + { + return 3; + } + if ( data == 1 ) + { + return 4; + } + } + } + else if (tile == Tile::tripWireSource_Id || (Tile::tiles[tile] != NULL && dynamic_cast(Tile::tiles[tile]))) + { + if (orientation == Direction::SOUTH) + { + if (data == Direction::SOUTH || data == Direction::NORTH) + { + return Direction::DIRECTION_OPPOSITE[data]; + } + } + else if (orientation == Direction::WEST) + { + if (data == Direction::NORTH) + { + return Direction::WEST; + } + if (data == Direction::SOUTH) + { + return Direction::EAST; + } + if (data == Direction::WEST) + { + return Direction::NORTH; + } + if (data == Direction::EAST) + { + return Direction::SOUTH; + } + } + else if (orientation == Direction::EAST) + { + if (data == Direction::NORTH) + { + return Direction::EAST; + } + if (data == Direction::SOUTH) + { + return Direction::WEST; + } + if (data == Direction::WEST) + { + return Direction::NORTH; + } + if (data == Direction::EAST) + { + return Direction::SOUTH; + } + } + } + else if (tile == Tile::pistonBase_Id || tile == Tile::pistonStickyBase_Id || tile == Tile::lever_Id || tile == Tile::dispenser_Id) + { + if (orientation == Direction::SOUTH) + { + if (data == Facing::NORTH || data == Facing::SOUTH) + { + return Facing::OPPOSITE_FACING[data]; + } + } + else if (orientation == Direction::WEST) + { + if (data == Facing::NORTH) + { + return Facing::WEST; + } + if (data == Facing::SOUTH) + { + return Facing::EAST; + } + if (data == Facing::WEST) + { + return Facing::NORTH; + } + if (data == Facing::EAST) + { + return Facing::SOUTH; + } + } else if (orientation == Direction::EAST) + { + if (data == Facing::NORTH) + { + return Facing::EAST; + } + if (data == Facing::SOUTH) + { + return Facing::WEST; + } + if (data == Facing::WEST) + { + return Facing::NORTH; + } + if (data == Facing::EAST) + { + return Facing::SOUTH; + } + } + } + return data; + +} + +void StructurePiece::placeBlock( Level* level, int block, int data, int x, int y, int z, BoundingBox* chunkBB ) +{ + int worldX = getWorldX( x, z ); + int worldY = getWorldY( y ); + int worldZ = getWorldZ( x, z ); + + if ( !chunkBB->isInside( worldX, worldY, worldZ ) ) + { + return; + } + + // 4J Stu - We shouldn't be removing bedrock when generating things (eg in SuperFlat) + if(worldY == 0) return; + + level->setTileAndDataNoUpdate( worldX, worldY, worldZ, block, data ); +} + + +/** +* The purpose of this method is to wrap the getTile call on Level, in order +* to prevent the level from generating chunks that shouldn't be loaded yet. +* Returns 0 if the call is out of bounds. +* +* @param level +* @param x +* @param y +* @param z +* @param chunkPosition +* @return +*/ +int StructurePiece::getBlock( Level* level, int x, int y, int z, BoundingBox* chunkBB ) +{ + int worldX = getWorldX( x, z ); + int worldY = getWorldY( y ); + int worldZ = getWorldZ( x, z ); + + if ( !chunkBB->isInside( worldX, worldY, worldZ ) ) + { + return 0; + } + + return level->getTile( worldX, worldY, worldZ ); +} + +void StructurePiece::generateAirBox(Level *level, BoundingBox *chunkBB, int x0, int y0, int z0, int x1, int y1, int z1) +{ + for (int y = y0; y <= y1; y++) + { + for (int x = x0; x <= x1; x++) + { + for (int z = z0; z <= z1; z++) + { + placeBlock(level, 0, 0, x, y, z, chunkBB); + } + } + } +} + +void StructurePiece::generateBox( Level* level, BoundingBox* chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, + int edgeTile, int fillTile, bool skipAir ) +{ + for ( int y = y0; y <= y1; y++ ) + { + for ( int x = x0; x <= x1; x++ ) + { + for ( int z = z0; z <= z1; z++ ) + { + + if ( skipAir && getBlock( level, x, y, z, chunkBB ) == 0 ) + { + continue; + } + if ( y == y0 || y == y1 || x == x0 || x == x1 || z == z0 || z == z1 ) + { + placeBlock( level, edgeTile, 0, x, y, z, chunkBB ); + } + else + { + placeBlock( level, fillTile, 0, x, y, z, chunkBB ); + } + + } + } + } +} + +void StructurePiece::generateBox(Level *level, BoundingBox *chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, int edgeTile, int edgeData, int fillTile, int fillData, bool skipAir) +{ + for (int y = y0; y <= y1; y++) + { + for (int x = x0; x <= x1; x++) + { + for (int z = z0; z <= z1; z++) + { + + if (skipAir && getBlock(level, x, y, z, chunkBB) == 0) + { + continue; + } + if (y == y0 || y == y1 || x == x0 || x == x1 || z == z0 || z == z1) + { + placeBlock(level, edgeTile, edgeData, x, y, z, chunkBB); + } + else + { + placeBlock(level, fillTile, fillData, x, y, z, chunkBB); + } + + } + } + } +} + +void StructurePiece::generateBox( Level* level, BoundingBox* chunkBB, BoundingBox* boxBB, int edgeTile, int fillTile, + bool skipAir ) +{ + generateBox( level, chunkBB, boxBB->x0, boxBB->y0, boxBB->z0, boxBB->x1, boxBB->y1, boxBB->z1, edgeTile, fillTile, + skipAir ); +} + +void StructurePiece::generateBox( Level* level, BoundingBox* chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, + bool skipAir, Random* random, StructurePiece::BlockSelector* selector ) +{ + for ( int y = y0; y <= y1; y++ ) + { + for ( int x = x0; x <= x1; x++ ) + { + for ( int z = z0; z <= z1; z++ ) + { + + if ( skipAir && getBlock( level, x, y, z, chunkBB ) == 0 ) + { + continue; + } + selector->next( random, x, y, z, y == y0 || y == y1 || x == x0 || x == x1 || z == z0 || z == z1 ); + placeBlock( level, selector->getNextId(), selector->getNextData(), x, y, z, chunkBB ); + + } + } + } +} + +void StructurePiece::generateBox( Level* level, BoundingBox* chunkBB, BoundingBox* boxBB, bool skipAir, Random* random, + StructurePiece::BlockSelector* selector ) +{ + generateBox( level, chunkBB, boxBB->x0, boxBB->y0, boxBB->z0, boxBB->x1, boxBB->y1, boxBB->z1, skipAir, random, + selector ); +} + +void StructurePiece::generateMaybeBox( Level* level, BoundingBox* chunkBB, Random *random, float probability, int x0, + int y0, int z0, int x1, int y1, int z1, int edgeTile, int fillTile, + bool skipAir ) +{ + for ( int y = y0; y <= y1; y++ ) + { + for ( int x = x0; x <= x1; x++ ) + { + for ( int z = z0; z <= z1; z++ ) + { + + if ( random->nextFloat() > probability ) + { + continue; + } + if ( skipAir && getBlock( level, x, y, z, chunkBB ) == 0 ) + { + continue; + } + if ( y == y0 || y == y1 || x == x0 || x == x1 || z == z0 || z == z1 ) + { + placeBlock( level, edgeTile, 0, x, y, z, chunkBB ); + } + else + { + placeBlock( level, fillTile, 0, x, y, z, chunkBB ); + } + + } + } + } +} + +void StructurePiece::maybeGenerateBlock( Level* level, BoundingBox* chunkBB, Random *random, float probability, int x, + int y, int z, int tile, int data ) +{ + if ( random->nextFloat() < probability ) + { + placeBlock( level, tile, data, x, y, z, chunkBB ); + } +} + +void StructurePiece::generateUpperHalfSphere( Level* level, BoundingBox* chunkBB, int x0, int y0, int z0, int x1, + int y1, int z1, int fillTile, bool skipAir ) +{ + float diagX = (float)( x1 - x0 + 1 ); + float diagY = (float)( y1 - y0 + 1 ); + float diagZ = (float)( z1 - z0 + 1 ); + float cx = x0 + diagX / 2; + float cz = z0 + diagZ / 2; + + for ( int y = y0; y <= y1; y++ ) + { + float normalizedYDistance = ( float )( y - y0 ) / diagY; + + for ( int x = x0; x <= x1; x++ ) + { + float normalizedXDistance = ( float )( x - cx ) / ( diagX * 0.5f ); + + for ( int z = z0; z <= z1; z++ ) + { + float normalizedZDistance = ( float )( z - cz ) / ( diagZ * 0.5f ); + + if ( skipAir && getBlock( level, x, y, z, chunkBB ) == 0 ) + { + continue; + } + + float dist = ( normalizedXDistance * normalizedXDistance ) + ( normalizedYDistance * + normalizedYDistance ) + ( normalizedZDistance * normalizedZDistance ); + + if ( dist <= 1.05f ) + { + placeBlock( level, fillTile, 0, x, y, z, chunkBB ); + } + + } + } + } + +} + +void StructurePiece::generateAirColumnUp( Level* level, int x, int startY, int z, BoundingBox* chunkBB ) +{ + int worldX = getWorldX( x, z ); + int worldY = getWorldY( startY ); + int worldZ = getWorldZ( x, z ); + + if ( !chunkBB->isInside( worldX, worldY, worldZ ) ) + { + return; + } + + while ( !level->isEmptyTile( worldX, worldY, worldZ ) && worldY < Level::maxBuildHeight - 1 ) + { + level->setTileAndDataNoUpdate( worldX, worldY, worldZ, 0, 0 ); + worldY++; + } +} + +void StructurePiece::fillColumnDown( Level* level, int tile, int tileData, int x, int startY, int z, + BoundingBox* chunkBB ) +{ + int worldX = getWorldX( x, z ); + int worldY = getWorldY( startY ); + int worldZ = getWorldZ( x, z ); + + if ( !chunkBB->isInside( worldX, worldY, worldZ ) ) + { + return; + } + + while ( ( level->isEmptyTile( worldX, worldY, worldZ ) || level->getMaterial( worldX, worldY, worldZ )->isLiquid() ) && worldY > 1 ) + { + level->setTileAndDataNoUpdate( worldX, worldY, worldZ, tile, tileData ); + worldY--; + } +} + +bool StructurePiece::createChest( Level* level, BoundingBox* chunkBB, Random* random, int x, int y, int z, + WeighedTreasureArray treasure, int numRolls ) +{ + int worldX = getWorldX( x, z ); + int worldY = getWorldY( y ); + int worldZ = getWorldZ( x, z ); + + if ( chunkBB->isInside( worldX, worldY, worldZ ) ) + { + if ( level->getTile( worldX, worldY, worldZ ) != Tile::chest->id ) + { + level->setTile( worldX, worldY, worldZ, Tile::chest->id ); + shared_ptr chest = dynamic_pointer_cast(level->getTileEntity( worldX, worldY, worldZ )); + if ( chest != NULL ) WeighedTreasure::addChestItems( random, treasure, chest, numRolls ); + return true; + } + } + return false; +} + +bool StructurePiece::createDispenser(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, int facing, WeighedTreasureArray items, int numRolls) +{ + int worldX = getWorldX(x, z); + int worldY = getWorldY(y); + int worldZ = getWorldZ(x, z); + + if (chunkBB->isInside(worldX, worldY, worldZ)) + { + if (level->getTile(worldX, worldY, worldZ) != Tile::dispenser_Id) + { + level->setTileAndData(worldX, worldY, worldZ, Tile::dispenser_Id, getOrientationData(Tile::dispenser_Id, facing)); + shared_ptr dispenser = dynamic_pointer_cast(level->getTileEntity(worldX, worldY, worldZ)); + if (dispenser != NULL) WeighedTreasure::addDispenserItems(random, items, dispenser, numRolls); + return true; + } + } + return false; +} + +void StructurePiece::createDoor( Level* level, BoundingBox* chunkBB, Random* random, int x, int y, int z, + int orientation ) +{ + int worldX = getWorldX( x, z ); + int worldY = getWorldY( y ); + int worldZ = getWorldZ( x, z ); + + if ( chunkBB->isInside( worldX, worldY, worldZ ) ) + { + DoorItem::place( level, worldX, worldY, worldZ, orientation, Tile::door_wood ); + } +} diff --git a/Minecraft.World/StructurePiece.h b/Minecraft.World/StructurePiece.h new file mode 100644 index 00000000..f1ead433 --- /dev/null +++ b/Minecraft.World/StructurePiece.h @@ -0,0 +1,117 @@ +#pragma once +#include "WeighedRandom.h" +#include "BoundingBox.h" + +class Level; +class Random; +class ChunkPos; +class BlockSelector; +class ChestTileEntity; +class TilePos; + +/** + * + * A structure piece is a construction or room, located somewhere in the world + * with a given orientatino (out of Direction.java). Structure pieces have a + * bounding box that says where the piece is located and its bounds, and the + * orientation is used to translate local coordinates into world coordinates. + *

+ * The default orientation is Direction.UNDEFINED, in which case no translation + * will occur. If the orientation is Direction.NORTH, coordinate (0, 0, 0) will + * be at (boundingBox.x0, boundingBox.y0, boundingBox.z1). In other words, (1, + * 1, 1) will be translated to (boundingBox.x0 + 1, boundingBox.y0 + 1, + * boundingBox.z1 - 1). + *

+ * When using Direction.SOUTH, the x coordinate will be the same, and the z + * coordinate will be flipped. In other words, the bounding box is NOT rotated! + * It is only flipped along the z axis. Also note that the bounding box is in + * world coordinates, so the local drawing must never reach outside of this. + *

+ * When using east and west coordinates, the local z coordinate will be swapped + * with the local x coordinate. For example, (0, 0, 0) is (boundingBox.z1, + * boundingBox.y0, boundingBox.z0), and (1, 1, 1) becomes (boundingBox.x1 - 1, + * boundingBox.y0 + 1, boundingBox.z0 + 1) when using Direction.WEST. + *

+ * When-ever a structure piece is placing blocks, it is VERY IMPORTANT to always + * make sure that all getTile and setTile calls are within the chunk's bounding + * box. Failing to check this will cause the level generator to create new + * chunks, leading to infinite loops and other errors. + */ +class StructurePiece +{ +public: + class BlockSelector + { + + protected: + int nextId; + int nextData; + + public: + virtual void next(Random *random, int worldX, int worldY, int worldZ, bool isEdge) {} + + virtual int getNextId() { return nextId; } + virtual int getNextData() { return nextData; } + }; + +public: // 4J is protected in java, but accessed from VillagePieces, not sure how + BoundingBox *boundingBox; +protected: + int orientation; + int genDepth; + + StructurePiece(int genDepth); +public: + virtual ~StructurePiece(); + + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB) = 0; + + virtual BoundingBox *getBoundingBox(); + + int getGenDepth(); + +public: + bool isInChunk(ChunkPos *pos); + static StructurePiece *findCollisionPiece(list *pieces, BoundingBox *box); + virtual TilePos *getLocatorPosition(); +protected: + bool edgesLiquid(Level *level, BoundingBox *chunkBB); +public: + // 4J Stu - Made these public to use in game rules + int getWorldX(int x, int z); + int getWorldY(int y); + int getWorldZ(int x, int z); + int getOrientationData(int tile, int data); + virtual void placeBlock(Level *level, int block, int data, int x, int y, int z, BoundingBox *chunkBB); + + /** + * The purpose of this method is to wrap the getTile call on Level, in order + * to prevent the level from generating chunks that shouldn't be loaded yet. + * Returns 0 if the call is out of bounds. + * + * @param level + * @param x + * @param y + * @param z + * @param chunkPosition + * @return + */ + virtual int getBlock(Level *level, int x, int y, int z, BoundingBox *chunkBB); + virtual void generateAirBox(Level *level, BoundingBox *chunkBB, int x0, int y0, int z0, int x1, int y1, int z1); + virtual void generateBox(Level *level, BoundingBox *chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, int edgeTile, int fillTile, bool skipAir); + virtual void generateBox(Level *level, BoundingBox *chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, int edgeTile, int edgeData, int fillTile, int fillData, bool skipAir); + virtual void generateBox(Level *level, BoundingBox *chunkBB, BoundingBox *boxBB, int edgeTile, int fillTile, bool skipAir); + virtual void generateBox(Level *level, BoundingBox *chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, bool skipAir, Random *random, BlockSelector *selector); + virtual void generateBox(Level *level, BoundingBox *chunkBB, BoundingBox *boxBB, bool skipAir, Random *random, BlockSelector *selector); + virtual void generateMaybeBox(Level *level, BoundingBox *chunkBB, Random *random, float probability, int x0, int y0, int z0, int x1, int y1, int z1, int edgeTile, int fillTile, bool skipAir); + virtual void maybeGenerateBlock(Level *level, BoundingBox *chunkBB, Random *random, float probability, int x, int y, int z, int tile, int data); + virtual void generateUpperHalfSphere(Level *level, BoundingBox *chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, int fillTile, bool skipAir); + virtual void generateAirColumnUp(Level *level, int x, int startY, int z, BoundingBox *chunkBB); + virtual void fillColumnDown(Level *level, int tile, int tileData, int x, int startY, int z, BoundingBox *chunkBB); + virtual bool createChest(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, WeighedTreasureArray treasure, int numRolls); + virtual bool createDispenser(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, int facing, WeighedTreasureArray items, int numRolls); + +protected: + void createDoor(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, int orientation); +}; diff --git a/Minecraft.World/StructureRecipies.cpp b/Minecraft.World/StructureRecipies.cpp new file mode 100644 index 00000000..f8ac558c --- /dev/null +++ b/Minecraft.World/StructureRecipies.cpp @@ -0,0 +1,128 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.tile.h" +#include "Recipy.h" +#include "Recipes.h" +#include "StructureRecipies.h" + +void StructureRecipies::addRecipes(Recipes *r) +{ + r->addShapedRecipy(new ItemInstance(Tile::sandStone), // + L"ssctg", + L"##", // + L"##", // + + L'#', Tile::sand, + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::sandStone, 4, SandStoneTile::TYPE_SMOOTHSIDE), // + L"ssczg", + L"##", // + L"##", // + + L'#', new ItemInstance(Tile::sandStone), + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::sandStone, 1, SandStoneTile::TYPE_HEIROGLYPHS), // + L"ssczg", + L"#", // + L"#", // + + L'#', new ItemInstance(Tile::stoneSlabHalf, 1, StoneSlabTile::SAND_SLAB), + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::quartzBlock, 1, QuartzBlockTile::TYPE_CHISELED), // + L"ssczg", + L"#", // + L"#", // + + L'#', new ItemInstance(Tile::stoneSlabHalf, 1, StoneSlabTile::QUARTZ_SLAB), + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::quartzBlock, 2, QuartzBlockTile::TYPE_LINES_Y), // + L"ssczg", + L"#", // + L"#", // + + L'#', new ItemInstance(Tile::quartzBlock, 1, QuartzBlockTile::TYPE_DEFAULT), + L'S'); + + // 4J Stu - Changed the order, as the blocks that go with sandstone cause a 3-icon scroll + // that touches the text "Structures" in the title in 720 fullscreen. + r->addShapedRecipy(new ItemInstance(Tile::workBench), // + L"ssctg", + L"##", // + L"##", // + + L'#', Tile::wood, + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::furnace), // + L"sssctg", + L"###", // + L"# #", // + L"###", // + + L'#', Tile::stoneBrick, + L'S'); + + r->addShapedRecipy(new ItemInstance((Tile*)Tile::chest), // + L"sssctg", + L"###", // + L"# #", // + L"###", // + + L'#', Tile::wood, + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::enderChest), // + L"sssctcig", + L"###", // + L"#E#", // + L"###", // + + L'#', Tile::obsidian, L'E', Item::eyeOfEnder, + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::stoneBrickSmooth, 4), // + L"ssctg", + L"##", // + L"##", // + + L'#', Tile::rock, + L'S'); + + // 4J Stu - Move this into "Recipes" to change the order things are displayed on the crafting menu + //r->addShapedRecipy(new ItemInstance(Tile::ironFence, 16), // + // L"sscig", + // L"###", // + // L"###", // + + // L'#', Item::ironIngot, + // L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::thinGlass, 16), // + L"ssctg", + L"###", // + L"###", // + + L'#', Tile::glass, + L'D'); + + r->addShapedRecipy(new ItemInstance(Tile::netherBrick, 1), // + L"sscig", + L"NN", // + L"NN", // + + L'N', Item::netherbrick, + L'S'); + + r->addShapedRecipy(new ItemInstance(Tile::redstoneLight, 1), // + L"ssscictg", + L" R ", // + L"RGR", // + L" R ", // + L'R', Item::redStone, 'G', Tile::lightGem, + L'M'); + +} \ No newline at end of file diff --git a/Minecraft.World/StructureRecipies.h b/Minecraft.World/StructureRecipies.h new file mode 100644 index 00000000..b972a757 --- /dev/null +++ b/Minecraft.World/StructureRecipies.h @@ -0,0 +1,7 @@ +#pragma once + +class StructureRecipies +{ +public: + void addRecipes(Recipes *r); +}; diff --git a/Minecraft.World/StructureStart.cpp b/Minecraft.World/StructureStart.cpp new file mode 100644 index 00000000..22a4312c --- /dev/null +++ b/Minecraft.World/StructureStart.cpp @@ -0,0 +1,111 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "StructureStart.h" +#include "StructurePiece.h" +#include "BoundingBox.h" + + +StructureStart::StructureStart() +{ + boundingBox = NULL; // 4J added initialiser +} + +StructureStart::~StructureStart() +{ + for(AUTO_VAR(it, pieces.begin()); it != pieces.end(); it++ ) + { + delete (*it); + } + delete boundingBox; +} + +BoundingBox *StructureStart::getBoundingBox() +{ + return boundingBox; +} + +list *StructureStart::getPieces() +{ + return &pieces; +} + +void StructureStart::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + AUTO_VAR(it, pieces.begin()); + + while( it != pieces.end() ) + { + if( (*it)->getBoundingBox()->intersects(chunkBB) && !(*it)->postProcess(level, random, chunkBB)) + { + // this piece can't be placed, so remove it to avoid future + // attempts + it = pieces.erase(it); + } + else + { + it++; + } + } +} + +void StructureStart::calculateBoundingBox() +{ + boundingBox = BoundingBox::getUnknownBox(); + + for( AUTO_VAR(it, pieces.begin()); it != pieces.end(); it++ ) + { + StructurePiece *piece = *it; + boundingBox->expand(piece->getBoundingBox()); + } +} + +void StructureStart::moveBelowSeaLevel(Level *level, Random *random, int offset) +{ + const int MAX_Y = level->seaLevel - offset; + + // set lowest possible position (at bedrock) + int y1Pos = boundingBox->getYSpan() + 1; + // move up randomly within the available span + if (y1Pos < MAX_Y) + { + y1Pos += random->nextInt(MAX_Y - y1Pos); + } + + // move all bounding boxes + int dy = y1Pos - boundingBox->y1; + boundingBox->move(0, dy, 0); + for( AUTO_VAR(it, pieces.begin()); it != pieces.end(); it++ ) + { + StructurePiece *piece = *it; + piece->getBoundingBox()->move(0, dy, 0); + } +} + +void StructureStart::moveInsideHeights(Level *level, Random *random, int lowestAllowed, int highestAllowed) +{ + int heightSpan = highestAllowed - lowestAllowed + 1 - boundingBox->getYSpan(); + int y0Pos = 1; + + if (heightSpan > 1) + { + y0Pos = lowestAllowed + random->nextInt(heightSpan); + } + else + { + y0Pos = lowestAllowed; + } + + // move all bounding boxes + int dy = y0Pos - boundingBox->y0; + boundingBox->move(0, dy, 0); + for( AUTO_VAR(it, pieces.begin()); it != pieces.end(); it++ ) + { + StructurePiece *piece = *it; + piece->getBoundingBox()->move(0, dy, 0); + } +} + +bool StructureStart::isValid() +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/StructureStart.h b/Minecraft.World/StructureStart.h new file mode 100644 index 00000000..e035f564 --- /dev/null +++ b/Minecraft.World/StructureStart.h @@ -0,0 +1,25 @@ +#pragma once +class StructurePiece; +class BoundingBox; + +class StructureStart +{ + +protected: + list pieces; + BoundingBox *boundingBox; + + StructureStart(); +public: + ~StructureStart(); + BoundingBox *getBoundingBox(); + list *getPieces(); + void postProcess(Level *level, Random *random, BoundingBox *chunkBB); +protected: + void calculateBoundingBox(); + void moveBelowSeaLevel(Level *level, Random *random, int offset); + void moveInsideHeights(Level *level, Random *random, int lowestAllowed, int highestAllowed); +public: + bool isValid(); + +}; diff --git a/Minecraft.World/SwampBiome.cpp b/Minecraft.World/SwampBiome.cpp new file mode 100644 index 00000000..0dec2f48 --- /dev/null +++ b/Minecraft.World/SwampBiome.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "SwampTreeFeature.h" + +SwampBiome::SwampBiome(int id) : Biome(id) +{ + decorator->treeCount = 2; + decorator->flowerCount = -999; + decorator->deadBushCount = 1; + decorator->mushroomCount = 8; + decorator->reedsCount = 10; + decorator->clayCount = 1; + decorator->waterlilyCount = 4; + + // waterColor = 0xe0ffae; +} + + +Feature *SwampBiome::getTreeFeature(Random *random) +{ + return new SwampTreeFeature(); // 4J used to return member swampTree, now returning newly created object so that caller can be consistently resposible for cleanup +} + +// 4J Stu - Not using these any more +//int SwampBiome::getGrassColor() +//{ +// double temp = getTemperature(); +// double rain = getDownfall(); +// +// return ((GrassColor::get(temp, rain) & 0xfefefe) + 0x4e0e4e) / 2; +//} +// +//int SwampBiome::getFolageColor() +//{ +// double temp = getTemperature(); +// double rain = getDownfall(); +// +// return ((FoliageColor::get(temp, rain) & 0xfefefe) + 0x4e0e4e) / 2; +//} \ No newline at end of file diff --git a/Minecraft.World/SwampBiome.h b/Minecraft.World/SwampBiome.h new file mode 100644 index 00000000..2bcdb8ac --- /dev/null +++ b/Minecraft.World/SwampBiome.h @@ -0,0 +1,18 @@ +#pragma once +#include "Biome.h" +class LevelSource; + +class SwampBiome : public Biome +{ + // 4J Stu - No idea why this is protected in Java +//protected: +public: + SwampBiome(int id); + +public: + virtual Feature *getTreeFeature(Random *random); + + // 4J Stu - Not using these any more + //virtual int getGrassColor(); + //virtual int getFolageColor(); +}; \ No newline at end of file diff --git a/Minecraft.World/SwampRiversLayer.cpp b/Minecraft.World/SwampRiversLayer.cpp new file mode 100644 index 00000000..68cef8a8 --- /dev/null +++ b/Minecraft.World/SwampRiversLayer.cpp @@ -0,0 +1,34 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" +#include "IntCache.h" +#include "SwampRiversLayer.h" + +SwampRiversLayer::SwampRiversLayer(__int64 seed, shared_ptr parent) : Layer(seed) +{ + this->parent = parent; +} + +intArray SwampRiversLayer::getArea(int xo, int yo, int w, int h) +{ + intArray b = parent->getArea(xo - 1, yo - 1, w + 2, h + 2); + + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + initRandom(x + xo, y + yo); + int old = b[(x + 1) + (y + 1) * (w + 2)]; + if ((old == Biome::swampland->id && nextRandom(6) == 0) || ((old == Biome::jungle->id || old == Biome::jungleHills->id) && nextRandom(8) == 0)) + { + result[x + y * w] = Biome::river->id; + } + else + { + result[x + y * w] = old; + } + } + } + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/SwampRiversLayer.h b/Minecraft.World/SwampRiversLayer.h new file mode 100644 index 00000000..f724ad82 --- /dev/null +++ b/Minecraft.World/SwampRiversLayer.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Layer.h" + +class SwampRiversLayer : public Layer +{ +public: + SwampRiversLayer(__int64 seed, shared_ptr parent); + + intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/SwampTreeFeature.cpp b/Minecraft.World/SwampTreeFeature.cpp new file mode 100644 index 00000000..19d3f66a --- /dev/null +++ b/Minecraft.World/SwampTreeFeature.cpp @@ -0,0 +1,132 @@ +#include "stdafx.h" +#include "SwampTreeFeature.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "JavaMath.h" + +bool SwampTreeFeature::place(Level *level, Random *random, int x, int y, int z) +{ + int treeHeight = random->nextInt(4) + 5; + while (level->getMaterial(x, y - 1, z) == Material::water) + y--; + + bool free = true; + if (y < 1 || y + treeHeight + 1 > Level::genDepth) return false; + + // 4J Stu Added to stop tree features generating areas previously place by game rule generation + if(app.getLevelGenerationOptions() != NULL) + { + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + bool intersects = levelGenOptions->checkIntersects(x - 3, y - 1, z - 3, x + 3, y + treeHeight, z + 3); + if(intersects) + { + //app.DebugPrintf("Skipping reeds feature generation as it overlaps a game rule structure\n"); + return false; + } + } + + for (int yy = y; yy <= y + 1 + treeHeight; yy++) + { + int r = 1; + if (yy == y) r = 0; + if (yy >= y + 1 + treeHeight - 2) r = 3; + for (int xx = x - r; xx <= x + r && free; xx++) + { + for (int zz = z - r; zz <= z + r && free; zz++) + { + if (yy >= 0 && yy < Level::genDepth) + { + int tt = level->getTile(xx, yy, zz); + if (tt != 0 && tt != Tile::leaves_Id) + { + if (tt == Tile::calmWater_Id || tt == Tile::water_Id) + { + if (yy > y) free = false; + } + else + { + free = false; + } + } + } + else + { + free = false; + } + } + } + } + + if (!free) return false; + + int belowTile = level->getTile(x, y - 1, z); + if ((belowTile != Tile::grass_Id && belowTile != Tile::dirt_Id) || y >= Level::genDepth - treeHeight - 1) return false; + + placeBlock(level, x, y - 1, z, Tile::dirt_Id); + + for (int yy = y - 3 + treeHeight; yy <= y + treeHeight; yy++) + { + int yo = yy - (y + treeHeight); + int offs = 2 - yo / 2; + for (int xx = x - offs; xx <= x + offs; xx++) + { + int xo = xx - (x); + for (int zz = z - offs; zz <= z + offs; zz++) + { + int zo = zz - (z); + if (abs(xo) == offs && abs(zo) == offs && (random->nextInt(2) == 0 || yo == 0)) continue; + if (!Tile::solid[level->getTile(xx, yy, zz)]) placeBlock(level, xx, yy, zz, Tile::leaves_Id); + } + } + } + + for (int hh = 0; hh < treeHeight; hh++) + { + int t = level->getTile(x, y + hh, z); + if (t == 0 || t == Tile::leaves_Id || t == Tile::water_Id || t == Tile::calmWater_Id) placeBlock(level, x, y + hh, z, Tile::treeTrunk_Id); + } + + for (int yy = y - 3 + treeHeight; yy <= y + treeHeight; yy++) + { + int yo = yy - (y + treeHeight); + int offs = 2 - yo / 2; + for (int xx = x - offs; xx <= x + offs; xx++) + { + for (int zz = z - offs; zz <= z + offs; zz++) + { + if (level->getTile(xx, yy, zz) == Tile::leaves_Id) + { + if (random->nextInt(4) == 0 && level->getTile(xx - 1, yy, zz) == 0) + { + addVine(level, xx - 1, yy, zz, VineTile::VINE_EAST); + } + if (random->nextInt(4) == 0 && level->getTile(xx + 1, yy, zz) == 0) + { + addVine(level, xx + 1, yy, zz, VineTile::VINE_WEST); + } + if (random->nextInt(4) == 0 && level->getTile(xx, yy, zz - 1) == 0) + { + addVine(level, xx, yy, zz - 1, VineTile::VINE_SOUTH); + } + if (random->nextInt(4) == 0 && level->getTile(xx, yy, zz + 1) == 0) + { + addVine(level, xx, yy, zz + 1, VineTile::VINE_NORTH); + } + } + } + } + } + return true; + +} + +void SwampTreeFeature::addVine(Level *level, int xx, int yy, int zz, int dir) +{ + placeBlock(level, xx, yy, zz, Tile::vine_Id, dir); + int maxDir = 4; + while (level->getTile(xx, --yy, zz) == 0 && maxDir > 0) + { + placeBlock(level, xx, yy, zz, Tile::vine_Id, dir); + maxDir--; + } +} \ No newline at end of file diff --git a/Minecraft.World/SwampTreeFeature.h b/Minecraft.World/SwampTreeFeature.h new file mode 100644 index 00000000..e1c40b8f --- /dev/null +++ b/Minecraft.World/SwampTreeFeature.h @@ -0,0 +1,11 @@ +#pragma once +#include "Feature.h" +class Random; + +class SwampTreeFeature : public Feature +{ +public: + virtual bool place(Level *level, Random *random, int x, int y, int z); +private: + void addVine(Level *level, int xx, int yy, int zz, int dir); +}; \ No newline at end of file diff --git a/Minecraft.World/SwellGoal.cpp b/Minecraft.World/SwellGoal.cpp new file mode 100644 index 00000000..9065ba16 --- /dev/null +++ b/Minecraft.World/SwellGoal.cpp @@ -0,0 +1,54 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.sensing.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.monster.h" +#include "SwellGoal.h" + +SwellGoal::SwellGoal(Creeper *creeper) +{ + target = weak_ptr(); + + this->creeper = creeper; + setRequiredControlFlags(Control::MoveControlFlag); +} + +bool SwellGoal::canUse() +{ + shared_ptr target = creeper->getTarget(); + return creeper->getSwellDir() > 0 || (target != NULL && (creeper->distanceToSqr(target) < 3 * 3)); +} + +void SwellGoal::start() +{ + creeper->getNavigation()->stop(); + target = weak_ptr(creeper->getTarget()); +} + +void SwellGoal::stop() +{ + target = weak_ptr(); +} + +void SwellGoal::tick() +{ + if (target.lock() == NULL) + { + creeper->setSwellDir(-1); + return; + } + + if (creeper->distanceToSqr(target.lock()) > 7 * 7) + { + creeper->setSwellDir(-1); + return; + } + + if (!creeper->getSensing()->canSee(target.lock())) + { + creeper->setSwellDir(-1); + return; + } + + creeper->setSwellDir(1); +} diff --git a/Minecraft.World/SwellGoal.h b/Minecraft.World/SwellGoal.h new file mode 100644 index 00000000..ba6b5379 --- /dev/null +++ b/Minecraft.World/SwellGoal.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Goal.h" + +class Creeper; + +class SwellGoal : public Goal +{ +private: + Creeper *creeper; + weak_ptr target; + +public: + SwellGoal(Creeper *creeper); + + bool canUse(); + void start(); + void stop(); + void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/SynchedEntityData.cpp b/Minecraft.World/SynchedEntityData.cpp new file mode 100644 index 00000000..9f1aa1f7 --- /dev/null +++ b/Minecraft.World/SynchedEntityData.cpp @@ -0,0 +1,558 @@ +#include "stdafx.h" +#include "Class.h" +#include "BasicTypeContainers.h" +#include "InputOutputStream.h" +#include "net.minecraft.h" +#include "net.minecraft.network.packet.h" +#include "net.minecraft.world.item.h" +#include "SynchedEntityData.h" + + +SynchedEntityData::SynchedEntityData() +{ + m_isDirty = false; + m_isEmpty = true; +} + +void SynchedEntityData::define(int id, int value) +{ + MemSect(17); + checkId(id); + int type = TYPE_INT; + shared_ptr dataItem = shared_ptr( new DataItem(type, id, value) ); + itemsById[id] = dataItem; + MemSect(0); + m_isEmpty = false; +} + +void SynchedEntityData::define(int id, byte value) +{ + MemSect(17); + checkId(id); + int type = TYPE_BYTE; + shared_ptr dataItem = shared_ptr( new DataItem(type, id, value) ); + itemsById[id] = dataItem; + MemSect(0); + m_isEmpty = false; +} + +void SynchedEntityData::define(int id, short value) +{ + MemSect(17); + checkId(id); + int type = TYPE_SHORT; + shared_ptr dataItem = shared_ptr( new DataItem(type, id, value) ); + itemsById[id] = dataItem; + MemSect(0); + m_isEmpty = false; +} + +void SynchedEntityData::define(int id, const wstring& value) +{ + MemSect(17); + checkId(id); + int type = TYPE_STRING; + shared_ptr dataItem = shared_ptr( new DataItem(type, id, value) ); + itemsById[id] = dataItem; + MemSect(0); + m_isEmpty = false; +} + +void SynchedEntityData::defineNULL(int id, void *pVal) +{ + MemSect(17); + checkId(id); + int type = TYPE_ITEMINSTANCE; + shared_ptr dataItem = shared_ptr( new DataItem(type, id, shared_ptr()) ); + itemsById[id] = dataItem; + MemSect(0); + m_isEmpty = false; +} + +void SynchedEntityData::checkId(int id) +{ +#if 0 + if (id > MAX_ID_VALUE) + { + throw new IllegalArgumentException(L"Data value id is too big with " + _toString(id) + L"! (Max is " + _toString(MAX_ID_VALUE) + L")"); + } + if (itemsById.find(id) != itemsById.end()) + { + throw new IllegalArgumentException(L"Duplicate id value for " + _toString(id) + L"!"); + } +#endif +} + +byte SynchedEntityData::getByte(int id) +{ + return itemsById[id]->getValue_byte(); +} + +short SynchedEntityData::getShort(int id) +{ + return itemsById[id]->getValue_short(); +} + +int SynchedEntityData::getInteger(int id) +{ + return itemsById[id]->getValue_int(); +} + +float SynchedEntityData::getFloat(int id) +{ + assert(false); // 4J - not currently implemented + return 0; +} + +wstring SynchedEntityData::getString(int id) +{ + return itemsById[id]->getValue_wstring(); +} + +shared_ptr SynchedEntityData::getItemInstance(int id) +{ + //assert(false); // 4J - not currently implemented + return itemsById[id]->getValue_itemInstance(); +} + +Pos *SynchedEntityData::getPos(int id) +{ + assert(false); // 4J - not currently implemented + return NULL; +} + +void SynchedEntityData::set(int id, int value) +{ + shared_ptr dataItem = itemsById[id]; + + // update the value if it has changed + if (value != dataItem->getValue_int()) + { + dataItem->setValue(value); + dataItem->setDirty(true); + m_isDirty = true; + } +} + +void SynchedEntityData::set(int id, byte value) +{ + shared_ptr dataItem = itemsById[id]; + + // update the value if it has changed + if (value != dataItem->getValue_byte()) + { + dataItem->setValue(value); + dataItem->setDirty(true); + m_isDirty = true; + } +} + +void SynchedEntityData::set(int id, short value) +{ + shared_ptr dataItem = itemsById[id]; + + // update the value if it has changed + if (value != dataItem->getValue_short()) + { + dataItem->setValue(value); + dataItem->setDirty(true); + m_isDirty = true; + } +} + +void SynchedEntityData::set(int id, const wstring& value) +{ + shared_ptr dataItem = itemsById[id]; + + // update the value if it has changed + if (value != dataItem->getValue_wstring()) + { + dataItem->setValue(value); + dataItem->setDirty(true); + m_isDirty = true; + } +} + +void SynchedEntityData::set(int id, shared_ptr value) +{ + shared_ptr dataItem = itemsById[id]; + + // update the value if it has changed + if (value != dataItem->getValue_itemInstance()) + { + dataItem->setValue(value); + dataItem->setDirty(true); + m_isDirty = true; + } +} + +void SynchedEntityData::markDirty(int id) +{ + (*itemsById.find(id)).second->dirty = true; + m_isDirty = true; +} + +bool SynchedEntityData::isDirty() +{ + return m_isDirty; +} + +void SynchedEntityData::pack(vector > *items, DataOutputStream *output) // TODO throws IOException +{ + + if (items != NULL) + { + AUTO_VAR(itEnd, items->end()); + for (AUTO_VAR(it, items->begin()); it != itEnd; it++) + { + shared_ptr dataItem = *it; + writeDataItem(output, dataItem); + } + } + + // add an eof + output->writeByte(EOF_MARKER); +} + +vector > *SynchedEntityData::packDirty() +{ + + vector > *result = NULL; + + if (m_isDirty) + { + AUTO_VAR(itEnd, itemsById.end()); + for ( AUTO_VAR(it, itemsById.begin()); it != itEnd; it++ ) + { + shared_ptr dataItem = (*it).second; + if (dataItem->isDirty()) + { + dataItem->setDirty(false); + + if (result == NULL) + { + result = new vector >(); + } + result->push_back(dataItem); + } + } + } + m_isDirty = false; + + return result; +} + +void SynchedEntityData::packAll(DataOutputStream *output) // throws IOException +{ + AUTO_VAR(itEnd, itemsById.end()); + for (AUTO_VAR(it, itemsById.begin()); it != itEnd; it++ ) + { + shared_ptr dataItem = (*it).second; + writeDataItem(output, dataItem); + } + + // add an eof + output->writeByte(EOF_MARKER); +} + +vector > *SynchedEntityData::getAll() +{ + vector > *result = NULL; + + AUTO_VAR(itEnd, itemsById.end()); + for (AUTO_VAR(it, itemsById.begin()); it != itEnd; it++ ) + { + if (result == NULL) + { + result = new vector >(); + } + shared_ptr dataItem = (*it).second; + result->push_back(dataItem); + } + + return result; +} + + +void SynchedEntityData::writeDataItem(DataOutputStream *output, shared_ptr dataItem) //throws IOException +{ + // pack type and id + int header = ((dataItem->getType() << TYPE_SHIFT) | (dataItem->getId() & MAX_ID_VALUE)) & 0xff; + output->writeByte(header); + + // write value + switch (dataItem->getType()) + { + case TYPE_BYTE: + output->writeByte( dataItem->getValue_byte()); + break; + case TYPE_INT: + output->writeInt( dataItem->getValue_int()); + break; + case TYPE_SHORT: + output->writeShort( dataItem->getValue_short()); + break; + case TYPE_STRING: + Packet::writeUtf(dataItem->getValue_wstring(), output); + break; + case TYPE_ITEMINSTANCE: + { + shared_ptr instance = (shared_ptr )dataItem->getValue_itemInstance(); + Packet::writeItem(instance, output); + } + break; + + default: + assert(false); // 4J - not implemented + break; + } +} + +vector > *SynchedEntityData::unpack(DataInputStream *input) //throws IOException +{ + + vector > *result = NULL; + + int currentHeader = input->readByte(); + + while (currentHeader != EOF_MARKER) + { + + if (result == NULL) + { + result = new vector >(); + } + + // split type and id + int itemType = (currentHeader & TYPE_MASK) >> TYPE_SHIFT; + int itemId = (currentHeader & MAX_ID_VALUE); + + shared_ptr item = shared_ptr(); + switch (itemType) + { + case TYPE_BYTE: + { + byte dataRead = input->readByte(); + item = shared_ptr( new DataItem(itemType, itemId, dataRead) ); + } + break; + case TYPE_SHORT: + { + short dataRead = input->readShort(); + item = shared_ptr( new DataItem(itemType, itemId, dataRead) ); + } + break; + case TYPE_INT: + { + int dataRead = input->readInt(); + item = shared_ptr( new DataItem(itemType, itemId, dataRead) ); + } + break; + case TYPE_STRING: + item = shared_ptr( new DataItem(itemType, itemId, Packet::readUtf(input, MAX_STRING_DATA_LENGTH)) ); + break; + case TYPE_ITEMINSTANCE: + { + item = shared_ptr(new DataItem(itemType, itemId, Packet::readItem(input))); + } + break; + default: + app.DebugPrintf(" ------ garbage data, or early end of stream due to an incomplete packet\n"); + delete result; + return NULL; + break; + } + result->push_back(item); + + currentHeader = input->readByte(); + } + + return result; +} + +/** +* Assigns values from a list of data items. +* +* @param items +*/ + +void SynchedEntityData::assignValues(vector > *items) +{ + AUTO_VAR(itEnd, items->end()); + for (AUTO_VAR(it, items->begin()); it != itEnd; it++) + { + shared_ptr item = *it; + AUTO_VAR(itemFromId, itemsById.find(item->getId())); + if (itemFromId != itemsById.end() ) + { + switch(item->getType()) + { + case TYPE_BYTE: + itemFromId->second->setValue(item->getValue_byte()); + break; + case TYPE_SHORT: + itemFromId->second->setValue(item->getValue_short()); + break; + case TYPE_INT: + itemFromId->second->setValue(item->getValue_int()); + break; + case TYPE_STRING: + itemFromId->second->setValue(item->getValue_wstring()); + break; + case TYPE_ITEMINSTANCE: + itemFromId->second->setValue(item->getValue_itemInstance()); + break; + default: + assert(false); // 4J - not implemented + break; + } + } + } +} + +bool SynchedEntityData::isEmpty() +{ + return m_isEmpty; +} + +int SynchedEntityData::getSizeInBytes() +{ + int size = 1; + + AUTO_VAR(itEnd, itemsById.end()); + for (AUTO_VAR(it, itemsById.begin()); it != itEnd; it++ ) + { + shared_ptr dataItem = (*it).second; + + size += 1; + + // write value + switch (dataItem->getType()) + { + case TYPE_BYTE: + size += 1; + break; + case TYPE_SHORT: + size += 2; + break; + case TYPE_INT: + size += 4; + break; + case TYPE_STRING: + size += (int)dataItem->getValue_wstring().length() + 2; // Estimate, assuming all ascii chars + break; + case TYPE_ITEMINSTANCE: + // short + byte + short + size += 2 + 1 + 2; // Estimate, assuming all ascii chars + break; + default: + break; + } + } + return size; +} + + +////////////////// +// DataItem class +///////////////// + +SynchedEntityData::DataItem::DataItem(int type, int id, int value) : type( type ), id( id ) +{ + this->value_int = value; + this->dirty = true; +} + +SynchedEntityData::DataItem::DataItem(int type, int id, byte value) : type( type ), id( id ) +{ + this->value_byte = value; + this->dirty = true; +} + +SynchedEntityData::DataItem::DataItem(int type, int id, short value) : type( type ), id( id ) +{ + this->value_short = value; + this->dirty = true; +} + +SynchedEntityData::DataItem::DataItem(int type, int id, const wstring& value) : type( type ), id( id ) +{ + this->value_wstring = value; + this->dirty = true; +} + +SynchedEntityData::DataItem::DataItem(int type, int id, shared_ptr itemInstance) : type( type ), id( id ) +{ + this->value_itemInstance = itemInstance; + this->dirty = true; +} + +int SynchedEntityData::DataItem::getId() +{ + return id; +} + +void SynchedEntityData::DataItem::setValue(int value) +{ + this->value_int = value; +} + +void SynchedEntityData::DataItem::setValue(byte value) +{ + this->value_byte = value; +} + +void SynchedEntityData::DataItem::setValue(short value) +{ + this->value_short = value; +} + +void SynchedEntityData::DataItem::setValue(const wstring& value) +{ + this->value_wstring = value; +} + +void SynchedEntityData::DataItem::setValue(shared_ptr itemInstance) +{ + this->value_itemInstance = itemInstance; +} + +int SynchedEntityData::DataItem::getValue_int() +{ + return value_int; +} + +short SynchedEntityData::DataItem::getValue_short() +{ + return value_short; +} + +byte SynchedEntityData::DataItem::getValue_byte() +{ + return value_byte; +} + +wstring SynchedEntityData::DataItem::getValue_wstring() +{ + return value_wstring; +} + +shared_ptr SynchedEntityData::DataItem::getValue_itemInstance() +{ + return value_itemInstance; +} + +int SynchedEntityData::DataItem::getType() +{ + return type; +} + +bool SynchedEntityData::DataItem::isDirty() +{ + return dirty; +} + +void SynchedEntityData::DataItem::setDirty(bool dirty) +{ + this->dirty = dirty; +} \ No newline at end of file diff --git a/Minecraft.World/SynchedEntityData.h b/Minecraft.World/SynchedEntityData.h new file mode 100644 index 00000000..e79a10b8 --- /dev/null +++ b/Minecraft.World/SynchedEntityData.h @@ -0,0 +1,127 @@ +#pragma once +using namespace std; + +class Pos; + + +class SynchedEntityData +{ +public: + class DataItem + { + friend class SynchedEntityData; + private: + const int type; + const int id; + // 4J - there used to be one "value" type here of general type Object, just storing the different (used) varieties + // here separately for us + byte value_byte; + int value_int; + short value_short; + wstring value_wstring; + shared_ptr value_itemInstance; + bool dirty; + + public: + // There was one type here that took a generic Object type, using overloading here instead + DataItem(int type, int id, byte value); + DataItem(int type, int id, int value); + DataItem(int type, int id, const wstring& value); + DataItem(int type, int id, shared_ptr itemInstance); + DataItem(int type, int id, short value); + + int getId(); + void setValue(byte value); + void setValue(int value); + void setValue(short value); + void setValue(const wstring& value); + void setValue(shared_ptr value); + byte getValue_byte(); + int getValue_int(); + short getValue_short(); + wstring getValue_wstring(); + shared_ptr getValue_itemInstance(); + int getType(); + bool isDirty(); + void setDirty(bool dirty); + }; + +public: + static const int MAX_STRING_DATA_LENGTH = 64; + static const int EOF_MARKER = 0x7f; + +private: + static const int TYPE_BYTE = 0; + static const int TYPE_SHORT = 1; + static const int TYPE_INT = 2; + static const int TYPE_FLOAT = 3; + static const int TYPE_STRING = 4; + // special types (max possible value is 7): + static const int TYPE_ITEMINSTANCE = 5; + static const int TYPE_POS = 6; + +private: + bool m_isEmpty; + + // must have enough bits to fit the type +private: + static const int TYPE_MASK = 0xe0; + static const int TYPE_SHIFT = 5; + + // the id value must fit in the remaining bits + static const int MAX_ID_VALUE = ~TYPE_MASK & 0xff; + + unordered_map > itemsById; + bool m_isDirty; + +public: + SynchedEntityData(); + + // 4J - this function used to be a template, but there's only 3 varieties of use I've found so just hard-coding now, as + // the original had some automatic Class to type sort of conversion that's a real pain for us to actually do + void define(int id, byte value); + void define(int id, const wstring& value); + void define(int id, int value); + void define(int id, short value); + void defineNULL(int id, void *pVal); + + void checkId(int id); // 4J - added to contain common code from overloaded define functions above + byte getByte(int id); + short getShort(int id); + int getInteger(int id); + float getFloat(int id); + wstring getString(int id); + shared_ptr getItemInstance(int id); + Pos *getPos(int id); + // 4J - using overloads rather than template here + void set(int id, byte value); + void set(int id, int value); + void set(int id, short value); + void set(int id, const wstring& value); + void set(int id, shared_ptr); + void markDirty(int id); + bool isDirty(); + static void pack(vector > *items, DataOutputStream *output); // TODO throws IOException + vector > *packDirty(); + void packAll(DataOutputStream *output); // throws IOException + vector > *getAll(); + +private: + static void writeDataItem(DataOutputStream *output, shared_ptr dataItem); //throws IOException + + +public: + static vector > *unpack(DataInputStream *input); // throws IOException + + /** + * Assigns values from a list of data items. + * + * @param items + */ +public: + void assignValues(vector > *items); + bool isEmpty(); + + // 4J Added + int getSizeInBytes(); +}; \ No newline at end of file diff --git a/Minecraft.World/Synth.cpp b/Minecraft.World/Synth.cpp new file mode 100644 index 00000000..c6e926e5 --- /dev/null +++ b/Minecraft.World/Synth.cpp @@ -0,0 +1,15 @@ +#include "stdafx.h" +#include "Synth.h" + +doubleArray Synth::create(int width, int height) +{ + doubleArray result = doubleArray(width * height); + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + result[x + y * width] = getValue(x, y); + } + } + return result; +} diff --git a/Minecraft.World/Synth.h b/Minecraft.World/Synth.h new file mode 100644 index 00000000..74e68634 --- /dev/null +++ b/Minecraft.World/Synth.h @@ -0,0 +1,9 @@ +#pragma once +class Synth +{ +public: + virtual double getValue(double x, double y) = 0; + + doubleArray create(int width, int height); +}; + diff --git a/Minecraft.World/System.h b/Minecraft.World/System.h new file mode 100644 index 00000000..06e265cc --- /dev/null +++ b/Minecraft.World/System.h @@ -0,0 +1,39 @@ +#pragma once +using namespace std; + +#include "ArrayWithLength.h" + +// 4J Jev, just thought it would be easier this way. +#define ArrayCopyFunctionDeclaration(x) static void arraycopy(arrayWithLength src, unsigned int srcPos, arrayWithLength *dst, unsigned int dstPos, unsigned int length); +#define ArrayCopyFunctionDefinition(x) void System::arraycopy(arrayWithLength src, unsigned int srcPos, arrayWithLength *dst, unsigned int dstPos, unsigned int length)\ +{\ + arraycopy(src,srcPos,dst,dstPos,length);\ +}\ + +class System +{ + template static void arraycopy(arrayWithLength src, unsigned int srcPos, arrayWithLength *dst, unsigned int dstPos, unsigned int length); + +public: + ArrayCopyFunctionDeclaration(byte) + ArrayCopyFunctionDeclaration(Node *) + ArrayCopyFunctionDeclaration(Biome *) + ArrayCopyFunctionDeclaration(int) + + static __int64 nanoTime(); + static __int64 currentTimeMillis(); + static __int64 currentRealTimeMillis(); // 4J Added to get real-world time for timestamps in saves + + static void ReverseUSHORT(unsigned short *pusVal); + static void ReverseSHORT(short *psVal); + static void ReverseULONG(unsigned long *pulVal); + static void ReverseULONG(unsigned int *pulVal); + static void ReverseINT(int *piVal); + static void ReverseULONGLONG(__int64 *pullVal); + static void ReverseWCHARA(WCHAR *pwch,int iLen); + +}; + +#define MAKE_FOURCC(ch0, ch1, ch2, ch3) \ + ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \ + ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24 )) diff --git a/Minecraft.World/Tag.cpp b/Minecraft.World/Tag.cpp new file mode 100644 index 00000000..40da99fe --- /dev/null +++ b/Minecraft.World/Tag.cpp @@ -0,0 +1,181 @@ +#include "stdafx.h" +#include "Tag.h" +#include "EndTag.h" +#include "ByteTag.h" +#include "ByteArrayTag.h" +#include "DoubleTag.h" +#include "FloatTag.h" +#include "IntTag.h" +#include "LongTag.h" +#include "ShortTag.h" +#include "StringTag.h" +#include "ListTag.h" +#include "CompoundTag.h" + +Tag::Tag(const wstring &name) +{ + if (name.empty()) + { + this->name = L""; + } + else + { + this->name = name; + } +} + +// 4J - Was Object obj +bool Tag::equals(Tag *obj) +{ + if (obj == NULL )// || !(obj instanceof Tag)) + { + return false; + } + Tag *o = (Tag *) obj; + if (getId() != o->getId()) + { + return false; + } + if ( (name.empty() && !o->name.empty()) || (!name.empty() && o->name.empty())) + { + return false; + } + if (!name.empty() && name.compare(o->name) != 0) + { + return false; + } + return true; +} + +void Tag::print(ostream out) +{ + out << ""; +} + +void Tag::print(char *prefix, wostream out) +{ + wstring name = getName(); + + out << prefix; + out << getTagName(getId()); + if ( name.length() > 0) + { + out << L"(\"" << name << L"\")"; + } + out << L": "; + out << toString() << endl; +} + +wstring Tag::getName() +{ + return name; +} + +Tag *Tag::setName(const wstring& name) +{ + this->name = name; + return this; +} + +Tag *Tag::readNamedTag(DataInput *dis) +{ + byte type = dis->readByte(); + if (type == 0) return new EndTag(); + + // 4J Stu - readByte can return -1, so if it's that then also mark as the end tag + if(type == 255) + { + app.DebugPrintf("readNamedTag read a type of 255\n"); +#ifndef _CONTENT_PACKAGE + __debugbreak(); +#endif + return new EndTag(); + } + + wstring name = dis->readUTF();//new String(bytes, "UTF-8"); + + Tag *tag = newTag(type, name); + // short length = dis.readShort(); + // byte[] bytes = new byte[length]; + // dis.readFully(bytes); + + tag->load(dis); + return tag; +} + +void Tag::writeNamedTag(Tag *tag, DataOutput *dos) +{ + dos->writeByte(tag->getId()); + if (tag->getId() == Tag::TAG_End) return; + + // byte[] bytes = tag.getName().getBytes("UTF-8"); + // dos.writeShort(bytes.length); + // dos.write(bytes); + dos->writeUTF(tag->getName()); + + tag->write(dos); +} + +Tag *Tag::newTag(byte type, const wstring &name) +{ + switch (type) + { + case TAG_End: + return new EndTag(name); + case TAG_Byte: + return new ByteTag(name); + case TAG_Short: + return new ShortTag(name); + case TAG_Int: + return new IntTag(name); + case TAG_Long: + return new LongTag(name); + case TAG_Float: + return new FloatTag(name); + case TAG_Double: + return new DoubleTag(name); + case TAG_Byte_Array: + return new ByteArrayTag(name); + case TAG_Int_Array: + return new IntArrayTag(name); + case TAG_String: + return new StringTag(name); + case TAG_List: + return new ListTag(name); + case TAG_Compound: + return new CompoundTag(name); + } + return NULL; +} + +wchar_t *Tag::getTagName(byte type) +{ + switch (type) + { + case TAG_End: + return L"TAG_End"; + case TAG_Byte: + return L"TAG_Byte"; + case TAG_Short: + return L"TAG_Short"; + case TAG_Int: + return L"TAG_Int"; + case TAG_Long: + return L"TAG_Long"; + case TAG_Float: + return L"TAG_Float"; + case TAG_Double: + return L"TAG_Double"; + case TAG_Byte_Array: + return L"TAG_Byte_Array"; + case TAG_Int_Array: + return L"TAG_Int_Array"; + case TAG_String: + return L"TAG_String"; + case TAG_List: + return L"TAG_List"; + case TAG_Compound: + return L"TAG_Compound"; + } + return L"UNKNOWN"; +} \ No newline at end of file diff --git a/Minecraft.World/Tag.h b/Minecraft.World/Tag.h new file mode 100644 index 00000000..c3221aea --- /dev/null +++ b/Minecraft.World/Tag.h @@ -0,0 +1,45 @@ +#pragma once +#include +#include "InputOutputStream.h" +using namespace std; + + +class Tag +{ +public: + static const byte TAG_End = 0; + static const byte TAG_Byte = 1; + static const byte TAG_Short = 2; + static const byte TAG_Int = 3; + static const byte TAG_Long = 4; + static const byte TAG_Float = 5; + static const byte TAG_Double = 6; + static const byte TAG_Byte_Array = 7; + static const byte TAG_String = 8; + static const byte TAG_List = 9; + static const byte TAG_Compound = 10; + static const byte TAG_Int_Array = 11; + +private: + wstring name; + +protected: + Tag(const wstring &name); + +public: + virtual void write(DataOutput *dos) = 0; + virtual void load(DataInput *dis) = 0; + virtual wstring toString() = 0; + virtual byte getId() = 0; + void print(ostream out); + void print(char *prefix, wostream out); + wstring getName(); + Tag *setName(const wstring& name); + static Tag *readNamedTag(DataInput *dis); + static void writeNamedTag(Tag *tag, DataOutput *dos); + static Tag *newTag(byte type, const wstring &name); + static wchar_t *getTagName(byte type); + virtual ~Tag() {} + virtual bool equals(Tag *obj); // 4J Brought forward from 1.2 + virtual Tag *copy() = 0; // 4J Brought foward from 1.2 +}; diff --git a/Minecraft.World/TaigaBiome.cpp b/Minecraft.World/TaigaBiome.cpp new file mode 100644 index 00000000..7a9cc105 --- /dev/null +++ b/Minecraft.World/TaigaBiome.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "TaigaBiome.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.level.biome.h" + +TaigaBiome::TaigaBiome(int id) : Biome(id) +{ + friendlies_wolf.push_back(new MobSpawnerData(eTYPE_WOLF, 8, 4, 4)); // 4J - moved to their own category + + decorator->treeCount = 10; + decorator->grassCount = 1; +} + +Feature *TaigaBiome::getTreeFeature(Random *random) +{ + if (random->nextInt(3) == 0) + { + return new PineFeature(); + } + return new SpruceFeature(false); +} \ No newline at end of file diff --git a/Minecraft.World/TaigaBiome.h b/Minecraft.World/TaigaBiome.h new file mode 100644 index 00000000..8250fab1 --- /dev/null +++ b/Minecraft.World/TaigaBiome.h @@ -0,0 +1,10 @@ +#pragma once +#include "Biome.h" + +class TaigaBiome : public Biome +{ +public: + TaigaBiome(int id); + + virtual Feature *getTreeFeature(Random *random); +}; \ No newline at end of file diff --git a/Minecraft.World/TakeFlowerGoal.cpp b/Minecraft.World/TakeFlowerGoal.cpp new file mode 100644 index 00000000..34c584c6 --- /dev/null +++ b/Minecraft.World/TakeFlowerGoal.cpp @@ -0,0 +1,82 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.npc.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "TakeFlowerGoal.h" + +TakeFlowerGoal::TakeFlowerGoal(Villager *villager) +{ + takeFlower = false; + pickupTick = 0; + golem = weak_ptr(); + + this->villager = villager; + setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag); +} + +bool TakeFlowerGoal::canUse() +{ + if (villager->getAge() >= 0) return false; + if (!villager->level->isDay()) return false; + + vector > *golems = villager->level->getEntitiesOfClass(typeid(VillagerGolem), villager->bb->grow(6, 2, 6)); + if (golems->size() == 0) + { + delete golems; + return false; + } + + //for (Entity e : golems) + for(AUTO_VAR(it,golems->begin()); it != golems->end(); ++it) + { + shared_ptr vg = dynamic_pointer_cast(*it); + if (vg->getOfferFlowerTick() > 0) + { + golem = weak_ptr(vg); + break; + } + } + delete golems; + return golem.lock() != NULL; +} + +bool TakeFlowerGoal::canContinueToUse() +{ + return golem.lock() != NULL && golem.lock()->getOfferFlowerTick() > 0; +} + +void TakeFlowerGoal::start() +{ + pickupTick = villager->getRandom()->nextInt((int) (OfferFlowerGoal::OFFER_TICKS * 0.8)); + takeFlower = false; + golem.lock()->getNavigation()->stop(); +} + +void TakeFlowerGoal::stop() +{ + golem = weak_ptr(); + villager->getNavigation()->stop(); +} + +void TakeFlowerGoal::tick() +{ + villager->getLookControl()->setLookAt(golem.lock(), 30, 30); + if (golem.lock()->getOfferFlowerTick() == pickupTick) + { + villager->getNavigation()->moveTo(golem.lock(), 0.15f); + takeFlower = true; + } + + if (takeFlower) + { + if (villager->distanceToSqr(golem.lock()) < 2 * 2) + { + golem.lock()->offerFlower(false); + villager->getNavigation()->stop(); + } + } +} \ No newline at end of file diff --git a/Minecraft.World/TakeFlowerGoal.h b/Minecraft.World/TakeFlowerGoal.h new file mode 100644 index 00000000..923f35fd --- /dev/null +++ b/Minecraft.World/TakeFlowerGoal.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Goal.h" + +class TakeFlowerGoal : public Goal +{ +private: + Villager *villager; + weak_ptr golem; + int pickupTick; + bool takeFlower; + +public: + TakeFlowerGoal(Villager *villager); + + bool canUse(); + bool canContinueToUse(); + void start(); + void stop(); + void tick(); +}; \ No newline at end of file diff --git a/Minecraft.World/TakeItemEntityPacket.cpp b/Minecraft.World/TakeItemEntityPacket.cpp new file mode 100644 index 00000000..1c6da009 --- /dev/null +++ b/Minecraft.World/TakeItemEntityPacket.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "TakeItemEntityPacket.h" + + + +TakeItemEntityPacket::TakeItemEntityPacket() +{ + itemId = -1; + playerId = -1; +} + +TakeItemEntityPacket::TakeItemEntityPacket(int itemId, int playerId) +{ + this->itemId = itemId; + this->playerId = playerId; +} + +void TakeItemEntityPacket::read(DataInputStream *dis) //throws IOException +{ + itemId = dis->readInt(); + playerId = dis->readInt(); +} + +void TakeItemEntityPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(itemId); + dos->writeInt(playerId); +} + +void TakeItemEntityPacket::handle(PacketListener *listener) +{ + listener->handleTakeItemEntity(shared_from_this()); +} + +int TakeItemEntityPacket::getEstimatedSize() +{ + return 8; +} diff --git a/Minecraft.World/TakeItemEntityPacket.h b/Minecraft.World/TakeItemEntityPacket.h new file mode 100644 index 00000000..7c4b45fe --- /dev/null +++ b/Minecraft.World/TakeItemEntityPacket.h @@ -0,0 +1,22 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class TakeItemEntityPacket : public Packet, public enable_shared_from_this +{ +public: + int itemId, playerId; + + TakeItemEntityPacket(); + TakeItemEntityPacket(int itemId, int playerId); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new TakeItemEntityPacket()); } + virtual int getId() { return 22; } +}; \ No newline at end of file diff --git a/Minecraft.World/TallGrass.cpp b/Minecraft.World/TallGrass.cpp new file mode 100644 index 00000000..e2e7cb19 --- /dev/null +++ b/Minecraft.World/TallGrass.cpp @@ -0,0 +1,119 @@ +#include "stdafx.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.h" +#include "TallGrass.h" + +const unsigned int TallGrass::TALL_GRASS_TILE_NAMES[TALL_GRASS_TILE_NAMES_LENGTH] = { IDS_TILE_SHRUB, + IDS_TILE_GRASS, + IDS_TILE_FERN, + }; + +const wstring TallGrass::TEXTURE_NAMES[] = {L"deadbush", L"tallgrass", L"fern"}; + +TallGrass::TallGrass(int id) : Bush(id, Material::replaceable_plant) +{ + this->updateDefaultShape(); +} + +// 4J Added override +void TallGrass::updateDefaultShape() +{ + float ss = 0.4f; + this->setShape(0.5f - ss, 0, 0.5f - ss, 0.5f + ss, 0.8f, 0.5f + ss); +} + + +Icon *TallGrass::getTexture(int face, int data) +{ + if (data >= TALL_GRASS_TILE_NAMES_LENGTH) data = 0; + return icons[data]; +} + +int TallGrass::getColor(int auxData) +{ + if (auxData == DEAD_SHRUB) return 0xffffff; + + return FoliageColor::getDefaultColor(); +} + +int TallGrass::getColor() const +{ + // 4J Stu - Not using this any more + //double temp = 0.5; + //double rain = 1.0; + + //return GrassColor::get(temp, rain); + + return Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_Grass_Common ); +} + +int TallGrass::getColor(LevelSource *level, int x, int y, int z ) +{ + return getColor( level, x, y, z, level->getData(x, y, z) ); +} + +// 4J - changed interface to have data passed in, and put existing interface as wrapper above +int TallGrass::getColor(LevelSource *level, int x, int y, int z, int data) +{ + int d = data; + if (d == DEAD_SHRUB) return 0xffffff; + + return level->getBiome(x, z)->getGrassColor(); +} + +int TallGrass::getResource(int data, Random *random, int playerBonusLevel) +{ + if (random->nextInt(8) == 0) { + return Item::seeds_wheat->id; + } + + return -1; +} + +int TallGrass::getResourceCountForLootBonus(int bonusLevel, Random *random) +{ + return 1 + random->nextInt(bonusLevel * 2 + 1); +} + +void TallGrass::playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data) +{ + if (!level->isClientSide && player->getSelectedItem() != NULL && player->getSelectedItem()->id == Item::shears->id) + { + player->awardStat( + GenericStats::blocksMined(id), + GenericStats::param_blocksMined(id,data,1) + ); + + // drop leaf block instead of sapling + popResource(level, x, y, z, shared_ptr(new ItemInstance(Tile::tallgrass, 1, data))); + } + else + { + Bush::playerDestroy(level, player, x, y, z, data); + } +} + +int TallGrass::cloneTileData(Level *level, int x, int y, int z) +{ + return level->getData(x, y, z); +} + +unsigned int TallGrass::getDescriptionId(int iData /*= -1*/) +{ + if(iData < 0 ) iData = 0; + return TallGrass::TALL_GRASS_TILE_NAMES[iData]; +} + +void TallGrass::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[TALL_GRASS_TILE_NAMES_LENGTH]; + + for (int i = 0; i < TALL_GRASS_TILE_NAMES_LENGTH; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURE_NAMES[i]); + } +} diff --git a/Minecraft.World/TallGrass.h b/Minecraft.World/TallGrass.h new file mode 100644 index 00000000..9b4c5515 --- /dev/null +++ b/Minecraft.World/TallGrass.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Bush.h" +class ChunkRebuildData; + +class TallGrass : public Bush +{ + friend class Tile; + friend ChunkRebuildData; +public: + static const int DEAD_SHRUB = 0; + static const int TALL_GRASS = 1; + static const int FERN = 2; + + static const int TALL_GRASS_TILE_NAMES_LENGTH = 3; + + static const unsigned int TALL_GRASS_TILE_NAMES[TALL_GRASS_TILE_NAMES_LENGTH]; + +private: + static const wstring TEXTURE_NAMES[]; + Icon **icons; + +protected: + TallGrass(int id); + +public: + virtual void updateDefaultShape(); // 4J Added override + virtual Icon *getTexture(int face, int data); + + virtual int getColor(int auxData); + virtual int getColor() const; + + virtual int getColor(LevelSource *level, int x, int y, int z); + virtual int getColor(LevelSource *level, int x, int y, int z, int data); // 4J added + + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int getResourceCountForLootBonus(int bonusLevel, Random *random); + virtual void playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data); + virtual int cloneTileData(Level *level, int x, int y, int z); + virtual unsigned int getDescriptionId(int iData = -1); + + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/TallGrassFeature.cpp b/Minecraft.World/TallGrassFeature.cpp new file mode 100644 index 00000000..d13ada49 --- /dev/null +++ b/Minecraft.World/TallGrassFeature.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "TallGrassFeature.h" + +TallGrassFeature::TallGrassFeature(int tile, int type) +{ + this->tile = tile; + this->type = type; +} + +bool TallGrassFeature::place(Level *level, Random *random, int x, int y, int z) +{ + int t = 0; + while (((t = level->getTile(x, y, z)) == 0 || t == Tile::leaves_Id) && y > 0) + y--; + + for (int i = 0; i < 128; i++) + { + int x2 = x + random->nextInt(8) - random->nextInt(8); + int y2 = y + random->nextInt(4) - random->nextInt(4); + int z2 = z + random->nextInt(8) - random->nextInt(8); + if (level->isEmptyTile(x2, y2, z2)) + { + if (Tile::tiles[tile]->canSurvive(level, x2, y2, z2)) + { + level->setTileAndDataNoUpdate(x2, y2, z2, tile, type); + } + } + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/TallGrassFeature.h b/Minecraft.World/TallGrassFeature.h new file mode 100644 index 00000000..f72df542 --- /dev/null +++ b/Minecraft.World/TallGrassFeature.h @@ -0,0 +1,14 @@ +#pragma once +#include "Feature.h" + +class TallGrassFeature : public Feature +{ +private: + int tile; + int type; + +public: + TallGrassFeature (int tile, int type); + + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/TamableAnimal.cpp b/Minecraft.World/TamableAnimal.cpp new file mode 100644 index 00000000..b065d7bd --- /dev/null +++ b/Minecraft.World/TamableAnimal.cpp @@ -0,0 +1,159 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "SynchedEntityData.h" +#include "ParticleTypes.h" +#include "TamableAnimal.h" + +TamableAnimal::TamableAnimal(Level *level) : Animal(level) +{ + sitGoal = new SitGoal(this); +} + +TamableAnimal::~TamableAnimal() +{ + if(sitGoal != NULL) delete sitGoal; +} + +void TamableAnimal::defineSynchedData() +{ + Animal::defineSynchedData(); + entityData->define(DATA_FLAGS_ID, (byte) 0); + entityData->define(DATA_OWNERUUID_ID, L""); +} + +void TamableAnimal::addAdditonalSaveData(CompoundTag *tag) +{ + Animal::addAdditonalSaveData(tag); +#ifdef _XBOX_ONE + // 4J Stu Added from later Java version to remove owners from save transfer saves. We will probably want this on other platforms in the future + if (getOwnerUUID().empty()) + { + tag->putString(L"OwnerUUID", L""); + } + else + { + tag->putString(L"OwnerUUID", getOwnerUUID()); + } +#else + if (getOwnerUUID().empty()) + { + tag->putString(L"Owner", L""); + } + else + { + tag->putString(L"Owner", getOwnerUUID()); + } +#endif + tag->putBoolean(L"Sitting", isSitting()); +} + +void TamableAnimal::readAdditionalSaveData(CompoundTag *tag) +{ + Animal::readAdditionalSaveData(tag); +#ifdef _XBOX_ONE + // 4J Stu Added from later Java version to remove owners from save transfer saves. We will probably want this on other platforms in the future + wstring owner = L""; + if(tag->contains(L"OwnerUUID") ) + { + owner = tag->getString(L"OwnerUUID"); + } +#else + wstring owner = tag->getString(L"Owner"); +#endif + if (owner.length() > 0) + { + setOwnerUUID(owner); + setTame(true); + } + sitGoal->wantToSit(tag->getBoolean(L"Sitting")); +} + +void TamableAnimal::spawnTamingParticles(bool success) +{ + ePARTICLE_TYPE particle = eParticleType_heart; + if (!success) + { + particle = eParticleType_smoke; + } + for (int i = 0; i < 7; i++) + { + double xa = random->nextGaussian() * 0.02; + double ya = random->nextGaussian() * 0.02; + double za = random->nextGaussian() * 0.02; + level->addParticle(particle, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + .5f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); + } +} + +void TamableAnimal::handleEntityEvent(byte id) +{ + if (id == EntityEvent::TAMING_SUCCEEDED) + { + spawnTamingParticles(true); + } + else if (id == EntityEvent::TAMING_FAILED) + { + spawnTamingParticles(false); + } + else + { + Animal::handleEntityEvent(id); + } +} + +bool TamableAnimal::isTame() +{ + return (entityData->getByte(DATA_FLAGS_ID) & 0x04) != 0; +} + +void TamableAnimal::setTame(bool value) +{ + byte current = entityData->getByte(DATA_FLAGS_ID); + if (value) + { + entityData->set(DATA_FLAGS_ID, (byte) (current | 0x04)); + } + else + { + entityData->set(DATA_FLAGS_ID, (byte) (current & ~0x04)); + } +} + +bool TamableAnimal::isSitting() +{ + return (entityData->getByte(DATA_FLAGS_ID) & 0x01) != 0; +} + +void TamableAnimal::setSitting(bool value) +{ + byte current = entityData->getByte(DATA_FLAGS_ID); + if (value) + { + entityData->set(DATA_FLAGS_ID, (byte) (current | 0x01)); + } + else + { + entityData->set(DATA_FLAGS_ID, (byte) (current & ~0x01)); + } +} + +wstring TamableAnimal::getOwnerUUID() +{ + return entityData->getString(DATA_OWNERUUID_ID); +} + +void TamableAnimal::setOwnerUUID(const wstring &name) +{ + entityData->set(DATA_OWNERUUID_ID, name); +} + +shared_ptr TamableAnimal::getOwner() +{ + return level->getPlayerByUUID(getOwnerUUID()); +} + +SitGoal *TamableAnimal::getSitGoal() +{ + return sitGoal; +} \ No newline at end of file diff --git a/Minecraft.World/TamableAnimal.h b/Minecraft.World/TamableAnimal.h new file mode 100644 index 00000000..71502788 --- /dev/null +++ b/Minecraft.World/TamableAnimal.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Animal.h" + +class SitGoal; + +class TamableAnimal : public Animal +{ +protected: + static const int DATA_FLAGS_ID = 16; + static const int DATA_OWNERUUID_ID = 17; + + SitGoal *sitGoal; + +public: + TamableAnimal(Level *level); + virtual ~TamableAnimal(); + +protected: + virtual void defineSynchedData(); + +public: + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); +protected: + virtual void spawnTamingParticles(bool success); + +public: + virtual void handleEntityEvent(byte id); + virtual bool isTame(); + virtual void setTame(bool value); + virtual bool isSitting(); + virtual void setSitting(bool value); + virtual wstring getOwnerUUID(); + virtual void setOwnerUUID(const wstring &name); + virtual shared_ptr getOwner(); + virtual SitGoal *getSitGoal(); +}; \ No newline at end of file diff --git a/Minecraft.World/TargetGoal.cpp b/Minecraft.World/TargetGoal.cpp new file mode 100644 index 00000000..c3ac4f59 --- /dev/null +++ b/Minecraft.World/TargetGoal.cpp @@ -0,0 +1,113 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.sensing.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.pathfinder.h" +#include "net.minecraft.world.phys.h" +#include "TargetGoal.h" + +void TargetGoal::_init(Mob *mob, float within, bool mustSee, bool mustReach) +{ + reachCache = EmptyReachCache; + reachCacheTime = 0; + unseenTicks = 0; + + this->mob = mob; + this->within = within; + this->mustSee = mustSee; + this->mustReach = mustReach; +} + +TargetGoal::TargetGoal(Mob *mob, float within, bool mustSee) +{ + _init(mob, within, mustSee, false); +} + +TargetGoal::TargetGoal(Mob *mob, float within, bool mustSee, bool mustReach) +{ + _init(mob,within,mustSee,mustReach); +} + +bool TargetGoal::canContinueToUse() +{ + shared_ptr target = mob->getTarget(); + if (target == NULL) return false; + if (!target->isAlive()) return false; + if (mob->distanceToSqr(target) > within * within) return false; + if (mustSee) + { + if (mob->getSensing()->canSee(target)) + { + unseenTicks = 0; + } + else + { + if (++unseenTicks > UnseenMemoryTicks) return false; + } + } + return true; +} + +void TargetGoal::start() +{ + reachCache = EmptyReachCache; + reachCacheTime = 0; + unseenTicks = 0; +} + +void TargetGoal::stop() +{ + mob->setTarget(nullptr); +} + +bool TargetGoal::canAttack(shared_ptr target, bool allowInvulnerable) +{ + if (target == NULL) return false; + if (target == mob->shared_from_this()) return false; + if (!target->isAlive()) return false; + if (!mob->canAttackType(target->GetType())) return false; + + shared_ptr tamableAnimal = dynamic_pointer_cast(mob->shared_from_this()); + if (tamableAnimal != NULL && tamableAnimal->isTame()) + { + shared_ptr tamableTarget = dynamic_pointer_cast(target); + if (tamableTarget != NULL && tamableTarget->isTame()) return false; + if (target == tamableAnimal->getOwner()) return false; + } + else if (dynamic_pointer_cast(target) != NULL) + { + if (!allowInvulnerable && (dynamic_pointer_cast(target))->abilities.invulnerable) return false; + } + + if (!mob->isWithinRestriction(Mth::floor(target->x), Mth::floor(target->y), Mth::floor(target->z))) return false; + + if (mustSee && !mob->getSensing()->canSee(target)) return false; + + if (mustReach) + { + if (--reachCacheTime <= 0) reachCache = EmptyReachCache; + if (reachCache == EmptyReachCache) reachCache = canReach(target) ? CanReachCache : CantReachCache; + if (reachCache == CantReachCache) return false; + } + + return true; +} + +bool TargetGoal::canReach(shared_ptr target) +{ + reachCacheTime = 10 + mob->getRandom()->nextInt(5); + Path *path = mob->getNavigation()->createPath(target); + if (path == NULL) return false; + Node *last = path->last(); + if (last == NULL) + { + delete path; + return false; + } + int xx = last->x - Mth::floor(target->x); + int zz = last->z - Mth::floor(target->z); + delete path; + return xx * xx + zz * zz <= 1.5 * 1.5; +} \ No newline at end of file diff --git a/Minecraft.World/TargetGoal.h b/Minecraft.World/TargetGoal.h new file mode 100644 index 00000000..3bb9da09 --- /dev/null +++ b/Minecraft.World/TargetGoal.h @@ -0,0 +1,44 @@ +#pragma once + +#include "Goal.h" + +class TargetGoal : public Goal +{ + +public: + static const int TargetFlag = 1; + +private: + static const int EmptyReachCache = 0; + static const int CanReachCache = 1; + static const int CantReachCache = 2; + static const int UnseenMemoryTicks = 60; + +protected: + Mob *mob; // Owner of this goal + float within; + bool mustSee; + +private: + bool mustReach; + int reachCache; + int reachCacheTime; + int unseenTicks; + + void _init(Mob *mob, float within, bool mustSee, bool mustReach); + +public: + TargetGoal(Mob *mob, float within, bool mustSee); + TargetGoal(Mob *mob, float within, bool mustSee, bool mustReach); + virtual ~TargetGoal() {} + + virtual bool canContinueToUse(); + virtual void start(); + virtual void stop(); + +protected: + virtual bool canAttack(shared_ptr target, bool allowInvulnerable); + +private: + bool canReach(shared_ptr target); +}; \ No newline at end of file diff --git a/Minecraft.World/TeleportEntityPacket.cpp b/Minecraft.World/TeleportEntityPacket.cpp new file mode 100644 index 00000000..f0ed1e95 --- /dev/null +++ b/Minecraft.World/TeleportEntityPacket.cpp @@ -0,0 +1,91 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "PacketListener.h" +#include "TeleportEntityPacket.h" + + + +TeleportEntityPacket::TeleportEntityPacket() +{ + id = -1; + x = 0; + y = 0; + z = 0; + yRot = 0; + xRot = 0; +} + +TeleportEntityPacket::TeleportEntityPacket(shared_ptr e) +{ + id = e->entityId; + x = Mth::floor(e->x * 32); + y = Mth::floor(e->y * 32); + z = Mth::floor(e->z * 32); + yRot = (byte) (e->yRot * 256 / 360); + xRot = (byte) (e->xRot * 256 / 360); +} + +TeleportEntityPacket::TeleportEntityPacket(int id, int x, int y, int z, byte yRot, byte xRot) +{ + this->id = id; + this->x = x; + this->y = y; + this->z = z; + this->yRot = yRot; + this->xRot = xRot; +} + +void TeleportEntityPacket::read(DataInputStream *dis) //throws IOException +{ + id = dis->readShort(); +#ifdef _LARGE_WORLDS + x = dis->readInt(); + y = dis->readInt(); + z = dis->readInt(); +#else + x = dis->readShort(); + y = dis->readShort(); + z = dis->readShort(); +#endif + yRot = (byte) dis->read(); + xRot = (byte) dis->read(); +} + +void TeleportEntityPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeShort(id); +#ifdef _LARGE_WORLDS + dos->writeInt(x); + dos->writeInt(y); + dos->writeInt(z); +#else + dos->writeShort(x); + dos->writeShort(y); + dos->writeShort(z); +#endif + dos->write(yRot); + dos->write(xRot); +} + +void TeleportEntityPacket::handle(PacketListener *listener) +{ + listener->handleTeleportEntity(shared_from_this()); +} + +int TeleportEntityPacket::getEstimatedSize() +{ + return 2 + 2 + 2 + 2 + 1 + 1; +} + +bool TeleportEntityPacket::canBeInvalidated() +{ + return true; +} + +bool TeleportEntityPacket::isInvalidatedBy(shared_ptr packet) +{ + shared_ptr target = dynamic_pointer_cast(packet); + return target->id == id; +} \ No newline at end of file diff --git a/Minecraft.World/TeleportEntityPacket.h b/Minecraft.World/TeleportEntityPacket.h new file mode 100644 index 00000000..6fc28732 --- /dev/null +++ b/Minecraft.World/TeleportEntityPacket.h @@ -0,0 +1,27 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class TeleportEntityPacket : public Packet, public enable_shared_from_this +{ +public: + int id; + int x, y, z; + byte yRot, xRot; + + TeleportEntityPacket(); + TeleportEntityPacket(shared_ptr e); + TeleportEntityPacket(int id, int x, int y, int z, byte yRot, byte xRot); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + +public: + static shared_ptr create() { return shared_ptr(new TeleportEntityPacket()); } + virtual int getId() { return 34; } +}; \ No newline at end of file diff --git a/Minecraft.World/TemperatureLayer.cpp b/Minecraft.World/TemperatureLayer.cpp new file mode 100644 index 00000000..b4963925 --- /dev/null +++ b/Minecraft.World/TemperatureLayer.cpp @@ -0,0 +1,20 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.newbiome.layer.h" + +TemperatureLayer::TemperatureLayer(shared_ptr parent) : Layer(0) +{ + this->parent = parent; +} + +intArray TemperatureLayer::getArea(int xo, int yo, int w, int h) +{ + intArray b = parent->getArea(xo, yo, w, h); + + intArray result = IntCache::allocate(w * h); + for (int i = 0; i < w * h; i++) + { + result[i] = Biome::biomes[b[i]]->getTemperatureInt(); + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/TemperatureLayer.h b/Minecraft.World/TemperatureLayer.h new file mode 100644 index 00000000..34791caa --- /dev/null +++ b/Minecraft.World/TemperatureLayer.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Layer.h" + +class TemperatureLayer : public Layer +{ +public: + TemperatureLayer(shared_ptr parent); + + virtual intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/TemperatureMixerLayer.cpp b/Minecraft.World/TemperatureMixerLayer.cpp new file mode 100644 index 00000000..27fbdf68 --- /dev/null +++ b/Minecraft.World/TemperatureMixerLayer.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.newbiome.layer.h" + +TemperatureMixerLayer::TemperatureMixerLayer(shared_ptrtemp, shared_ptrparent, int layer) : Layer(0) +{ + this->parent = parent; + this->temp = temp; + this->layer = layer; +} + +intArray TemperatureMixerLayer::getArea(int xo, int yo, int w, int h) +{ + intArray b = parent->getArea(xo, yo, w, h); + intArray t = temp->getArea(xo, yo, w, h); + + intArray result = IntCache::allocate(w * h); + for (int i = 0; i < w * h; i++) { + result[i] = t[i] + (Biome::biomes[b[i]]->getTemperatureInt() - t[i]) / (layer * 2 + 1); + } + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/TemperatureMixerLayer.h b/Minecraft.World/TemperatureMixerLayer.h new file mode 100644 index 00000000..3b3f56e0 --- /dev/null +++ b/Minecraft.World/TemperatureMixerLayer.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Layer.h" + +class TemperatureMixerLayer : public Layer +{ +private: + shared_ptrtemp; + int layer; + +public: + TemperatureMixerLayer(shared_ptrtemp, shared_ptrparent, int layer); + + virtual intArray getArea(int xo, int yo, int w, int h); +}; \ No newline at end of file diff --git a/Minecraft.World/TemptGoal.cpp b/Minecraft.World/TemptGoal.cpp new file mode 100644 index 00000000..cf0ee3d2 --- /dev/null +++ b/Minecraft.World/TemptGoal.cpp @@ -0,0 +1,91 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "TemptGoal.h" + +TemptGoal::TemptGoal(PathfinderMob *mob, float speed, int itemId, bool canScare) +{ + px = py = pz = pRotX = pRotY = 0.0; + player = weak_ptr(); + calmDown = 0; + _isRunning = false; + oldAvoidWater = false; + + this->mob = mob; + this->speed = speed; + this->itemId = itemId; + this->canScare = canScare; + setRequiredControlFlags(Control::MoveControlFlag | Control::LookControlFlag); +} + +bool TemptGoal::canUse() +{ + if (calmDown > 0) + { + --calmDown; + return false; + } + player = weak_ptr(mob->level->getNearestPlayer(mob->shared_from_this(), 10)); + if (player.lock() == NULL) return false; + mob->setDespawnProtected(); // If we've got a nearby player, then consider this mob as something we'd miss if it despawned + shared_ptr item = player.lock()->getSelectedItem(); + if (item == NULL) return false; + if (item->id != itemId) return false; + return true; +} + +bool TemptGoal::canContinueToUse() +{ + if (canScare) + { + if(player.lock() == NULL) return false; + if (mob->distanceToSqr(player.lock()) < 6 * 6) + { + if (player.lock()->distanceToSqr(px, py, pz) > 0.1 * 0.1) return false; + if (abs(player.lock()->xRot - pRotX) > 5 || abs(player.lock()->yRot - pRotY) > 5) return false; + } + else + { + px = player.lock()->x; + py = player.lock()->y; + pz = player.lock()->z; + } + pRotX = player.lock()->xRot; + pRotY = player.lock()->yRot; + } + return canUse(); +} + +void TemptGoal::start() +{ + px = player.lock()->x; + py = player.lock()->y; + pz = player.lock()->z; + _isRunning = true; + oldAvoidWater = mob->getNavigation()->getAvoidWater(); + mob->getNavigation()->setAvoidWater(false); +} + +void TemptGoal::stop() +{ + player = weak_ptr(); + mob->getNavigation()->stop(); + calmDown = 100; + _isRunning = false; + mob->getNavigation()->setAvoidWater(oldAvoidWater); +} + +void TemptGoal::tick() +{ + mob->getLookControl()->setLookAt(player.lock(), 30, mob->getMaxHeadXRot()); + if (mob->distanceToSqr(player.lock()) < 2.5 * 2.5) mob->getNavigation()->stop(); + else mob->getNavigation()->moveTo(player.lock(), speed); +} + +bool TemptGoal::isRunning() +{ + return _isRunning; +} \ No newline at end of file diff --git a/Minecraft.World/TemptGoal.h b/Minecraft.World/TemptGoal.h new file mode 100644 index 00000000..78fbe1d8 --- /dev/null +++ b/Minecraft.World/TemptGoal.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Goal.h" + +class TemptGoal : public Goal +{ +private: + PathfinderMob *mob; + float speed; + double px, py, pz, pRotX, pRotY; + weak_ptr player; + int calmDown ; + bool _isRunning; + int itemId; + bool canScare; + bool oldAvoidWater; + +public: + TemptGoal(PathfinderMob *mob, float speed, int itemId, bool canScare); + + bool canUse(); + bool canContinueToUse(); + void start(); + void stop(); + void tick(); + bool isRunning(); +}; \ No newline at end of file diff --git a/Minecraft.World/TextureAndGeometryChangePacket.cpp b/Minecraft.World/TextureAndGeometryChangePacket.cpp new file mode 100644 index 00000000..04c88bed --- /dev/null +++ b/Minecraft.World/TextureAndGeometryChangePacket.cpp @@ -0,0 +1,53 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "PacketListener.h" +#include "TextureAndGeometryChangePacket.h" + + + + +TextureAndGeometryChangePacket::TextureAndGeometryChangePacket() +{ + id = -1; + path = L""; + dwSkinID = 0; +} + +TextureAndGeometryChangePacket::TextureAndGeometryChangePacket(shared_ptr e, const wstring &path) +{ + id = e->entityId; + this->path = path; + wstring skinValue = path.substr(7,path.size()); + skinValue = skinValue.substr(0,skinValue.find_first_of(L'.')); + std::wstringstream ss; + ss << std::dec << skinValue.c_str(); + ss >> dwSkinID; + dwSkinID = MAKE_SKIN_BITMASK(true, dwSkinID); + +} + +void TextureAndGeometryChangePacket::read(DataInputStream *dis) //throws IOException +{ + id = dis->readInt(); + dwSkinID = dis->readInt(); + path = dis->readUTF(); +} + +void TextureAndGeometryChangePacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(id); + dos->writeInt(dwSkinID); + dos->writeUTF(path); +} + +void TextureAndGeometryChangePacket::handle(PacketListener *listener) +{ + listener->handleTextureAndGeometryChange(shared_from_this()); +} + +int TextureAndGeometryChangePacket::getEstimatedSize() +{ + return 8 + (int)path.size(); +} diff --git a/Minecraft.World/TextureAndGeometryChangePacket.h b/Minecraft.World/TextureAndGeometryChangePacket.h new file mode 100644 index 00000000..dabe78fa --- /dev/null +++ b/Minecraft.World/TextureAndGeometryChangePacket.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class TextureAndGeometryChangePacket : public Packet, public enable_shared_from_this +{ +public: + + int id; + wstring path; + DWORD dwSkinID; + + TextureAndGeometryChangePacket(); + TextureAndGeometryChangePacket(shared_ptr e, const wstring &path); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new TextureAndGeometryChangePacket()); } + virtual int getId() { return 161; } +}; \ No newline at end of file diff --git a/Minecraft.World/TextureAndGeometryPacket.cpp b/Minecraft.World/TextureAndGeometryPacket.cpp new file mode 100644 index 00000000..70f2f516 --- /dev/null +++ b/Minecraft.World/TextureAndGeometryPacket.cpp @@ -0,0 +1,189 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "TextureAndGeometryPacket.h" + + + +TextureAndGeometryPacket::TextureAndGeometryPacket() +{ + this->textureName = L""; + this->dwTextureBytes = 0; + this->pbData = NULL; + this->dwBoxC = 0; + this->BoxDataA = NULL; + uiAnimOverrideBitmask=0; +} + +TextureAndGeometryPacket::~TextureAndGeometryPacket() +{ + // can't free these - they're used elsewhere +// if(this->BoxDataA!=NULL) +// { +// delete [] this->BoxDataA; +// } +// +// if(this->pbData!=NULL) +// { +// delete [] this->pbData; +// } +} + +TextureAndGeometryPacket::TextureAndGeometryPacket(const wstring &textureName, PBYTE pbData, DWORD dwBytes) +{ + this->textureName = textureName; + + wstring skinValue = textureName.substr(7,textureName.size()); + skinValue = skinValue.substr(0,skinValue.find_first_of(L'.')); + std::wstringstream ss; + ss << std::dec << skinValue.c_str(); + ss >> this->dwSkinID; + this->dwSkinID = MAKE_SKIN_BITMASK(true, this->dwSkinID); + this->pbData = pbData; + this->dwTextureBytes = dwBytes; + this->dwBoxC = 0; + this->BoxDataA=NULL; + this->uiAnimOverrideBitmask=0; +} + +TextureAndGeometryPacket::TextureAndGeometryPacket(const wstring &textureName, PBYTE pbData, DWORD dwBytes, DLCSkinFile *pDLCSkinFile) +{ + this->textureName = textureName; + + wstring skinValue = textureName.substr(7,textureName.size()); + skinValue = skinValue.substr(0,skinValue.find_first_of(L'.')); + std::wstringstream ss; + ss << std::dec << skinValue.c_str(); + ss >> this->dwSkinID; + this->dwSkinID = MAKE_SKIN_BITMASK(true, this->dwSkinID); + + this->pbData = pbData; + this->dwTextureBytes = dwBytes; + this->uiAnimOverrideBitmask = pDLCSkinFile->getAnimOverrideBitmask(); + this->dwBoxC = pDLCSkinFile->getAdditionalBoxesCount(); + if(this->dwBoxC!=0) + { + this->BoxDataA= new SKIN_BOX [this->dwBoxC]; + vector *pSkinBoxes=pDLCSkinFile->getAdditionalBoxes(); + int iCount=0; + + for(AUTO_VAR(it, pSkinBoxes->begin());it != pSkinBoxes->end(); ++it) + { + SKIN_BOX *pSkinBox=*it; + this->BoxDataA[iCount++]=*pSkinBox; + } + } + else + { + this->BoxDataA=NULL; + } +} + +TextureAndGeometryPacket::TextureAndGeometryPacket(const wstring &textureName, PBYTE pbData, DWORD dwBytes,vector *pvSkinBoxes, unsigned int uiAnimOverrideBitmask) +{ + this->textureName = textureName; + + wstring skinValue = textureName.substr(7,textureName.size()); + skinValue = skinValue.substr(0,skinValue.find_first_of(L'.')); + std::wstringstream ss; + ss << std::dec << skinValue.c_str(); + ss >> this->dwSkinID; + this->dwSkinID = MAKE_SKIN_BITMASK(true, this->dwSkinID); + + this->pbData = pbData; + this->dwTextureBytes = dwBytes; + this->uiAnimOverrideBitmask = uiAnimOverrideBitmask; + if(pvSkinBoxes==NULL) + { + this->dwBoxC=0; + this->BoxDataA=NULL; + } + else + { + this->dwBoxC = (DWORD)pvSkinBoxes->size(); + this->BoxDataA= new SKIN_BOX [this->dwBoxC]; + int iCount=0; + + for(AUTO_VAR(it, pvSkinBoxes->begin());it != pvSkinBoxes->end(); ++it) + { + SKIN_BOX *pSkinBox=*it; + this->BoxDataA[iCount++]=*pSkinBox; + } + } + +} + +void TextureAndGeometryPacket::handle(PacketListener *listener) +{ + listener->handleTextureAndGeometry(shared_from_this()); +} + +void TextureAndGeometryPacket::read(DataInputStream *dis) //throws IOException +{ + textureName = dis->readUTF(); + dwSkinID = (DWORD)dis->readInt(); + dwTextureBytes = (DWORD)dis->readShort(); + + if(dwTextureBytes>0) + { + this->pbData= new BYTE [dwTextureBytes]; + + for(DWORD i=0;ipbData[i] = dis->readByte(); + } + } + uiAnimOverrideBitmask = dis->readInt(); + + dwBoxC = (DWORD)dis->readShort(); + + if(dwBoxC>0) + { + this->BoxDataA= new SKIN_BOX [dwBoxC]; + } + + for(DWORD i=0;iBoxDataA[i].ePart = (eBodyPart) dis->readShort(); + this->BoxDataA[i].fX = dis->readFloat(); + this->BoxDataA[i].fY = dis->readFloat(); + this->BoxDataA[i].fZ = dis->readFloat(); + this->BoxDataA[i].fH = dis->readFloat(); + this->BoxDataA[i].fW = dis->readFloat(); + this->BoxDataA[i].fD = dis->readFloat(); + this->BoxDataA[i].fU = dis->readFloat(); + this->BoxDataA[i].fV = dis->readFloat(); + } +} + +void TextureAndGeometryPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeUTF(textureName); + dos->writeInt(dwSkinID); + dos->writeShort((short)dwTextureBytes); + for(DWORD i=0;iwriteByte(this->pbData[i]); + } + dos->writeInt(uiAnimOverrideBitmask); + + dos->writeShort((short)dwBoxC); + for(DWORD i=0;iwriteShort((short)this->BoxDataA[i].ePart); + dos->writeFloat(this->BoxDataA[i].fX); + dos->writeFloat(this->BoxDataA[i].fY); + dos->writeFloat(this->BoxDataA[i].fZ); + dos->writeFloat(this->BoxDataA[i].fH); + dos->writeFloat(this->BoxDataA[i].fW); + dos->writeFloat(this->BoxDataA[i].fD); + dos->writeFloat(this->BoxDataA[i].fU); + dos->writeFloat(this->BoxDataA[i].fV); + } +} + +int TextureAndGeometryPacket::getEstimatedSize() +{ + return 4096+ +sizeof(int) + sizeof(float)*8*4; +} diff --git a/Minecraft.World/TextureAndGeometryPacket.h b/Minecraft.World/TextureAndGeometryPacket.h new file mode 100644 index 00000000..8577f03d --- /dev/null +++ b/Minecraft.World/TextureAndGeometryPacket.h @@ -0,0 +1,35 @@ +#pragma once +using namespace std; + +#include "Packet.h" +#include "..\Minecraft.Client\Model.h" +#include "..\Minecraft.Client\SkinBox.h" + +class DLCSkinFile; + +class TextureAndGeometryPacket : public Packet, public enable_shared_from_this +{ +public: + wstring textureName; + DWORD dwSkinID; + PBYTE pbData; + DWORD dwTextureBytes; + SKIN_BOX *BoxDataA; + DWORD dwBoxC; + unsigned int uiAnimOverrideBitmask; + + TextureAndGeometryPacket(); + ~TextureAndGeometryPacket(); + TextureAndGeometryPacket(const wstring &textureName, PBYTE pbData, DWORD dwBytes); + TextureAndGeometryPacket(const wstring &textureName, PBYTE pbData, DWORD dwBytes, DLCSkinFile *pDLCSkinFile); + TextureAndGeometryPacket(const wstring &textureName, PBYTE pbData, DWORD dwBytes, vector *pvSkinBoxes, unsigned int uiAnimOverrideBitmask); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new TextureAndGeometryPacket()); } + virtual int getId() { return 160; } +}; \ No newline at end of file diff --git a/Minecraft.World/TextureChangePacket.cpp b/Minecraft.World/TextureChangePacket.cpp new file mode 100644 index 00000000..2368136c --- /dev/null +++ b/Minecraft.World/TextureChangePacket.cpp @@ -0,0 +1,46 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.entity.h" +#include "PacketListener.h" +#include "TextureChangePacket.h" + + + +TextureChangePacket::TextureChangePacket() +{ + id = -1; + action = e_TextureChange_Skin; + path = L""; +} + +TextureChangePacket::TextureChangePacket(shared_ptr e, ETextureChangeType action, const wstring &path) +{ + id = e->entityId; + this->action = action; + this->path = path; +} + +void TextureChangePacket::read(DataInputStream *dis) //throws IOException +{ + id = dis->readInt(); + action = (ETextureChangeType)dis->readByte(); + path = dis->readUTF(); +} + +void TextureChangePacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(id); + dos->writeByte(action); + dos->writeUTF(path); +} + +void TextureChangePacket::handle(PacketListener *listener) +{ + listener->handleTextureChange(shared_from_this()); +} + +int TextureChangePacket::getEstimatedSize() +{ + return 5 + (int)path.size(); +} diff --git a/Minecraft.World/TextureChangePacket.h b/Minecraft.World/TextureChangePacket.h new file mode 100644 index 00000000..959fc4fe --- /dev/null +++ b/Minecraft.World/TextureChangePacket.h @@ -0,0 +1,30 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class TextureChangePacket : public Packet, public enable_shared_from_this +{ +public: + enum ETextureChangeType + { + e_TextureChange_Skin = 0, + e_TextureChange_Cape, + }; + + int id; + ETextureChangeType action; + wstring path; + + TextureChangePacket(); + TextureChangePacket(shared_ptr e, ETextureChangeType action, const wstring &path); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new TextureChangePacket()); } + virtual int getId() { return 157; } +}; \ No newline at end of file diff --git a/Minecraft.World/TexturePacket.cpp b/Minecraft.World/TexturePacket.cpp new file mode 100644 index 00000000..77dfdc38 --- /dev/null +++ b/Minecraft.World/TexturePacket.cpp @@ -0,0 +1,66 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "TexturePacket.h" + + + +TexturePacket::TexturePacket() +{ + this->textureName = L""; + this->dwBytes = 0; + this->pbData = NULL; +} + +TexturePacket::~TexturePacket() +{ + // can't free this - it's used elsewhere +// if(this->pbData!=NULL) +// { +// delete [] this->pbData; +// } +} + +TexturePacket::TexturePacket(const wstring &textureName, PBYTE pbData, DWORD dwBytes) +{ + this->textureName = textureName; + this->pbData = pbData; + this->dwBytes = dwBytes; +} + +void TexturePacket::handle(PacketListener *listener) +{ + listener->handleTexture(shared_from_this()); +} + +void TexturePacket::read(DataInputStream *dis) //throws IOException +{ + textureName = dis->readUTF(); + dwBytes = (DWORD)dis->readShort(); + + if(dwBytes>0) + { + this->pbData= new BYTE [dwBytes]; + + for(DWORD i=0;ipbData[i] = dis->readByte(); + } + } +} + +void TexturePacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeUTF(textureName); + dos->writeShort((short)dwBytes); + for(DWORD i=0;iwriteByte(this->pbData[i]); + } +} + +int TexturePacket::getEstimatedSize() +{ + return 4096; +} diff --git a/Minecraft.World/TexturePacket.h b/Minecraft.World/TexturePacket.h new file mode 100644 index 00000000..c2dce25f --- /dev/null +++ b/Minecraft.World/TexturePacket.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class TexturePacket : public Packet, public enable_shared_from_this +{ +public: + wstring textureName; + PBYTE pbData; + DWORD dwBytes; + + TexturePacket(); + ~TexturePacket(); + TexturePacket(const wstring &textureName, PBYTE pbData, DWORD dwBytes); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new TexturePacket()); } + virtual int getId() { return 154; } +}; \ No newline at end of file diff --git a/Minecraft.World/TheEndBiome.cpp b/Minecraft.World/TheEndBiome.cpp new file mode 100644 index 00000000..9b95d76a --- /dev/null +++ b/Minecraft.World/TheEndBiome.cpp @@ -0,0 +1,26 @@ +#include "stdafx.h" +#include "TheEndBiome.h" +#include "TheEndBiomeDecorator.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.level.tile.h" + +TheEndBiome::TheEndBiome(int id) : Biome(id) +{ + enemies.clear(); + friendlies.clear(); + friendlies_chicken.clear(); // 4J added + friendlies_wolf.clear(); // 4J added + waterFriendlies.clear(); + + enemies.push_back(new MobSpawnerData(eTYPE_ENDERMAN, 10, 4, 4)); + topMaterial = (byte) Tile::dirt_Id; + this->material = (byte) Tile::dirt_Id; + + decorator = new TheEndBiomeDecorator(this); +} + +// 4J Stu - Don't need override +//int TheEndBiome::getSkyColor(float temp) +//{ +// return 0x000000; +//} \ No newline at end of file diff --git a/Minecraft.World/TheEndBiome.h b/Minecraft.World/TheEndBiome.h new file mode 100644 index 00000000..b3698511 --- /dev/null +++ b/Minecraft.World/TheEndBiome.h @@ -0,0 +1,11 @@ +#pragma once +#include "Biome.h" + +class TheEndBiome : public Biome +{ +public: + TheEndBiome(int id); + + // 4J Stu - Don't need override + //virtual int getSkyColor(float temp); +}; \ No newline at end of file diff --git a/Minecraft.World/TheEndBiomeDecorator.cpp b/Minecraft.World/TheEndBiomeDecorator.cpp new file mode 100644 index 00000000..31823c80 --- /dev/null +++ b/Minecraft.World/TheEndBiomeDecorator.cpp @@ -0,0 +1,71 @@ +#include "stdafx.h" +#include "TheEndBiomeDecorator.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.entity.boss.enderdragon.h" + + +// Spike centre positions, calculated using +// for(int i=0;i<8;i++) +// { +// int x=40 * Mth::cos(2*(-PI+(PI/8)*i)); +// int z=40* Mth::sin(2*(-PI+(PI/8)*i)); +// } + + +TheEndBiomeDecorator::SPIKE TheEndBiomeDecorator::SpikeValA[8]= +{ + // The chunk that the spike is in has to be the smallest x and z that any part of it is in + // a chunk(x,z) will only be post-processed when the chunks (x+1,z), (x,z+1) and (x+1,z+1) are also loaded + + // first two values are the smallest x and z of the chunk that the feature is in - so the centre point minus the radius + { 32, -16, 40, 0, 2 },// smallest block - 38,-2 + { 16, 16, 28, 28, 2 },// smallest block - 26,26 + { -16, 32, 0, 40, 2 },// smallest block - -2,38 + { -32, 16, -28, 28, 3 },// smallest block - -31,26 + { -48, -16, -40, 0, 3 },// smallest block - -43,-3 + { -32, -32, -28, -28, 3 },// smallest block - -31,-31 + { -16, -48, 0, -40, 4 },// smallest block - -4,-44 + { 16, -32, 28, -28, 4 },// smallest block - 24,-32 +}; + + +TheEndBiomeDecorator::TheEndBiomeDecorator(Biome *biome) : BiomeDecorator(biome) +{ + spikeFeature = new SpikeFeature(Tile::whiteStone_Id); + endPodiumFeature = new EndPodiumFeature(Tile::whiteStone_Id); +} + +void TheEndBiomeDecorator::decorate() +{ + decorateOres(); + + // this will only set the y to the top y of the chunks already processed... + int y = level->getTopSolidBlock(xo+8, zo+8); + if(y>level->GetHighestY()) level->SetHighestY(y); + + // 4J-PB - editing to place 8 spikes in a circle, with increasing height + + // are we within the chunk with a spike? + for(int i=0;i<8;i++) + { + if((xo == SpikeValA[i].iChunkX) && (zo == SpikeValA[i].iChunkZ)) + { + // in the right chunk + spikeFeature->placeWithIndex(level, random, SpikeValA[i].x, level->GetHighestY(), SpikeValA[i].z,i,SpikeValA[i].radius); + } + } + if (xo == 0 && zo == 0) + { + shared_ptr enderDragon = shared_ptr(new EnderDragon(level)); + enderDragon->moveTo(0, 128, 0, random->nextFloat() * 360, 0); + level->addEntity(enderDragon); + } + + // end podium radius is 4, position is 0,0, so chunk needs to be the -16,-16 one since this guarantees that all chunks required for the podium are loaded + if (xo == -16 && zo == -16) + { + endPodiumFeature->place(level, random, 0, Level::genDepth / 2, 0); + } +} \ No newline at end of file diff --git a/Minecraft.World/TheEndBiomeDecorator.h b/Minecraft.World/TheEndBiomeDecorator.h new file mode 100644 index 00000000..b9c790b3 --- /dev/null +++ b/Minecraft.World/TheEndBiomeDecorator.h @@ -0,0 +1,24 @@ +#include "BiomeDecorator.h" + +class TheEndBiomeDecorator : public BiomeDecorator +{ +public: + typedef struct + { + int iChunkX; + int iChunkZ; + int x; + int z; + int radius; + } + SPIKE; + + TheEndBiomeDecorator(Biome *biome); +protected: + Feature *spikeFeature; + Feature *endPodiumFeature; + virtual void decorate(); + + + static SPIKE SpikeValA[8]; +}; \ No newline at end of file diff --git a/Minecraft.World/TheEndDimension.cpp b/Minecraft.World/TheEndDimension.cpp new file mode 100644 index 00000000..ccc8641b --- /dev/null +++ b/Minecraft.World/TheEndDimension.cpp @@ -0,0 +1,91 @@ +#include "stdafx.h" +#include "TheEndDimension.h" +#include "FixedBiomeSource.h" +#include "net.minecraft.world.level.levelgen.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "..\Minecraft.Client\Common\Colours\ColourTable.h" + +void TheEndDimension::init() +{ + biomeSource = new FixedBiomeSource(Biome::sky, 0.5f, 0); + id = 1; + hasCeiling = true; +} + +ChunkSource *TheEndDimension::createRandomLevelSource() const +{ + return new TheEndLevelRandomLevelSource(level, level->getSeed()); +} + +float TheEndDimension::getTimeOfDay(__int64 time, float a) const +{ + return 0.0f; +} + +float *TheEndDimension::getSunriseColor(float td, float a) +{ + return NULL; +} + +Vec3 *TheEndDimension::getFogColor(float td, float a) const +{ + int fogColor = Minecraft::GetInstance()->getColourTable()->getColor( eMinecraftColour_End_Fog_Colour ); //0xa080a0; + float br = Mth::cos(td * PI * 2) * 2 + 0.5f; + if (br < 0.0f) br = 0.0f; + if (br > 1.0f) br = 1.0f; + + float r = ((fogColor >> 16) & 0xff) / 255.0f; + float g = ((fogColor >> 8) & 0xff) / 255.0f; + float b = ((fogColor) & 0xff) / 255.0f; + r *= br * 0.0f + 0.15f; + g *= br * 0.0f + 0.15f; + b *= br * 0.0f + 0.15f; + + return Vec3::newTemp(r, g, b); +} + +bool TheEndDimension::hasGround() +{ + return false; +} + +bool TheEndDimension::mayRespawn() const +{ + return false; +} + +bool TheEndDimension::isNaturalDimension() +{ + return false; +} + +float TheEndDimension::getCloudHeight() +{ + return 8; +} + +bool TheEndDimension::isValidSpawn(int x, int z) const +{ + int topTile = level->getTopTile(x, z); + + if (topTile == 0) return false; + + return Tile::tiles[topTile]->material->blocksMotion(); +} + +Pos *TheEndDimension::getSpawnPos() +{ + return new Pos(100, 50, 0); +} + +bool TheEndDimension::isFoggyAt(int x, int z) +{ + return true; +} + +int TheEndDimension::getSpawnYPosition() +{ + return 50; +} \ No newline at end of file diff --git a/Minecraft.World/TheEndDimension.h b/Minecraft.World/TheEndDimension.h new file mode 100644 index 00000000..95bd6980 --- /dev/null +++ b/Minecraft.World/TheEndDimension.h @@ -0,0 +1,20 @@ +#pragma once +#include "Dimension.h" + +class TheEndDimension : public Dimension +{ +public: + virtual void init(); + virtual ChunkSource *createRandomLevelSource() const; + virtual float getTimeOfDay(__int64 time, float a) const; + virtual float *getSunriseColor(float td, float a); + virtual Vec3 *getFogColor(float td, float a) const; + virtual bool hasGround(); + virtual bool mayRespawn() const; + virtual bool isNaturalDimension(); + virtual float getCloudHeight(); + virtual bool isValidSpawn(int x, int z) const; + virtual Pos *getSpawnPos(); + virtual int getSpawnYPosition(); + virtual bool isFoggyAt(int x, int z); +}; diff --git a/Minecraft.World/TheEndLevelRandomLevelSource.cpp b/Minecraft.World/TheEndLevelRandomLevelSource.cpp new file mode 100644 index 00000000..46f3dbab --- /dev/null +++ b/Minecraft.World/TheEndLevelRandomLevelSource.cpp @@ -0,0 +1,421 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.levelgen.h" +#include "net.minecraft.world.level.levelgen.feature.h" +#include "net.minecraft.world.level.levelgen.structure.h" +#include "net.minecraft.world.level.levelgen.synth.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.storage.h" +#include "TheEndLevelRandomLevelSource.h" + +TheEndLevelRandomLevelSource::TheEndLevelRandomLevelSource(Level *level, __int64 seed) +{ + m_XZSize = END_LEVEL_MIN_WIDTH; + + this->level = level; + + random = new Random(seed); + pprandom = new Random(seed); // 4J added + lperlinNoise1 = new PerlinNoise(random, 16); + lperlinNoise2 = new PerlinNoise(random, 16); + perlinNoise1 = new PerlinNoise(random, 8); + + scaleNoise = new PerlinNoise(random, 10); + depthNoise = new PerlinNoise(random, 16); +} + +TheEndLevelRandomLevelSource::~TheEndLevelRandomLevelSource() +{ + delete random; + delete pprandom; + delete lperlinNoise1; + delete lperlinNoise2; + delete perlinNoise1; + delete scaleNoise; + delete depthNoise; +} + +void TheEndLevelRandomLevelSource::prepareHeights(int xOffs, int zOffs, byteArray blocks, BiomeArray biomes) +{ + doubleArray buffer; // 4J - used to be declared with class level scope but tidying up for thread safety reasons + + int xChunks = 16 / CHUNK_WIDTH; + + int xSize = xChunks + 1; + int ySize = Level::genDepth / CHUNK_HEIGHT + 1; + int zSize = xChunks + 1; + buffer = getHeights(buffer, xOffs * xChunks, 0, zOffs * xChunks, xSize, ySize, zSize); + + for (int xc = 0; xc < xChunks; xc++) + { + for (int zc = 0; zc < xChunks; zc++) + { + for (int yc = 0; yc < Level::genDepth / CHUNK_HEIGHT; yc++) + { + double yStep = 1 / (double) CHUNK_HEIGHT; + double s0 = buffer[((xc + 0) * zSize + (zc + 0)) * ySize + (yc + 0)]; + double s1 = buffer[((xc + 0) * zSize + (zc + 1)) * ySize + (yc + 0)]; + double s2 = buffer[((xc + 1) * zSize + (zc + 0)) * ySize + (yc + 0)]; + double s3 = buffer[((xc + 1) * zSize + (zc + 1)) * ySize + (yc + 0)]; + + double s0a = (buffer[((xc + 0) * zSize + (zc + 0)) * ySize + (yc + 1)] - s0) * yStep; + double s1a = (buffer[((xc + 0) * zSize + (zc + 1)) * ySize + (yc + 1)] - s1) * yStep; + double s2a = (buffer[((xc + 1) * zSize + (zc + 0)) * ySize + (yc + 1)] - s2) * yStep; + double s3a = (buffer[((xc + 1) * zSize + (zc + 1)) * ySize + (yc + 1)] - s3) * yStep; + + for (int y = 0; y < CHUNK_HEIGHT; y++) + { + double xStep = 1 / (double) CHUNK_WIDTH; + + double _s0 = s0; + double _s1 = s1; + double _s0a = (s2 - s0) * xStep; + double _s1a = (s3 - s1) * xStep; + + for (int x = 0; x < CHUNK_WIDTH; x++) + { + int offs = (x + xc * CHUNK_WIDTH) << Level::genDepthBitsPlusFour | (0 + zc * CHUNK_WIDTH) << Level::genDepthBits | (yc * CHUNK_HEIGHT + y); + int step = 1 << Level::genDepthBits; + double zStep = 1 / (double) CHUNK_WIDTH; + + double val = _s0; + double vala = (_s1 - _s0) * zStep; + for (int z = 0; z < CHUNK_WIDTH; z++) + { + int tileId = 0; + if (val > 0) + { + tileId = Tile::whiteStone_Id; + } else { + } + + blocks[offs] = (byte) tileId; + offs += step; + val += vala; + } + _s0 += _s0a; + _s1 += _s1a; + } + + s0 += s0a; + s1 += s1a; + s2 += s2a; + s3 += s3a; + } + } + } + } + delete [] buffer.data; + +} + +void TheEndLevelRandomLevelSource::buildSurfaces(int xOffs, int zOffs, byteArray blocks, BiomeArray biomes) +{ + for (int x = 0; x < 16; x++) + { + for (int z = 0; z < 16; z++) + { + int runDepth = 1; + int run = -1; + + byte top = (byte) Tile::whiteStone_Id; + byte material = (byte) Tile::whiteStone_Id; + + for (int y = Level::genDepthMinusOne; y >= 0; y--) + { + int offs = (z * 16 + x) * Level::genDepth + y; + + int old = blocks[offs]; + + if (old == 0) + { + run = -1; + } + else if (old == Tile::rock_Id) + { + if (run == -1) + { + if (runDepth <= 0) + { + top = 0; + material = (byte) Tile::whiteStone_Id; + } + + run = runDepth; + if (y >= 0) blocks[offs] = top; + else blocks[offs] = material; + } + else if (run > 0) + { + run--; + blocks[offs] = material; + } + } + } + } + } +} + +LevelChunk *TheEndLevelRandomLevelSource::create(int x, int z) +{ + return getChunk(x, z); +} + +LevelChunk *TheEndLevelRandomLevelSource::getChunk(int xOffs, int zOffs) +{ + random->setSeed(xOffs * 341873128712l + zOffs * 132897987541l); + + BiomeArray biomes; + // 4J - now allocating this with a physical alloc & bypassing general memory management so that it will get cleanly freed + unsigned int blocksSize = Level::genDepth * 16 * 16; + byte *tileData = (byte *)XPhysicalAlloc(blocksSize, MAXULONG_PTR, 4096, PAGE_READWRITE); + XMemSet128(tileData,0,blocksSize); + byteArray blocks = byteArray(tileData,blocksSize); +// byteArray blocks = byteArray(16 * level->depth * 16); + +// LevelChunk *levelChunk = new LevelChunk(level, blocks, xOffs, zOffs); // 4J moved below + level->getBiomeSource()->getBiomeBlock(biomes, xOffs * 16, zOffs * 16, 16, 16, true); + + prepareHeights(xOffs, zOffs, blocks, biomes); + buildSurfaces(xOffs, zOffs, blocks, biomes); + + // 4J - this now creates compressed block data from the blocks array passed in, so moved it until after the blocks are actually finalised. We also + // now need to free the passed in blocks as the LevelChunk doesn't use the passed in allocation anymore. + LevelChunk *levelChunk = new LevelChunk(level, blocks, xOffs, zOffs); + XPhysicalFree(tileData); + + levelChunk->recalcHeightmap(); + + //delete blocks.data; // Don't delete the blocks as the array data is actually owned by the chunk now + delete biomes.data; + + return levelChunk; +} + +doubleArray TheEndLevelRandomLevelSource::getHeights(doubleArray buffer, int x, int y, int z, int xSize, int ySize, int zSize) +{ + if (buffer.data == NULL) + { + buffer = doubleArray(xSize * ySize * zSize); + } + + double s = 1 * 684.412; + double hs = 1 * 684.412; + + doubleArray pnr, ar, br, sr, dr, fi, fis; // 4J - used to be declared with class level scope but moved here for thread safety + + sr = scaleNoise->getRegion(sr, x, z, xSize, zSize, 1.121, 1.121, 0.5); + dr = depthNoise->getRegion(dr, x, z, xSize, zSize, 200.0, 200.0, 0.5); + + s *= 2; + + pnr = perlinNoise1->getRegion(pnr, x, y, z, xSize, ySize, zSize, s / 80.0, hs / 160.0, s / 80.0); + ar = lperlinNoise1->getRegion(ar, x, y, z, xSize, ySize, zSize, s, hs, s); + br = lperlinNoise2->getRegion(br, x, y, z, xSize, ySize, zSize, s, hs, s); + + int p = 0; + int pp = 0; + + for (int xx = 0; xx < xSize; xx++) + { + for (int zz = 0; zz < zSize; zz++) + { + double scale = ((sr[pp] + 256.0) / 512); + if (scale > 1) scale = 1; + + + double depth = (dr[pp] / 8000.0); + if (depth < 0) depth = -depth * 0.3; + depth = depth * 3.0 - 2.0; + + float xd = ((xx + x) - 0) / 1.0f; + float zd = ((zz + z) - 0) / 1.0f; + float doffs = 100 - sqrt(xd * xd + zd * zd) * 8; + if (doffs > 80) doffs = 80; + if (doffs < -100) doffs = -100; + if (depth > 1) depth = 1; + depth = depth / 8; + depth = 0; + + if (scale < 0) scale = 0; + scale = (scale) + 0.5; + depth = depth * ySize / 16; + + pp++; + + double yCenter = ySize / 2.0; + + + for (int yy = 0; yy < ySize; yy++) + { + double val = 0; + double yOffs = (yy - (yCenter)) * 8 / scale; + + if (yOffs < 0) yOffs *= -1; + + double bb = ar[p] / 512; + double cc = br[p] / 512; + + double v = (pnr[p] / 10 + 1) / 2; + if (v < 0) val = bb; + else if (v > 1) val = cc; + else val = bb + (cc - bb) * v; + val -= 8; + val += doffs; + + int r = 2; + if (yy > ySize / 2 - r) + { + double slide = (yy - (ySize / 2 - r)) / (64.0f); + if (slide < 0) slide = 0; + if (slide > 1) slide = 1; + val = val * (1 - slide) + -3000 * slide; + } + r = 8; + if (yy < r) + { + double slide = (r - yy) / (r - 1.0f); + val = val * (1 - slide) + -30 * slide; + } + + + buffer[p] = val; + p++; + } + } + } + + delete [] pnr.data; + delete [] ar.data; + delete [] br.data; + delete [] sr.data; + delete [] dr.data; + delete [] fi.data; + delete [] fis.data; + + return buffer; + +} + +bool TheEndLevelRandomLevelSource::hasChunk(int x, int y) +{ + return true; +} + +void TheEndLevelRandomLevelSource::calcWaterDepths(ChunkSource *parent, int xt, int zt) +{ + int xo = xt * 16; + int zo = zt * 16; + for (int x = 0; x < 16; x++) + { + int y = level->getSeaLevel(); + for (int z = 0; z < 16; z++) + { + int xp = xo + x + 7; + int zp = zo + z + 7; + int h = level->getHeightmap(xp, zp); + if (h <= 0) + { + if (level->getHeightmap(xp - 1, zp) > 0 || level->getHeightmap(xp + 1, zp) > 0 || level->getHeightmap(xp, zp - 1) > 0 || level->getHeightmap(xp, zp + 1) > 0) + { + bool hadWater = false; + if (hadWater || (level->getTile(xp - 1, y, zp) == Tile::calmWater_Id && level->getData(xp - 1, y, zp) < 7)) hadWater = true; + if (hadWater || (level->getTile(xp + 1, y, zp) == Tile::calmWater_Id && level->getData(xp + 1, y, zp) < 7)) hadWater = true; + if (hadWater || (level->getTile(xp, y, zp - 1) == Tile::calmWater_Id && level->getData(xp, y, zp - 1) < 7)) hadWater = true; + if (hadWater || (level->getTile(xp, y, zp + 1) == Tile::calmWater_Id && level->getData(xp, y, zp + 1) < 7)) hadWater = true; + if (hadWater) + { + for (int x2 = -5; x2 <= 5; x2++) + { + for (int z2 = -5; z2 <= 5; z2++) + { + int d = (x2 > 0 ? x2 : -x2) + (z2 > 0 ? z2 : -z2); + + if (d <= 5) + { + d = 6 - d; + if (level->getTile(xp + x2, y, zp + z2) == Tile::calmWater_Id) + { + int od = level->getData(xp + x2, y, zp + z2); + if (od < 7 && od < d) + { + level->setData(xp + x2, y, zp + z2, d); + } + } + } + } + } + if (hadWater) + { + level->setTileAndDataNoUpdate(xp, y, zp, Tile::calmWater_Id, 7); + for (int y2 = 0; y2 < y; y2++) + { + level->setTileAndDataNoUpdate(xp, y2, zp, Tile::calmWater_Id, 8); + } + } + } + } + } + } + } + +} + +void TheEndLevelRandomLevelSource::postProcess(ChunkSource *parent, int xt, int zt) +{ + HeavyTile::instaFall = true; + int xo = xt * 16; + int zo = zt * 16; + + // 4J - added. The original java didn't do any setting of the random seed here, and passes the level random to the biome decorator. + // We'll be running our postProcess in parallel with getChunk etc. so we need to use a separate random - have used the same initialisation code as + // used in RandomLevelSource::postProcess to make sure this random value is consistent for each world generation. + pprandom->setSeed(level->getSeed()); + __int64 xScale = pprandom->nextLong() / 2 * 2 + 1; + __int64 zScale = pprandom->nextLong() / 2 * 2 + 1; + pprandom->setSeed(((xt * xScale) + (zt * zScale)) ^ level->getSeed()); + + Biome *biome = level->getBiome(xo + 16, zo + 16); + biome->decorate(level, pprandom, xo, zo); // 4J - passing pprandom rather than level->random here to make this consistent with our parallel world generation + + HeavyTile::instaFall = false; + + app.processSchematics(parent->getChunk(xt,zt)); +} + +bool TheEndLevelRandomLevelSource::save(bool force, ProgressListener *progressListener) +{ + return true; +} + +bool TheEndLevelRandomLevelSource::tick() +{ + return false; +} + +bool TheEndLevelRandomLevelSource::shouldSave() +{ + return true; +} + +wstring TheEndLevelRandomLevelSource::gatherStats() +{ + return L"RandomLevelSource"; +} + +vector *TheEndLevelRandomLevelSource::getMobsAt(MobCategory *mobCategory, int x, int y, int z) +{ + Biome *biome = level->getBiome(x, z); + if (biome == NULL) + { + return NULL; + } + return biome->getMobs(mobCategory); +} + +TilePos *TheEndLevelRandomLevelSource::findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z) +{ + return NULL; +} + diff --git a/Minecraft.World/TheEndLevelRandomLevelSource.h b/Minecraft.World/TheEndLevelRandomLevelSource.h new file mode 100644 index 00000000..2dc7f131 --- /dev/null +++ b/Minecraft.World/TheEndLevelRandomLevelSource.h @@ -0,0 +1,60 @@ +#pragma once + +#include "ChunkSource.h" +class PerlinNoise; + +class TheEndLevelRandomLevelSource : public ChunkSource +{ +public: + static const double SNOW_CUTOFF; + static const double SNOW_SCALE; + static const bool FLOATING_ISLANDS; + + static const int CHUNK_HEIGHT = 4; + static const int CHUNK_WIDTH = 8; +private: + Random *random; + Random *pprandom; + +private: + PerlinNoise *lperlinNoise1; + PerlinNoise *lperlinNoise2; + PerlinNoise *perlinNoise1; +public: + PerlinNoise *scaleNoise; + PerlinNoise *depthNoise; + PerlinNoise *forestNoise; + + +private: + Level *level; + +public: + TheEndLevelRandomLevelSource(Level *level, __int64 seed); + ~TheEndLevelRandomLevelSource(); + + void prepareHeights(int xOffs, int zOffs, byteArray blocks, BiomeArray biomes); + void buildSurfaces(int xOffs, int zOffs, byteArray blocks, BiomeArray biomes); + +public: + virtual LevelChunk *create(int x, int z); + virtual LevelChunk *getChunk(int xOffs, int zOffs); + +private: + doubleArray getHeights(doubleArray buffer, int x, int y, int z, int xSize, int ySize, int zSize); + +public: + virtual bool hasChunk(int x, int y); +private: + void calcWaterDepths(ChunkSource *parent, int xt, int zt); +public: + virtual void postProcess(ChunkSource *parent, int xt, int zt); + virtual bool save(bool force, ProgressListener *progressListener); + virtual bool tick(); + virtual bool shouldSave(); + virtual wstring gatherStats(); + +public: + virtual vector *getMobsAt(MobCategory *mobCategory, int x, int y, int z); + virtual TilePos *findNearestMapFeature(Level *level, const wstring& featureName, int x, int y, int z); +}; diff --git a/Minecraft.World/TheEndPortal.cpp b/Minecraft.World/TheEndPortal.cpp new file mode 100644 index 00000000..19d95b1f --- /dev/null +++ b/Minecraft.World/TheEndPortal.cpp @@ -0,0 +1,126 @@ +#include "stdafx.h" +#include "TheEndPortal.h" +#include "TheEndPortalTileEntity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.level.storage.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.h" + +DWORD TheEndPortal::tlsIdx = TlsAlloc(); + +// 4J - allowAnywhere is a static in java, implementing as TLS here to make thread safe +bool TheEndPortal::allowAnywhere() +{ + return (TlsGetValue(tlsIdx) != NULL); +} + +void TheEndPortal::allowAnywhere(bool set) +{ + TlsSetValue(tlsIdx,(LPVOID)(set?1:0)); +} + +TheEndPortal::TheEndPortal(int id, Material *material) : EntityTile(id, material, isSolidRender()) +{ + this->setLightEmission(1.0f); +} + +shared_ptr TheEndPortal::newTileEntity(Level *level) +{ + return shared_ptr(new TheEndPortalTileEntity()); +} + +void TheEndPortal::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + float r = 1 / 16.0f; + this->setShape(0, 0, 0, 1, r, 1); +} + +bool TheEndPortal::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + if (face != 0) return false; + return EntityTile::shouldRenderFace(level, x, y, z, face); +} + +void TheEndPortal::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source) +{ +} + +bool TheEndPortal::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool TheEndPortal::isCubeShaped() +{ + return false; +} + +int TheEndPortal::getResourceCount(Random *random) +{ + return 0; +} + +void TheEndPortal::entityInside(Level *level, int x, int y, int z, shared_ptr entity) +{ + if (entity->riding == NULL && entity->rider.lock() == NULL) + { + if (dynamic_pointer_cast(entity) != NULL) + { + if (!level->isClientSide) + { + // 4J Stu - Update the level data position so that the stronghold portal can be shown on the maps + int x,z; + x = z = 0; + if(level->dimension == 0 && !level->getLevelData()->getHasStrongholdEndPortal() && app.GetTerrainFeaturePosition( eTerrainFeature_StrongholdEndPortal, &x, &z) ) + { + level->getLevelData()->setXStrongholdEndPortal(x); + level->getLevelData()->setZStrongholdEndPortal(z); + level->getLevelData()->setHasStrongholdEndPortal(); + } + + (dynamic_pointer_cast(entity))->changeDimension(1); + } + } + } +} + +void TheEndPortal::animateTick(Level *level, int xt, int yt, int zt, Random *random) +{ + double x = xt + random->nextFloat(); + double y = yt + 0.8f; + double z = zt + random->nextFloat(); + double xa = 0; + double ya = 0; + double za = 0; + + level->addParticle(eParticleType_endportal, x, y, z, xa, ya, za); +} + +int TheEndPortal::getRenderShape() +{ + return SHAPE_INVISIBLE; +} + +void TheEndPortal::onPlace(Level *level, int x, int y, int z) +{ + if (allowAnywhere()) return; + + if (level->dimension->id != 0) + { + level->setTile(x, y, z, 0); + return; + } +} + +int TheEndPortal::cloneTileId(Level *level, int x, int y, int z) +{ + return 0; +} + +void TheEndPortal::registerIcons(IconRegister *iconRegister) +{ + // don't register null, because of particles + icon = iconRegister->registerIcon(L"portal"); +} diff --git a/Minecraft.World/TheEndPortal.h b/Minecraft.World/TheEndPortal.h new file mode 100644 index 00000000..31cddb2d --- /dev/null +++ b/Minecraft.World/TheEndPortal.h @@ -0,0 +1,27 @@ +#pragma once +#include "EntityTile.h" + +class TheEndPortal : public EntityTile +{ +public: + static DWORD tlsIdx; + // 4J - was just a static but implemented with TLS for our version + static bool allowAnywhere(); + static void allowAnywhere(bool set); + + TheEndPortal(int id, Material *material); + + virtual shared_ptr newTileEntity(Level *level); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + virtual void addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual int getResourceCount(Random *random); + virtual void entityInside(Level *level, int x, int y, int z, shared_ptr entity); + virtual void animateTick(Level *level, int xt, int yt, int zt, Random *random); + virtual int getRenderShape(); + virtual void onPlace(Level *level, int x, int y, int z); + virtual int cloneTileId(Level *level, int x, int y, int z); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/TheEndPortalFrameTile.cpp b/Minecraft.World/TheEndPortalFrameTile.cpp new file mode 100644 index 00000000..cab8319f --- /dev/null +++ b/Minecraft.World/TheEndPortalFrameTile.cpp @@ -0,0 +1,83 @@ +#include "stdafx.h" +#include "TheEndPortalFrameTile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" +#include "Facing.h" + +const wstring TheEndPortalFrameTile::TEXTURE_EYE = L"endframe_eye"; + +TheEndPortalFrameTile::TheEndPortalFrameTile(int id) : Tile(id, Material::glass, isSolidRender() ) +{ + iconTop = NULL; + iconEye = NULL; +} + +Icon *TheEndPortalFrameTile::getTexture(int face, int data) +{ + if (face == Facing::UP) + { + return iconTop; + } + if (face == Facing::DOWN) + { + return Tile::whiteStone->getTexture(face); + } + return icon; +} + +void TheEndPortalFrameTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"endframe_side"); + iconTop = iconRegister->registerIcon(L"endframe_top"); + iconEye = iconRegister->registerIcon(L"endframe_eye"); +} + +Icon *TheEndPortalFrameTile::getEye() +{ + return iconEye; +} + +bool TheEndPortalFrameTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +int TheEndPortalFrameTile::getRenderShape() +{ + return SHAPE_PORTAL_FRAME; +} + +void TheEndPortalFrameTile::updateDefaultShape() +{ + setShape(0, 0, 0, 1, 13.0f / 16.0f, 1); +} + +void TheEndPortalFrameTile::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source) +{ + setShape(0, 0, 0, 1, 13.0f / 16.0f, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + + int data = level->getData(x, y, z); + if (hasEye(data)) + { + setShape(5.0f / 16.0f, 13.0f / 16.0f, 5.0f / 16.0f, 11.0f / 16.0f, 1, 11.0f / 16.0f); + Tile::addAABBs(level, x, y, z, box, boxes, source); + } + updateDefaultShape(); +} + +bool TheEndPortalFrameTile::hasEye(int data) +{ + return (data & EYE_BIT) != 0; +} + +int TheEndPortalFrameTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return 0; +} + +void TheEndPortalFrameTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int dir = (((Mth::floor(by->yRot * 4 / (360) + 0.5)) & 3) + 2) % 4; + level->setData(x, y, z, dir); +} diff --git a/Minecraft.World/TheEndPortalFrameTile.h b/Minecraft.World/TheEndPortalFrameTile.h new file mode 100644 index 00000000..b48d3c09 --- /dev/null +++ b/Minecraft.World/TheEndPortalFrameTile.h @@ -0,0 +1,26 @@ +#pragma once +#include "Tile.h" + +class TheEndPortalFrameTile : public Tile +{ +public: + static const int EYE_BIT = 4; + static const wstring TEXTURE_EYE; + +private: + Icon *iconTop; + Icon *iconEye; + +public: + TheEndPortalFrameTile(int id); + virtual Icon *getTexture(int face, int data); + void registerIcons(IconRegister *iconRegister); + Icon *getEye(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual int getRenderShape(); + virtual void updateDefaultShape(); + virtual void addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source); + static bool hasEye(int data); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); +}; \ No newline at end of file diff --git a/Minecraft.World/TheEndPortalTileEntity.cpp b/Minecraft.World/TheEndPortalTileEntity.cpp new file mode 100644 index 00000000..fa9b3544 --- /dev/null +++ b/Minecraft.World/TheEndPortalTileEntity.cpp @@ -0,0 +1,10 @@ +#include "stdafx.h" +#include "TheEndPortalTileEntity.h" + +// 4J Added +shared_ptr TheEndPortalTileEntity::clone() +{ + shared_ptr result = shared_ptr( new TheEndPortalTileEntity() ); + TileEntity::clone(result); + return result; +} \ No newline at end of file diff --git a/Minecraft.World/TheEndPortalTileEntity.h b/Minecraft.World/TheEndPortalTileEntity.h new file mode 100644 index 00000000..29752d44 --- /dev/null +++ b/Minecraft.World/TheEndPortalTileEntity.h @@ -0,0 +1,12 @@ +#pragma once +#include "TileEntity.h" + +class TheEndPortalTileEntity : public TileEntity +{ +public: + eINSTANCEOF GetType() { return eTYPE_THEENDPORTALTILEENTITY; } + static TileEntity *create() { return new TheEndPortalTileEntity(); } + + // 4J Added + shared_ptr clone(); +}; \ No newline at end of file diff --git a/Minecraft.World/ThinFenceTile.cpp b/Minecraft.World/ThinFenceTile.cpp new file mode 100644 index 00000000..bd1f1687 --- /dev/null +++ b/Minecraft.World/ThinFenceTile.cpp @@ -0,0 +1,154 @@ +#include "stdafx.h" +#include "ThinFenceTile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" + +ThinFenceTile::ThinFenceTile(int id, const wstring &tex, const wstring &edgeTex, Material *material, bool dropsResources) : Tile(id, material,isSolidRender()) +{ + iconSide = NULL; + edgeTexture = edgeTex; + this->dropsResources = dropsResources; + this->texture = tex; +} + +int ThinFenceTile::getResource(int data, Random *random, int playerBonusLevel) +{ + if (!dropsResources) + { + return 0; + } + return Tile::getResource(data, random, playerBonusLevel); +} + +bool ThinFenceTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool ThinFenceTile::isCubeShaped() +{ + return false; +} + +int ThinFenceTile::getRenderShape() +{ + return Tile::SHAPE_IRON_FENCE; +} + +bool ThinFenceTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + int id = level->getTile(x, y, z); + if (id == this->id) return false; + return Tile::shouldRenderFace(level, x, y, z, face); +} + +void ThinFenceTile::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source) +{ + bool n = attachsTo(level->getTile(x, y, z - 1)); + bool s = attachsTo(level->getTile(x, y, z + 1)); + bool w = attachsTo(level->getTile(x - 1, y, z)); + bool e = attachsTo(level->getTile(x + 1, y, z)); + + if ((w && e) || (!w && !e && !n && !s)) + { + setShape(0, 0, 7.0f / 16.0f, 1, 1, 9.0f / 16.0f); + Tile::addAABBs(level, x, y, z, box, boxes, source); + } + else if (w && !e) + { + setShape(0, 0, 7.0f / 16.0f, .5f, 1, 9.0f / 16.0f); + Tile::addAABBs(level, x, y, z, box, boxes, source); + } + else if (!w && e) + { + setShape(.5f, 0, 7.0f / 16.0f, 1, 1, 9.0f / 16.0f); + Tile::addAABBs(level, x, y, z, box, boxes, source); + } + if ((n && s) || (!w && !e && !n && !s)) + { + setShape(7.0f / 16.0f, 0, 0, 9.0f / 16.0f, 1, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + } + else if (n && !s) + { + setShape(7.0f / 16.0f, 0, 0, 9.0f / 16.0f, 1, .5f); + Tile::addAABBs(level, x, y, z, box, boxes, source); + } + else if (!n && s) + { + setShape(7.0f / 16.0f, 0, .5f, 9.0f / 16.0f, 1, 1); + Tile::addAABBs(level, x, y, z, box, boxes, source); + } +} + +void ThinFenceTile::updateDefaultShape() +{ + setShape(0, 0, 0, 1, 1, 1); +} + +void ThinFenceTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + float minX = 7.0f / 16.0f; + float maxX = 9.0f / 16.0f; + float minZ = 7.0f / 16.0f; + float maxZ = 9.0f / 16.0f; + + bool n = attachsTo(level->getTile(x, y, z - 1)); + bool s = attachsTo(level->getTile(x, y, z + 1)); + bool w = attachsTo(level->getTile(x - 1, y, z)); + bool e = attachsTo(level->getTile(x + 1, y, z)); + + if ((w && e) || (!w && !e && !n && !s)) + { + minX = 0; + maxX = 1; + } + else if (w && !e) + { + minX = 0; + } + else if (!w && e) + { + maxX = 1; + } + if ((n && s) || (!w && !e && !n && !s)) + { + minZ = 0; + maxZ = 1; + } + else if (n && !s) + { + minZ = 0; + } + else if (!n && s) + { + maxZ = 1; + } + setShape(minX, 0, minZ, maxX, 1, maxZ); +} + +Icon *ThinFenceTile::getEdgeTexture() +{ + return iconSide; +} + +bool ThinFenceTile::attachsTo(int tile) +{ + return Tile::solid[tile] || tile == id || tile == Tile::glass_Id; +} + +bool ThinFenceTile::isSilkTouchable() +{ + return true; +} + +shared_ptr ThinFenceTile::getSilkTouchItemInstance(int data) +{ + return shared_ptr(new ItemInstance(id, 1, data)); +} + +void ThinFenceTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(texture); + iconSide = iconRegister->registerIcon(edgeTexture); +} diff --git a/Minecraft.World/ThinFenceTile.h b/Minecraft.World/ThinFenceTile.h new file mode 100644 index 00000000..ca3c501f --- /dev/null +++ b/Minecraft.World/ThinFenceTile.h @@ -0,0 +1,32 @@ +#pragma once +#include "Tile.h" +#include "Definitions.h" + +class ThinFenceTile : public Tile +{ +private: + wstring edgeTexture; + bool dropsResources; + wstring texture; + Icon *iconSide; + +public: + ThinFenceTile(int id, const wstring &tex, const wstring &edgeTex, Material *material, bool dropsResources); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual int getRenderShape(); + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + virtual void addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source); + virtual void updateDefaultShape(); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual Icon *getEdgeTexture(); + bool attachsTo(int tile); + +protected: + bool isSilkTouchable(); + shared_ptr getSilkTouchItemInstance(int data); + +public: + void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/ThornsEnchantment.cpp b/Minecraft.World/ThornsEnchantment.cpp new file mode 100644 index 00000000..e2d9f1fa --- /dev/null +++ b/Minecraft.World/ThornsEnchantment.cpp @@ -0,0 +1,77 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.damagesource.h" +#include "ThornsEnchantment.h" + +const float ThornsEnchantment::CHANCE_PER_LEVEl = 0.15f; + +ThornsEnchantment::ThornsEnchantment(int id, int freq) : Enchantment(id, freq, EnchantmentCategory::armor_torso) +{ + setDescriptionId(IDS_ENCHANTMENT_THORNS); +} + +int ThornsEnchantment::getMinCost(int level) +{ + return 10 + 20 * (level - 1); +} + +int ThornsEnchantment::getMaxCost(int level) +{ + return Enchantment::getMinCost(level) + 50; +} + +int ThornsEnchantment::getMaxLevel() +{ + return 3; +} + +bool ThornsEnchantment::canEnchant(shared_ptr item) +{ + ArmorItem *armor = dynamic_cast(item->getItem()); + if (armor) return true; + return Enchantment::canEnchant(item); +} + +bool ThornsEnchantment::shouldHit(int level, Random *random) +{ + if (level <= 0) return false; + return random->nextFloat() < CHANCE_PER_LEVEl * level; +} + +int ThornsEnchantment::getDamage(int level, Random *random) +{ + if (level > 10) + { + return level - 10; + } + else + { + return 1 + random->nextInt(4); + } +} + +void ThornsEnchantment::doThornsAfterAttack(shared_ptr source, shared_ptr target, Random *random) +{ + int level = EnchantmentHelper::getArmorThorns(target); + shared_ptr item = EnchantmentHelper::getRandomItemWith(Enchantment::thorns, target); + + if (shouldHit(level, random)) + { + source->hurt(DamageSource::thorns(target), getDamage(level, random)); + source->playSound(eSoundType_DAMAGE_THORNS, .5f, 1.0f); + + if (item != NULL) + { + item->hurt(3, target); + } + } + else + { + if (item != NULL) + { + item->hurt(1, target); + } + } +} \ No newline at end of file diff --git a/Minecraft.World/ThornsEnchantment.h b/Minecraft.World/ThornsEnchantment.h new file mode 100644 index 00000000..42f4326b --- /dev/null +++ b/Minecraft.World/ThornsEnchantment.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Enchantment.h" + +class ThornsEnchantment : public Enchantment +{ +private: + static const float CHANCE_PER_LEVEl; + +public: + ThornsEnchantment(int id, int freq); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); + virtual bool canEnchant(shared_ptr item); + static bool shouldHit(int level, Random *random); + static int getDamage(int level, Random *random); + static void doThornsAfterAttack(shared_ptr source, shared_ptr target, Random *random); +}; \ No newline at end of file diff --git a/Minecraft.World/ThreadName.cpp b/Minecraft.World/ThreadName.cpp new file mode 100644 index 00000000..f41beb61 --- /dev/null +++ b/Minecraft.World/ThreadName.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" + +// From Xbox documentation + +typedef struct tagTHREADNAME_INFO { + DWORD dwType; // Must be 0x1000 + LPCSTR szName; // Pointer to name (in user address space) + DWORD dwThreadID; // Thread ID (-1 for caller thread) + DWORD dwFlags; // Reserved for future use; must be zero +} THREADNAME_INFO; + +void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName ) +{ +#ifndef __PS3__ + THREADNAME_INFO info; + + info.dwType = 0x1000; + info.szName = szThreadName; + info.dwThreadID = dwThreadID; + info.dwFlags = 0; + +#if ( defined _WINDOWS64 | defined _DURANGO ) + __try + { + RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR *)&info ); + } + __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER ) + { + } +#endif +#ifdef _XBOX + __try + { + RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD *)&info ); + } + __except( GetExceptionCode()==0x406D1388 ? EXCEPTION_CONTINUE_EXECUTION : EXCEPTION_EXECUTE_HANDLER ) + { + } +#endif +#endif // __PS3__ +} diff --git a/Minecraft.World/ThreadName.h b/Minecraft.World/ThreadName.h new file mode 100644 index 00000000..e7afe594 --- /dev/null +++ b/Minecraft.World/ThreadName.h @@ -0,0 +1,3 @@ +#pragma once + +void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName ); \ No newline at end of file diff --git a/Minecraft.World/Throwable.cpp b/Minecraft.World/Throwable.cpp new file mode 100644 index 00000000..2f9567a1 --- /dev/null +++ b/Minecraft.World/Throwable.cpp @@ -0,0 +1,281 @@ +#include "stdafx.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "com.mojang.nbt.h" +#include "Throwable.h" + + + +void Throwable::_throwableInit() +{ + xTile = -1; + yTile = -1; + zTile = -1; + lastTile = 0; + inGround = false; + shakeTime = 0; + owner = nullptr; + life = 0; + flightTime = 0; +} + +Throwable::Throwable(Level *level) : Entity(level) +{ + _throwableInit(); + setSize(0.25f, 0.25f); +} + +void Throwable::defineSynchedData() +{ +} + +bool Throwable::shouldRenderAtSqrDistance(double distance) +{ + double size = bb->getSize() * 4; + size *= 64.0f; + return distance < size * size; +} + +Throwable::Throwable(Level *level, shared_ptr mob) : Entity(level) +{ + _throwableInit(); + this->owner = mob; + + setSize(4 / 16.0f, 4 / 16.0f); + + this->moveTo(mob->x, mob->y + mob->getHeadHeight(), mob->z, mob->yRot, mob->xRot); + + + x -= cos(yRot / 180 * PI) * 0.16f; + y -= 0.1f; + z -= sin(yRot / 180 * PI) * 0.16f; + this->setPos(x, y, z); + this->heightOffset = 0; + + + float speed = 0.4f; + xd = (-sin(yRot / 180 * PI) * cos(xRot / 180 * PI)) * speed; + zd = (cos(yRot / 180 * PI) * cos(xRot / 180 * PI)) * speed; + yd = (-sin((xRot + getThrowUpAngleOffset()) / 180 * PI)) * speed; + + shoot(xd, yd, zd, getThrowPower(), 1); +} + +Throwable::Throwable(Level *level, double x, double y, double z) : Entity(level) +{ + _throwableInit(); + life = 0; + + setSize(4 / 16.0f, 4 / 16.0f); + + this->setPos(x, y, z); + this->heightOffset = 0; +} + + +float Throwable::getThrowPower() +{ + return 1.5f; +} + +float Throwable::getThrowUpAngleOffset() +{ + return 0; +} + +void Throwable::shoot(double xd, double yd, double zd, float pow, float uncertainty) +{ + float dist = sqrt(xd * xd + yd * yd + zd * zd); + + xd /= dist; + yd /= dist; + zd /= dist; + + xd += (random->nextGaussian()) * 0.0075f * uncertainty; + yd += (random->nextGaussian()) * 0.0075f * uncertainty; + zd += (random->nextGaussian()) * 0.0075f * uncertainty; + + xd *= pow; + yd *= pow; + zd *= pow; + + this->xd = xd; + this->yd = yd; + this->zd = zd; + + float sd = (float) sqrt(xd * xd + zd * zd); + + yRotO = this->yRot = (float) (atan2(xd, zd) * 180 / PI); + xRotO = this->xRot = (float) (atan2(yd, (double)sd) * 180 / PI); + life = 0; +} + +void Throwable::lerpMotion(double xd, double yd, double zd) +{ + this->xd = xd; + this->yd = yd; + this->zd = zd; + if (xRotO == 0 && yRotO == 0) + { + float sd = (float) sqrt(xd * xd + zd * zd); + yRotO = this->yRot = (float) (atan2(xd, zd) * 180 / PI); + xRotO = this->xRot = (float) (atan2(yd, (double)sd) * 180 / PI); + } +} + +void Throwable::tick() +{ + xOld = x; + yOld = y; + zOld = z; + Entity::tick(); + + if (shakeTime > 0) shakeTime--; + + if (inGround) + { + int tile = level->getTile(xTile, yTile, zTile); + if (tile == lastTile) + { + life++; + if (life == 20 * 60) remove(); + return; + } + else + { + inGround = false; + + xd *= random->nextFloat() * 0.2f; + yd *= random->nextFloat() * 0.2f; + zd *= random->nextFloat() * 0.2f; + life = 0; + flightTime = 0; + } + } + else + { + flightTime++; + } + + Vec3 *from = Vec3::newTemp(x, y, z); + Vec3 *to = Vec3::newTemp(x + xd, y + yd, z + zd); + HitResult *res = level->clip(from, to); + + from = Vec3::newTemp(x, y, z); + to = Vec3::newTemp(x + xd, y + yd, z + zd); + if (res != NULL) + { + to = Vec3::newTemp(res->pos->x, res->pos->y, res->pos->z); + } + + if (!level->isClientSide) + { + shared_ptr hitEntity = nullptr; + vector > *objects = level->getEntities(shared_from_this(), this->bb->expand(xd, yd, zd)->grow(1, 1, 1)); + double nearest = 0; + for (int i = 0; i < objects->size(); i++) + { + shared_ptr e = objects->at(i); + if (!e->isPickable() || (e == owner && flightTime < 5)) continue; + + float rr = 0.3f; + AABB *bb = e->bb->grow(rr, rr, rr); + HitResult *p = bb->clip(from, to); + if (p != NULL) + { + double dd = from->distanceTo(p->pos); + delete p; + if (dd < nearest || nearest == 0) + { + hitEntity = e; + nearest = dd; + } + } + } + + if (hitEntity != NULL) + { + if(res != NULL) delete res; + res = new HitResult(hitEntity); + } + } + + if (res != NULL) + { + onHit(res); + delete res; + } + x += xd; + y += yd; + z += zd; + + float sd = (float) sqrt(xd * xd + zd * zd); + yRot = (float) (atan2(xd, zd) * 180 / PI); + xRot = (float) (atan2(yd, (double)sd) * 180 / PI); + + while (xRot - xRotO < -180) + xRotO -= 360; + while (xRot - xRotO >= 180) + xRotO += 360; + + while (yRot - yRotO < -180) + yRotO -= 360; + while (yRot - yRotO >= 180) + yRotO += 360; + + xRot = xRotO + (xRot - xRotO) * 0.2f; + yRot = yRotO + (yRot - yRotO) * 0.2f; + + + float inertia = 0.99f; + float gravity = getGravity(); + + if (isInWater()) + { + for (int i = 0; i < 4; i++) + { + float s = 1 / 4.0f; + level->addParticle(eParticleType_bubble, x - xd * s, y - yd * s, z - zd * s, xd, yd, zd); + } + inertia = 0.80f; + } + + xd *= inertia; + yd *= inertia; + zd *= inertia; + yd -= gravity; + + setPos(x, y, z); +} + +float Throwable::getGravity() +{ + return 0.03f; +} + + +void Throwable::addAdditonalSaveData(CompoundTag *tag) +{ + tag->putShort(L"xTile", (short) xTile); + tag->putShort(L"yTile", (short) yTile); + tag->putShort(L"zTile", (short) zTile); + tag->putByte(L"inTile", (byte) lastTile); + tag->putByte(L"shake", (byte) shakeTime); + tag->putByte(L"inGround", (byte) (inGround ? 1 : 0)); +} + +void Throwable::readAdditionalSaveData(CompoundTag *tag) +{ + xTile = tag->getShort(L"xTile"); + yTile = tag->getShort(L"yTile"); + zTile = tag->getShort(L"zTile"); + lastTile = tag->getByte(L"inTile") & 0xff; + shakeTime = tag->getByte(L"shake") & 0xff; + inGround = tag->getByte(L"inGround") == 1; +} + +float Throwable::getShadowHeightOffs() +{ + return 0; +} \ No newline at end of file diff --git a/Minecraft.World/Throwable.h b/Minecraft.World/Throwable.h new file mode 100644 index 00000000..4d7daea0 --- /dev/null +++ b/Minecraft.World/Throwable.h @@ -0,0 +1,60 @@ +#pragma once + +#include "Entity.h" + +class Mob; +class HitResult; + +class Throwable : public Entity +{ +private: + int xTile; + int yTile; + int zTile; + int lastTile; + +protected: + bool inGround; + +public: + int shakeTime; + +protected: + shared_ptr owner; + +private: + int life; + int flightTime; + + void _throwableInit(); + +public: + Throwable(Level *level); + +protected: + virtual void defineSynchedData(); + +public: + virtual bool shouldRenderAtSqrDistance(double distance); + + Throwable(Level *level, shared_ptr mob); + Throwable(Level *level, double x, double y, double z); + +protected: + virtual float getThrowPower(); + virtual float getThrowUpAngleOffset(); + +public: + virtual void shoot(double xd, double yd, double zd, float pow, float uncertainty); + virtual void lerpMotion(double xd, double yd, double zd); + virtual void tick(); + +protected: + virtual float getGravity(); + virtual void onHit(HitResult *res) = 0; + +public: + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual float getShadowHeightOffs(); +}; \ No newline at end of file diff --git a/Minecraft.World/ThrownEgg.cpp b/Minecraft.World/ThrownEgg.cpp new file mode 100644 index 00000000..45e5ca8c --- /dev/null +++ b/Minecraft.World/ThrownEgg.cpp @@ -0,0 +1,67 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.damagesource.h" +#include "com.mojang.nbt.h" +#include "ThrownEgg.h" +#include "MobCategory.h" + + + +void ThrownEgg::_init() +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); +} + +ThrownEgg::ThrownEgg(Level *level) : Throwable(level) +{ + _init(); +} + +ThrownEgg::ThrownEgg(Level *level, shared_ptr mob) : Throwable(level,mob) +{ + _init(); +} + +ThrownEgg::ThrownEgg(Level *level, double x, double y, double z) : Throwable(level,x,y,z) +{ + _init(); +} + +void ThrownEgg::onHit(HitResult *res) +{ + if (res->entity != NULL) + { + DamageSource *damageSource = DamageSource::thrown(shared_from_this(), owner); + res->entity->hurt(damageSource, 0); + delete damageSource; + } + if (!level->isClientSide && random->nextInt(8) == 0) + { + if(level->canCreateMore( eTYPE_CHICKEN, Level::eSpawnType_Breed) ) // 4J - added limit for number of chickens in world + { + int count = 1; + if (random->nextInt(32) == 0) count = 4; + for (int i = 0; i < count; i++) + { + shared_ptr chicken = shared_ptr( new Chicken(level) ); + chicken->setAge(-20 * 60 * 20); + + chicken->moveTo(x, y, z, yRot, 0); + chicken->setDespawnProtected(); // 4J added, default to being protected against despawning + level->addEntity(chicken); + } + } + } + + for (int i = 0; i < 8; i++) + level->addParticle(eParticleType_snowballpoof, x, y, z, 0, 0, 0); + + if (!level->isClientSide) + { + remove(); + } +} diff --git a/Minecraft.World/ThrownEgg.h b/Minecraft.World/ThrownEgg.h new file mode 100644 index 00000000..864755b7 --- /dev/null +++ b/Minecraft.World/ThrownEgg.h @@ -0,0 +1,23 @@ +#pragma once +using namespace std; + +#include "Throwable.h" + +class HitResult; + +class ThrownEgg : public Throwable +{ +public: + eINSTANCEOF GetType() { return eTYPE_THROWNEGG; } + +private: + void _init(); + +public: + ThrownEgg(Level *level); + ThrownEgg(Level *level, shared_ptr mob); + ThrownEgg(Level *level, double x, double y, double z); + +protected: + virtual void onHit(HitResult *res); +}; diff --git a/Minecraft.World/ThrownEnderpearl.cpp b/Minecraft.World/ThrownEnderpearl.cpp new file mode 100644 index 00000000..1cddfb1f --- /dev/null +++ b/Minecraft.World/ThrownEnderpearl.cpp @@ -0,0 +1,62 @@ +#include "stdafx.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "..\Minecraft.Client\ServerPlayer.h" +#include "..\Minecraft.Client\PlayerConnection.h" +#include "ThrownEnderpearl.h" + + + +ThrownEnderpearl::ThrownEnderpearl(Level *level) : Throwable(level) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); +} + +ThrownEnderpearl::ThrownEnderpearl(Level *level, shared_ptr mob) : Throwable(level,mob) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); +} + +ThrownEnderpearl::ThrownEnderpearl(Level *level, double x, double y, double z) : Throwable(level,x,y,z) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); +} + +void ThrownEnderpearl::onHit(HitResult *res) +{ + if (res->entity != NULL) + { + DamageSource *damageSource = DamageSource::thrown(shared_from_this(), owner); + res->entity->hurt(damageSource, 0); + delete damageSource; + } + for (int i = 0; i < 32; i++) + { + level->addParticle(eParticleType_ender, x, y + random->nextDouble() * 2, z, random->nextGaussian(), 0, random->nextGaussian()); + } + + if (!level->isClientSide) + { + // Fix for #67486 - TCR #001: BAS Game Stability: Customer Encountered: TU8: Code: Gameplay: The title crashes on Host's console when Client Player leaves the game before the Ender Pearl thrown by him touches the ground. + // If the owner has been removed, then ignore + shared_ptr serverPlayer = dynamic_pointer_cast(owner); + if (serverPlayer != NULL && !serverPlayer->removed) + { + if(!serverPlayer->connection->done && serverPlayer->level == this->level) + { + owner->teleportTo(x, y, z); + owner->fallDistance = 0; + owner->hurt(DamageSource::fall, 5); + } + } + remove(); + } +} \ No newline at end of file diff --git a/Minecraft.World/ThrownEnderpearl.h b/Minecraft.World/ThrownEnderpearl.h new file mode 100644 index 00000000..6f16ebfc --- /dev/null +++ b/Minecraft.World/ThrownEnderpearl.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Throwable.h" + +class HitResult; + +class ThrownEnderpearl : public Throwable +{ +public: + eINSTANCEOF GetType() { return eTYPE_THROWNENDERPEARL; } + static Entity *create(Level *level) { return new ThrownEnderpearl(level); } + + ThrownEnderpearl(Level *level); + ThrownEnderpearl(Level *level, shared_ptr mob); + ThrownEnderpearl(Level *level, double x, double y, double z); + +protected: + virtual void onHit(HitResult *res); +}; \ No newline at end of file diff --git a/Minecraft.World/ThrownExpBottle.cpp b/Minecraft.World/ThrownExpBottle.cpp new file mode 100644 index 00000000..75d3d9a1 --- /dev/null +++ b/Minecraft.World/ThrownExpBottle.cpp @@ -0,0 +1,55 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "JavaMath.h" +#include "ThrownExpBottle.h" + + + +ThrownExpBottle::ThrownExpBottle(Level *level) : Throwable(level) +{ +} + +ThrownExpBottle::ThrownExpBottle(Level *level, shared_ptr mob) : Throwable(level,mob) +{ +} + +ThrownExpBottle::ThrownExpBottle(Level *level, double x, double y, double z) : Throwable(level, x, y, z) +{ +} + + +float ThrownExpBottle::getGravity() +{ + return 0.07f; +} + +float ThrownExpBottle::getThrowPower() +{ + return 0.7f; +} + +float ThrownExpBottle::getThrowUpAngleOffset() +{ + return -20; +} + +void ThrownExpBottle::onHit(HitResult *res) +{ + + if (!level->isClientSide) + { + level->levelEvent(LevelEvent::PARTICLES_POTION_SPLASH, (int) Math::round(x), (int) Math::round(y), (int) Math::round(z), 0); + + int xpCount = 3 + level->random->nextInt(5) + level->random->nextInt(5); + while (xpCount > 0) + { + int newCount = ExperienceOrb::getExperienceValue(xpCount); + xpCount -= newCount; + level->addEntity(shared_ptr( new ExperienceOrb(level, x, y, z, newCount) ) ); + } + + remove(); + } +} \ No newline at end of file diff --git a/Minecraft.World/ThrownExpBottle.h b/Minecraft.World/ThrownExpBottle.h new file mode 100644 index 00000000..8430794b --- /dev/null +++ b/Minecraft.World/ThrownExpBottle.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Throwable.h" + +class HitResult; + +class ThrownExpBottle : public Throwable +{ +public: + eINSTANCEOF GetType() { return eTYPE_THROWNEXPBOTTLE; } + static Entity *create(Level *level) { return new ThrownExpBottle(level); } +public: + ThrownExpBottle(Level *level); + ThrownExpBottle(Level *level, shared_ptr mob); + ThrownExpBottle(Level *level, double x, double y, double z); + +protected: + virtual float getGravity(); + virtual float getThrowPower(); + virtual float getThrowUpAngleOffset(); + virtual void onHit(HitResult *res); +}; \ No newline at end of file diff --git a/Minecraft.World/ThrownPotion.cpp b/Minecraft.World/ThrownPotion.cpp new file mode 100644 index 00000000..7375d661 --- /dev/null +++ b/Minecraft.World/ThrownPotion.cpp @@ -0,0 +1,136 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.effect.h" +#include "SharedConstants.h" +#include "JavaMath.h" +#include "ThrownPotion.h" + + + +const double ThrownPotion::SPLASH_RANGE = 4.0; +const double ThrownPotion::SPLASH_RANGE_SQ = ThrownPotion::SPLASH_RANGE * ThrownPotion::SPLASH_RANGE; + +void ThrownPotion::_init() +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + potionValue = 0; +} + +ThrownPotion::ThrownPotion(Level *level) : Throwable(level) +{ + _init(); +} + +ThrownPotion::ThrownPotion(Level *level, shared_ptr mob, int potionValue) : Throwable(level,mob) +{ + _init(); + + this->potionValue = potionValue; +} + +ThrownPotion::ThrownPotion(Level *level, double x, double y, double z, int potionValue) : Throwable(level,x,y,z) +{ + _init(); + this->potionValue = potionValue; +} + +float ThrownPotion::getGravity() +{ + return 0.05f; +} + +float ThrownPotion::getThrowPower() +{ + return 0.5f; +} + +float ThrownPotion::getThrowUpAngleOffset() +{ + return -20; +} + +void ThrownPotion::setPotionValue(int potionValue) +{ + this->potionValue = potionValue; +} + +int ThrownPotion::getPotionValue() +{ + return potionValue; +} + +void ThrownPotion::onHit(HitResult *res) +{ + if (!level->isClientSide) + { + vector *mobEffects = Item::potion->getMobEffects(potionValue); + + if (mobEffects != NULL && !mobEffects->empty()) + { + AABB *aoe = bb->grow(SPLASH_RANGE, SPLASH_RANGE / 2, SPLASH_RANGE); + vector > *entitiesOfClass = level->getEntitiesOfClass(typeid(Mob), aoe); + + if (entitiesOfClass != NULL && !entitiesOfClass->empty()) + { + //for (Entity e : entitiesOfClass) + for(AUTO_VAR(it, entitiesOfClass->begin()); it != entitiesOfClass->end(); ++it) + { + //shared_ptr e = *it; + shared_ptr e = dynamic_pointer_cast( *it ); + double dist = distanceToSqr(e); + if (dist < SPLASH_RANGE_SQ) + { + double scale = 1.0 - (sqrt(dist) / SPLASH_RANGE); + if (e == res->entity) + { + scale = 1; + } + + //for (MobEffectInstance effect : mobEffects) + for(AUTO_VAR(itMEI, mobEffects->begin()); itMEI != mobEffects->end(); ++itMEI) + { + MobEffectInstance *effect = *itMEI; + int id = effect->getId(); + if (MobEffect::effects[id]->isInstantenous()) + { + MobEffect::effects[id]->applyInstantenousEffect(this->owner, e, effect->getAmplifier(), scale); + } + else + { + int duration = (int) (scale * (double) effect->getDuration() + .5); + if (duration > SharedConstants::TICKS_PER_SECOND) + { + e->addEffect(new MobEffectInstance(id, duration, effect->getAmplifier())); + } + } + } + } + } + } + delete entitiesOfClass; + } + level->levelEvent(LevelEvent::PARTICLES_POTION_SPLASH, (int) Math::round(x), (int) Math::round(y), (int) Math::round(z), potionValue); + + remove(); + } +} + +void ThrownPotion::readAdditionalSaveData(CompoundTag *tag) +{ + Throwable::readAdditionalSaveData(tag); + + potionValue = tag->getInt(L"potionValue"); +} + +void ThrownPotion::addAdditonalSaveData(CompoundTag *tag) +{ + Throwable::addAdditonalSaveData(tag); + + tag->putInt(L"potionValue", potionValue); +} \ No newline at end of file diff --git a/Minecraft.World/ThrownPotion.h b/Minecraft.World/ThrownPotion.h new file mode 100644 index 00000000..cf27491c --- /dev/null +++ b/Minecraft.World/ThrownPotion.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Throwable.h" + +class HitResult; + +class ThrownPotion : public Throwable +{ +public: + eINSTANCEOF GetType() { return eTYPE_THROWNPOTION; } + static Entity *create(Level *level) { return new ThrownPotion(level); } + +public: + static const double SPLASH_RANGE; + +private: + static const double SPLASH_RANGE_SQ; + + int potionValue; + + void _init(); + +public: + ThrownPotion(Level *level); + ThrownPotion(Level *level, shared_ptr mob, int potionValue); + ThrownPotion(Level *level, double x, double y, double z, int potionValue); + +protected: + virtual float getGravity(); + virtual float getThrowPower(); + virtual float getThrowUpAngleOffset(); + +public: + void setPotionValue(int potionValue); + int getPotionValue(); + +protected: + virtual void onHit(HitResult *res); + +public: + void readAdditionalSaveData(CompoundTag *tag); + void addAdditonalSaveData(CompoundTag *tag); +}; \ No newline at end of file diff --git a/Minecraft.World/TickNextTickData.cpp b/Minecraft.World/TickNextTickData.cpp new file mode 100644 index 00000000..95cab285 --- /dev/null +++ b/Minecraft.World/TickNextTickData.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" + +#include "TickNextTickData.h" + +__int64 TickNextTickData::C = 0; + +TickNextTickData::TickNextTickData(int x, int y, int z, int tileId) +{ + m_delay = 0; + c = C++; + + this->x = x; + this->y = y; + this->z = z; + this->tileId = tileId; +} + + +bool TickNextTickData::equals(const void *o) const +{ + // TODO 4J Is this safe to cast it before we do a dynamic_cast? Will the dynamic_cast still fail? + // We cannot dynamic_cast a void* + if ( dynamic_cast( (TickNextTickData *) o ) != NULL) + { + TickNextTickData *t = (TickNextTickData *) o; + return x == t->x && y == t->y && z == t->z && tileId == t->tileId; + } + return false; +} + +int TickNextTickData::hashCode() const +{ + return (((x * 1024 * 1024) + (z * 1024) + y) * 256) + tileId; +} + +TickNextTickData *TickNextTickData::delay(__int64 l) +{ + this->m_delay = l; + return this; +} + +int TickNextTickData::compareTo(const TickNextTickData *tnd) const +{ + if (m_delay < tnd->m_delay) return -1; + if (m_delay > tnd->m_delay) return 1; + if (c < tnd->c) return -1; + if (c > tnd->c) return 1; + return 0; +} + +//A class that takes two arguments of the same type as the container elements and returns a bool. +//The expression comp(a,b), where comp is an object of this comparison class and a and b are elements of the container, +//shall return true if a is to be placed at an earlier position than b in a strict weak ordering operation. +//This can either be a class implementing a function call operator or a pointer to a function (see constructor for an example). +//This defaults to less, which returns the same as applying the less-than operator (a +// We don't need to do that as it is only as helper for the java sdk sorting operations + +class TickNextTickData +{ +private: + static __int64 C; + +public: + int x, y, z, tileId; + __int64 m_delay; + +private: + __int64 c; + +public: + TickNextTickData(int x, int y, int z, int tileId); + + bool equals(const void *o) const; + int hashCode() const; + TickNextTickData *delay(__int64 l); + int compareTo(const TickNextTickData *tnd) const; + + static bool compare_fnct(const TickNextTickData &x, const TickNextTickData &y); + static int hash_fnct(const TickNextTickData &k); + static bool eq_test(const TickNextTickData &x, const TickNextTickData &y); +}; + +typedef struct +{ + int operator() (const TickNextTickData &k) const { return TickNextTickData::hash_fnct (k); } + +} TickNextTickDataKeyHash; + +typedef struct +{ + bool operator() (const TickNextTickData &x, const TickNextTickData &y) const { return TickNextTickData::eq_test (x, y); } +} TickNextTickDataKeyEq; + +typedef struct +{ + bool operator() (const TickNextTickData &x, const TickNextTickData &y) const { return TickNextTickData::compare_fnct (x, y); } + +} TickNextTickDataKeyCompare; \ No newline at end of file diff --git a/Minecraft.World/Tile.cpp b/Minecraft.World/Tile.cpp new file mode 100644 index 00000000..e0ef8dd9 --- /dev/null +++ b/Minecraft.World/Tile.cpp @@ -0,0 +1,1632 @@ +#include "stdafx.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.locale.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.food.h" +#include "net.minecraft.world.h" +#include "net.minecraft.h" +#include "Tile.h" + +wstring Tile::TILE_DESCRIPTION_PREFIX = L"Tile."; + +const float Tile::INDESTRUCTIBLE_DESTROY_TIME = -1.0f; + +Tile::SoundType *Tile::SOUND_NORMAL = NULL; +Tile::SoundType *Tile::SOUND_WOOD = NULL; +Tile::SoundType *Tile::SOUND_GRAVEL = NULL; +Tile::SoundType *Tile::SOUND_GRASS = NULL; +Tile::SoundType *Tile::SOUND_STONE = NULL; +Tile::SoundType *Tile::SOUND_METAL = NULL; +Tile::SoundType *Tile::SOUND_GLASS = NULL; +Tile::SoundType *Tile::SOUND_CLOTH = NULL; +Tile::SoundType *Tile::SOUND_SAND = NULL; +Tile::SoundType *Tile::SOUND_SNOW = NULL; +Tile::SoundType *Tile::SOUND_LADDER = NULL; +Tile::SoundType *Tile::SOUND_ANVIL = NULL; + +bool Tile::solid[TILE_NUM_COUNT]; +int Tile::lightBlock[TILE_NUM_COUNT]; +bool Tile::transculent[TILE_NUM_COUNT]; +int Tile::lightEmission[TILE_NUM_COUNT]; +unsigned char Tile::_sendTileData[TILE_NUM_COUNT]; // 4J changed - was bool, now bitfield to indicate which bits are important to be sent +bool Tile::mipmapEnable[TILE_NUM_COUNT]; +bool Tile::propagate[TILE_NUM_COUNT]; + +Tile **Tile::tiles = NULL; + +Tile *Tile::rock = NULL; +GrassTile *Tile::grass = NULL; +Tile *Tile::dirt = NULL; +Tile *Tile::stoneBrick = NULL; +Tile *Tile::wood = NULL; +Tile *Tile::sapling = NULL; +Tile *Tile::unbreakable = NULL; +LiquidTile *Tile::water = NULL; +Tile *Tile::calmWater = NULL; +LiquidTile *Tile::lava = NULL; +Tile *Tile::calmLava = NULL; +Tile *Tile::sand = NULL; +Tile *Tile::gravel = NULL; +Tile *Tile::goldOre = NULL; +Tile *Tile::ironOre = NULL; +Tile *Tile::coalOre = NULL; +Tile *Tile::treeTrunk = NULL; +LeafTile *Tile::leaves = NULL; +Tile *Tile::sponge = NULL; +Tile *Tile::glass = NULL; +Tile *Tile::lapisOre = NULL; +Tile *Tile::lapisBlock = NULL; +Tile *Tile::dispenser = NULL; +Tile *Tile::sandStone = NULL; +Tile *Tile::musicBlock = NULL; +Tile *Tile::bed = NULL; +Tile *Tile::goldenRail = NULL; +Tile *Tile::detectorRail = NULL; +PistonBaseTile *Tile::pistonStickyBase = NULL; +Tile *Tile::web = NULL; +TallGrass *Tile::tallgrass = NULL; +DeadBushTile *Tile::deadBush = NULL; +PistonBaseTile *Tile::pistonBase = NULL; +PistonExtensionTile *Tile::pistonExtension = NULL; +Tile *Tile::cloth = NULL; +PistonMovingPiece *Tile::pistonMovingPiece = NULL; +Bush *Tile::flower = NULL; +Bush *Tile::rose = NULL; +Bush *Tile::mushroom1 = NULL; +Bush *Tile::mushroom2 = NULL; +Tile *Tile::goldBlock = NULL; +Tile *Tile::ironBlock = NULL; +HalfSlabTile *Tile::stoneSlab = NULL; +HalfSlabTile *Tile::stoneSlabHalf = NULL; +Tile *Tile::redBrick = NULL; +Tile *Tile::tnt = NULL; +Tile *Tile::bookshelf = NULL; +Tile *Tile::mossStone = NULL; +Tile *Tile::obsidian = NULL; +Tile *Tile::torch = NULL; +FireTile *Tile::fire = NULL; +Tile *Tile::mobSpawner = NULL; +Tile *Tile::stairs_wood = NULL; +ChestTile *Tile::chest = NULL; +RedStoneDustTile *Tile::redStoneDust = NULL; +Tile *Tile::diamondOre = NULL; +Tile *Tile::diamondBlock = NULL; +Tile *Tile::workBench = NULL; +Tile *Tile::crops = NULL; +Tile *Tile::farmland = NULL; +Tile *Tile::furnace = NULL; +Tile *Tile::furnace_lit = NULL; +Tile *Tile::sign = NULL; +Tile *Tile::door_wood = NULL; +Tile *Tile::ladder = NULL; +Tile *Tile::rail = NULL; +Tile *Tile::stairs_stone = NULL; +Tile *Tile::wallSign = NULL; +Tile *Tile::lever = NULL; +Tile *Tile::pressurePlate_stone = NULL; +Tile *Tile::door_iron = NULL; +Tile *Tile::pressurePlate_wood = NULL; +Tile *Tile::redStoneOre = NULL; +Tile *Tile::redStoneOre_lit = NULL; +Tile *Tile::notGate_off = NULL; +Tile *Tile::notGate_on = NULL; +Tile *Tile::button = NULL; +Tile *Tile::topSnow = NULL; +Tile *Tile::ice = NULL; +Tile *Tile::snow = NULL; +Tile *Tile::cactus = NULL; +Tile *Tile::clay = NULL; +Tile *Tile::reeds = NULL; +Tile *Tile::recordPlayer = NULL; +Tile *Tile::fence = NULL; +Tile *Tile::pumpkin = NULL; +Tile *Tile::hellRock = NULL; +Tile *Tile::hellSand = NULL; +Tile *Tile::lightGem = NULL; +PortalTile *Tile::portalTile = NULL; +Tile *Tile::litPumpkin = NULL; +Tile *Tile::cake = NULL; +RepeaterTile *Tile::diode_off = NULL; +RepeaterTile *Tile::diode_on = NULL; +Tile *Tile::aprilFoolsJoke = NULL; +Tile *Tile::trapdoor = NULL; + +Tile *Tile::monsterStoneEgg = NULL; +Tile *Tile::stoneBrickSmooth = NULL; +Tile *Tile::hugeMushroom1 = NULL; +Tile *Tile::hugeMushroom2 = NULL; +Tile *Tile::ironFence = NULL; +Tile *Tile::thinGlass = NULL; +Tile *Tile::melon = NULL; +Tile *Tile::pumpkinStem = NULL; +Tile *Tile::melonStem = NULL; +Tile *Tile::vine = NULL; +Tile *Tile::fenceGate = NULL; +Tile *Tile::stairs_bricks = NULL; +Tile *Tile::stairs_stoneBrickSmooth = NULL; + +MycelTile *Tile::mycel = NULL; +Tile *Tile::waterLily = NULL; +Tile *Tile::netherBrick = NULL; +Tile *Tile::netherFence = NULL; +Tile *Tile::stairs_netherBricks = NULL; +Tile *Tile::netherStalk = NULL; +Tile *Tile::enchantTable = NULL; +Tile *Tile::brewingStand = NULL; +CauldronTile *Tile::cauldron = NULL; +Tile *Tile::endPortalTile = NULL; +Tile *Tile::endPortalFrameTile = NULL; +Tile *Tile::whiteStone = NULL; +Tile *Tile::dragonEgg = NULL; +Tile *Tile::redstoneLight = NULL; +Tile *Tile::redstoneLight_lit = NULL; + +// TU9 +Tile *Tile::stairs_sandstone = NULL; +Tile *Tile::woodStairsDark = NULL; +Tile *Tile::woodStairsBirch = NULL; +Tile *Tile::woodStairsJungle = NULL; + +Tile *Tile::button_wood = NULL; +HalfSlabTile *Tile::woodSlab = NULL; +HalfSlabTile *Tile::woodSlabHalf = NULL; + +Tile *Tile::emeraldOre = NULL; +Tile *Tile::enderChest = NULL; +TripWireSourceTile *Tile::tripWireSource = NULL; +Tile *Tile::tripWire = NULL; +Tile *Tile::emeraldBlock = NULL; + + +Tile *Tile::cocoa = NULL; +Tile *Tile::skull = NULL; + +Tile *Tile::cobbleWall = NULL; +Tile *Tile::flowerPot = NULL; +Tile *Tile::carrots = NULL; +Tile *Tile::potatoes = NULL; +Tile *Tile::anvil = NULL; +Tile *Tile::netherQuartz = NULL; +Tile *Tile::quartzBlock = NULL; +Tile *Tile::stairs_quartz = NULL; + +Tile *Tile::woolCarpet = NULL; + +DWORD Tile::tlsIdxShape = TlsAlloc(); + +Tile::ThreadStorage::ThreadStorage() +{ + xx0 = yy0 = zz0 = xx1 = yy1 = zz1 = 0.0; + tileId = 0; +} + +void Tile::CreateNewThreadStorage() +{ + ThreadStorage *tls = new ThreadStorage(); + TlsSetValue(Tile::tlsIdxShape, tls); +} + +void Tile::ReleaseThreadStorage() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + delete tls; +} + +void Tile::staticCtor() +{ + Tile::SOUND_NORMAL = new Tile::SoundType(eMaterialSoundType_STONE, 1, 1); + Tile::SOUND_WOOD = new Tile::SoundType(eMaterialSoundType_WOOD, 1, 1); + Tile::SOUND_GRAVEL = new Tile::SoundType(eMaterialSoundType_GRAVEL, 1, 1); + Tile::SOUND_GRASS = new Tile::SoundType(eMaterialSoundType_GRASS, 1, 1); + Tile::SOUND_STONE = new Tile::SoundType(eMaterialSoundType_STONE, 1, 1); + Tile::SOUND_METAL = new Tile::SoundType(eMaterialSoundType_STONE, 1, 1.5f); + Tile::SOUND_GLASS = new Tile::SoundType(eMaterialSoundType_STONE, 1, 1, eSoundType_RANDOM_GLASS,eSoundType_STEP_STONE); + Tile::SOUND_CLOTH = new Tile::SoundType(eMaterialSoundType_CLOTH, 1, 1); + Tile::SOUND_SAND = new Tile::SoundType(eMaterialSoundType_SAND, 1, 1); + Tile::SOUND_SNOW = new Tile::SoundType(eMaterialSoundType_SNOW, 1, 1); + Tile::SOUND_LADDER = new Tile::SoundType(eMaterialSoundType_LADDER, 1, 1,eSoundType_DIG_WOOD); + Tile::SOUND_ANVIL = new Tile::SoundType(eMaterialSoundType_ANVIL, 0.3f, 1, eSoundType_DIG_STONE, eSoundType_RANDOM_ANVIL_LAND); + + Tile::tiles = new Tile *[TILE_NUM_COUNT]; + memset( tiles, 0, sizeof( Tile *)*TILE_NUM_COUNT ); + + Tile::rail = (new RailTile(66, false)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_rail, Item::eMaterial_iron)->setDestroyTime(0.7f)->setSoundType(Tile::SOUND_METAL)->setTextureName(L"rail")->setDescriptionId(IDS_TILE_RAIL)->sendTileData()->setUseDescriptionId(IDS_DESC_RAIL)->disableMipmap(); + Tile::goldenRail = (new RailTile(27, true)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_rail, Item::eMaterial_gold)->setDestroyTime(0.7f)->setSoundType(Tile::SOUND_METAL)->setTextureName(L"goldenRail")->setDescriptionId(IDS_TILE_GOLDEN_RAIL)->sendTileData()->setUseDescriptionId(IDS_DESC_POWEREDRAIL)->disableMipmap(); + Tile::detectorRail = (new DetectorRailTile(28)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_rail, Item::eMaterial_detector)->setDestroyTime(0.7f)->setSoundType(Tile::SOUND_METAL)->setTextureName(L"detectorRail")->setDescriptionId(IDS_TILE_DETECTOR_RAIL)->sendTileData()->setUseDescriptionId(IDS_DESC_DETECTORRAIL)->disableMipmap(); + + Tile::goldBlock = (new MetalTile(41)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_block, Item::eMaterial_gold)->setDestroyTime(3.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_METAL)->setTextureName(L"blockGold")->setDescriptionId(IDS_TILE_BLOCK_GOLD)->setUseDescriptionId(IDS_DESC_BLOCK_GOLD); + Tile::ironBlock = (new MetalTile(42)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_block, Item::eMaterial_iron)->setDestroyTime(5.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_METAL)->setTextureName(L"blockIron")->setDescriptionId(IDS_TILE_BLOCK_IRON)->setUseDescriptionId(IDS_DESC_BLOCK_IRON); + Tile::lapisBlock = (new Tile(22, Material::stone)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_block, Item::eMaterial_lapis)->setDestroyTime(3.0f)->setExplodeable(5)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"blockLapis")->setDescriptionId(IDS_TILE_BLOCK_LAPIS)->setUseDescriptionId(IDS_DESC_BLOCK_LAPIS); + Tile::musicBlock = (new MusicTile(25)) ->setDestroyTime(0.8f)->setTextureName(L"musicBlock")->setDescriptionId(IDS_TILE_MUSIC_BLOCK)->sendTileData()->setUseDescriptionId(IDS_DESC_NOTEBLOCK); + Tile::diamondBlock = (new MetalTile(57)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_block, Item::eMaterial_diamond)->setDestroyTime(5.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_METAL)->setTextureName(L"blockDiamond")->setDescriptionId(IDS_TILE_BLOCK_DIAMOND)->setUseDescriptionId(IDS_DESC_BLOCK_DIAMOND); + + Tile::goldOre = (new OreTile(14)) ->setDestroyTime(3.0f)->setExplodeable(5)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"oreGold")->setDescriptionId(IDS_TILE_ORE_GOLD)->setUseDescriptionId(IDS_DESC_ORE_GOLD); + Tile::ironOre = (new OreTile(15)) ->setDestroyTime(3.0f)->setExplodeable(5)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"oreIron")->setDescriptionId(IDS_TILE_ORE_IRON)->setUseDescriptionId(IDS_DESC_ORE_IRON); + Tile::coalOre = (new OreTile(16)) ->setDestroyTime(3.0f)->setExplodeable(5)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"oreCoal")->setDescriptionId(IDS_TILE_ORE_COAL)->setUseDescriptionId(IDS_DESC_ORE_COAL); + Tile::lapisOre = (new OreTile(21)) ->setDestroyTime(3.0f)->setExplodeable(5)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"oreLapis")->setDescriptionId(IDS_TILE_ORE_LAPIS)->setUseDescriptionId(IDS_DESC_ORE_LAPIS); + Tile::diamondOre = (new OreTile(56)) ->setDestroyTime(3.0f)->setExplodeable(5)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"oreDiamond")->setDescriptionId(IDS_TILE_ORE_DIAMOND)->setUseDescriptionId(IDS_DESC_ORE_DIAMOND); + + + Tile::rock = (new StoneTile(1)) ->setDestroyTime(1.5f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"stone")->setDescriptionId(IDS_TILE_STONE)->setUseDescriptionId(IDS_DESC_STONE); + Tile::grass = (GrassTile *) (new GrassTile(2)) ->setDestroyTime(0.6f)->setSoundType(Tile::SOUND_GRASS)->setTextureName(L"grass")->setDescriptionId(IDS_TILE_GRASS)->setUseDescriptionId(IDS_DESC_GRASS); + Tile::dirt = (new DirtTile(3)) ->setDestroyTime(0.5f)->setSoundType(Tile::SOUND_GRAVEL)->setTextureName(L"dirt")->setDescriptionId(IDS_TILE_DIRT)->setUseDescriptionId(IDS_DESC_DIRT); + Tile::sapling = (new Sapling(6)) ->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_GRASS)->setTextureName(L"sapling")->setDescriptionId(IDS_TILE_SAPLING)->sendTileData()->setUseDescriptionId(IDS_DESC_SAPLING)->disableMipmap(); + Tile::unbreakable = (new Tile(7, Material::stone)) ->setIndestructible()->setExplodeable(6000000)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"bedrock")->setDescriptionId(IDS_TILE_BEDROCK)->setNotCollectStatistics()->setUseDescriptionId(IDS_DESC_BEDROCK); + Tile::water = (LiquidTile *)(new LiquidTileDynamic(8, Material::water)) ->setDestroyTime(100.0f)->setLightBlock(3)->setTextureName(L"water")->setDescriptionId(IDS_TILE_WATER)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_WATER); + Tile::calmWater = (new LiquidTileStatic(9, Material::water))->setDestroyTime(100.0f)->setLightBlock(3)->setTextureName(L"water")->setDescriptionId(IDS_TILE_WATER)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_WATER); + Tile::lava = (LiquidTile *)(new LiquidTileDynamic(10, Material::lava)) ->setDestroyTime(00.0f)->setLightEmission(1.0f)->setLightBlock(255)->setTextureName(L"lava")->setDescriptionId(IDS_TILE_LAVA)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_LAVA); + Tile::calmLava = (new LiquidTileStatic(11, Material::lava)) ->setDestroyTime(100.0f)->setLightEmission(1.0f)->setLightBlock(255)->setTextureName(L"lava")->setDescriptionId(IDS_TILE_LAVA)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_LAVA); + Tile::sand = (new HeavyTile(12)) ->setDestroyTime(0.5f)->setSoundType(Tile::SOUND_SAND)->setTextureName(L"sand")->setDescriptionId(IDS_TILE_SAND)->setUseDescriptionId(IDS_DESC_SAND); + Tile::gravel = (new GravelTile(13)) ->setDestroyTime(0.6f)->setSoundType(Tile::SOUND_GRAVEL)->setTextureName(L"gravel")->setDescriptionId(IDS_TILE_GRAVEL)->setUseDescriptionId(IDS_DESC_GRAVEL); + Tile::treeTrunk = (new TreeTile(17))->setDestroyTime(2.0f) ->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"log")->setDescriptionId(IDS_TILE_LOG)->sendTileData()->setUseDescriptionId(IDS_DESC_LOG); + // 4J - for leaves, have specified that only the data bits that encode the type of leaf are important to be sent + Tile::leaves = (LeafTile *)(new LeafTile(18)) ->setDestroyTime(0.2f)->setLightBlock(1)->setSoundType(Tile::SOUND_GRASS)->setTextureName(L"leaves")->setDescriptionId(IDS_TILE_LEAVES)->sendTileData(LeafTile::LEAF_TYPE_MASK)->setUseDescriptionId(IDS_DESC_LEAVES); + Tile::sponge = (new Sponge(19)) ->setDestroyTime(0.6f)->setSoundType(Tile::SOUND_GRASS)->setTextureName(L"sponge")->setDescriptionId(IDS_TILE_SPONGE)->setUseDescriptionId(IDS_DESC_SPONGE); + Tile::glass = (new GlassTile(20, Material::glass, false)) ->setDestroyTime(0.3f)->setSoundType(Tile::SOUND_GLASS)->setTextureName(L"glass")->setDescriptionId(IDS_TILE_GLASS)->setUseDescriptionId(IDS_DESC_GLASS); + Tile::dispenser = (new DispenserTile(23)) ->setDestroyTime(3.5f)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"dispenser")->setDescriptionId(IDS_TILE_DISPENSER)->sendTileData()->setUseDescriptionId(IDS_DESC_DISPENSER); + + //Tile::wood = (new Tile(5, 4, Material::wood)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_structwoodstuff, Item::eMaterial_wood)->setDestroyTime(2.0f)->setExplodeable(5)->setSoundType(Tile::SOUND_WOOD)->setDescriptionId(IDS_TILE_WOOD)->sendTileData()->setUseDescriptionId(IDS_DESC_WOODENPLANKS); + Tile::wood = (new WoodTile(5)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_structwoodstuff, Item::eMaterial_wood)->setDestroyTime(2.0f)->setExplodeable(5)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"wood")->setDescriptionId(IDS_TILE_OAKWOOD_PLANKS)->sendTileData()->setUseDescriptionId(IDS_DESC_WOODENPLANKS); + Tile::sandStone = (new SandStoneTile(24)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_structblock, Item::eMaterial_sand)->setSoundType(Tile::SOUND_STONE)->setDestroyTime(0.8f)->sendTileData()->setTextureName(L"sandStone")->setDescriptionId(IDS_TILE_SANDSTONE)->setUseDescriptionId(IDS_DESC_SANDSTONE)->sendTileData(); + Tile::stoneBrick = (new Tile(4, Material::stone)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_structblock, Item::eMaterial_stone)->setDestroyTime(2.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"stonebrick")->setDescriptionId(IDS_TILE_STONE_BRICK)->setUseDescriptionId(IDS_DESC_STONE_BRICK); + Tile::redBrick = (new Tile(45, Material::stone)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_structblock, Item::eMaterial_brick)->setDestroyTime(2.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"brick")->setDescriptionId(IDS_TILE_BRICK)->setUseDescriptionId(IDS_DESC_BRICK); + Tile::clay = (new ClayTile(82)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_structblock, Item::eMaterial_clay)->setDestroyTime(0.6f)->setSoundType(Tile::SOUND_GRAVEL)->setTextureName(L"clay")->setDescriptionId(IDS_TILE_CLAY)->setUseDescriptionId(IDS_DESC_CLAY_TILE); + Tile::snow = (new SnowTile(80)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_structblock, Item::eMaterial_snow)->setDestroyTime(0.2f)->setSoundType(Tile::SOUND_CLOTH)->setTextureName(L"snow")->setDescriptionId(IDS_TILE_SNOW)->setUseDescriptionId(IDS_DESC_SNOW); + + Tile::torch = (new TorchTile(50)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_torch, Item::eMaterial_wood)->setDestroyTime(0.0f)->setLightEmission(15 / 16.0f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"torch")->setDescriptionId(IDS_TILE_TORCH)->sendTileData()->setUseDescriptionId(IDS_DESC_TORCH)->disableMipmap(); + Tile::litPumpkin = (new PumpkinTile(91, true)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_torch, Item::eMaterial_pumpkin)->setDestroyTime(1.0f)->setSoundType(Tile::SOUND_WOOD)->setLightEmission(1.0f)->setTextureName(L"litpumpkin")->setDescriptionId(IDS_TILE_LIT_PUMPKIN)->sendTileData()->setUseDescriptionId(IDS_DESC_JACKOLANTERN); + Tile::lightGem = (new LightGemTile(89, Material::glass)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_torch, Item::eMaterial_glowstone)->setDestroyTime(0.3f)->setSoundType(Tile::SOUND_GLASS)->setLightEmission(1.0f)->setTextureName(L"lightgem")->setDescriptionId(IDS_TILE_LIGHT_GEM)->setUseDescriptionId(IDS_DESC_GLOWSTONE); + + Tile::trapdoor = (new TrapDoorTile(96, Material::wood)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_door, Item::eMaterial_trap)->setDestroyTime(3.0f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"trapdoor")->setDescriptionId(IDS_TILE_TRAPDOOR)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_TRAPDOOR); + + Tile::bed = (new BedTile(26)) ->setDestroyTime(0.2f)->setTextureName(L"bed")->setDescriptionId(IDS_TILE_BED)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_BED); + Tile::pistonStickyBase = (PistonBaseTile *)(new PistonBaseTile(29, true)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_piston, Item::eMaterial_stickypiston)->setTextureName(L"pistonStickyBase")->setDescriptionId(IDS_TILE_PISTON_STICK_BASE)->setUseDescriptionId(IDS_DESC_STICKY_PISTON)->sendTileData(); + Tile::web = (new WebTile(30)) ->setLightBlock(1)->setDestroyTime(4.0f)->setTextureName(L"web")->setDescriptionId(IDS_TILE_WEB)->setUseDescriptionId(IDS_DESC_WEB); + Tile::tallgrass = (TallGrass *)(new TallGrass(31)) ->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_GRASS)->setTextureName(L"tallgrass")->setDescriptionId(IDS_TILE_TALL_GRASS)->setUseDescriptionId(IDS_DESC_TALL_GRASS)->disableMipmap(); + Tile::deadBush = (DeadBushTile *)(new DeadBushTile(32)) ->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_GRASS)->setTextureName(L"deadbush")->setDescriptionId(IDS_TILE_DEAD_BUSH)->setUseDescriptionId(IDS_DESC_DEAD_BUSH)->disableMipmap(); + Tile::pistonBase = (PistonBaseTile *)(new PistonBaseTile(33,false)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_piston, Item::eMaterial_piston)->setTextureName(L"pistonBase")->setDescriptionId(IDS_TILE_PISTON_BASE)->setUseDescriptionId(IDS_DESC_PISTON)->sendTileData(); + Tile::pistonExtension = (PistonExtensionTile *)(new PistonExtensionTile(34)) ->setDescriptionId(IDS_TILE_PISTON_BASE)->setUseDescriptionId(-1)->sendTileData(); + + Tile::cloth = (new ClothTile()) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_cloth, Item::eMaterial_cloth)->setDestroyTime(0.8f)->setSoundType(Tile::SOUND_CLOTH)->setTextureName(L"cloth")->setDescriptionId(IDS_TILE_CLOTH)->sendTileData()->setUseDescriptionId(IDS_DESC_WOOL); + Tile::pistonMovingPiece = (PistonMovingPiece *)(new PistonMovingPiece(36)) ->setDescriptionId(IDS_TILE_PISTON_BASE)->setUseDescriptionId(-1); + + Tile::pressurePlate_stone = (Tile *)(new PressurePlateTile(70, L"stone", Material::stone, PressurePlateTile::mobs)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_pressureplate, Item::eMaterial_stone)->setDestroyTime(0.5f)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"pressurePlate")->setDescriptionId(IDS_TILE_PRESSURE_PLATE)->sendTileData()->setUseDescriptionId(IDS_DESC_PRESSUREPLATE); + Tile::pressurePlate_wood = (new PressurePlateTile(72, L"wood", Material::wood, PressurePlateTile::everything)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_pressureplate, Item::eMaterial_wood)->setDestroyTime(0.5f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"pressurePlate")->setDescriptionId(IDS_TILE_PRESSURE_PLATE)->sendTileData()->setUseDescriptionId(IDS_DESC_PRESSUREPLATE); + + Tile::flower = (Bush *) (new Bush(37)) ->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_GRASS)->setTextureName(L"flower")->setDescriptionId(IDS_TILE_FLOWER)->setUseDescriptionId(IDS_DESC_FLOWER)->disableMipmap(); + Tile::rose = (Bush *) (new Bush(38)) ->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_GRASS)->setTextureName(L"rose")->setDescriptionId(IDS_TILE_ROSE)->setUseDescriptionId(IDS_DESC_FLOWER)->disableMipmap(); + Tile::mushroom1 = (Bush *) (new Mushroom(39, L"mushroom_brown")) ->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_GRASS)->setLightEmission(2 / 16.0f)->setTextureName(L"mushroom")->setDescriptionId(IDS_TILE_MUSHROOM)->setUseDescriptionId(IDS_DESC_MUSHROOM)->disableMipmap(); + Tile::mushroom2 = (Bush *) (new Mushroom(40, L"mushroom_red")) ->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_GRASS)->setTextureName(L"mushroom")->setDescriptionId(IDS_TILE_MUSHROOM)->setUseDescriptionId(IDS_DESC_MUSHROOM)->disableMipmap(); + +// Tile::stoneSlab = (new StoneSlabTile(43, true)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_slab, Item::eMaterial_stone)->setDestroyTime(2.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setDescriptionId(IDS_TILE_STONESLAB)->setUseDescriptionId(IDS_DESC_SLAB); +// Tile::stoneSlabHalf = (new StoneSlabTile(44, false)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_halfslab, Item::eMaterial_stone)->setDestroyTime(2.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setDescriptionId(IDS_TILE_STONESLAB)->setUseDescriptionId(IDS_DESC_HALFSLAB); + + + Tile::tnt = (new TntTile(46)) ->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_GRASS)->setTextureName(L"tnt")->setDescriptionId(IDS_TILE_TNT)->setUseDescriptionId(IDS_DESC_TNT); + Tile::bookshelf = (new BookshelfTile(47)) ->setDestroyTime(1.5f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"bookshelf")->setDescriptionId(IDS_TILE_BOOKSHELF)->setUseDescriptionId(IDS_DESC_BOOKSHELF); + Tile::mossStone = (new Tile(48, Material::stone)) ->setDestroyTime(2.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"stoneMoss")->setDescriptionId(IDS_TILE_STONE_MOSS)->setUseDescriptionId(IDS_DESC_MOSS_STONE); + // 4J - change of destroy time from 10.0f -> 50.0f for obsidian brought forward from 1.2.3 + Tile::obsidian = (new ObsidianTile(49)) ->setDestroyTime(50.0f)->setExplodeable(2000)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"obsidian")->setDescriptionId(IDS_TILE_OBSIDIAN)->setUseDescriptionId(IDS_DESC_OBSIDIAN); + + + Tile::fire = (FireTile *) ((new FireTile(51)) ->setDestroyTime(0.0f)->setLightEmission(1.0f)->setSoundType(Tile::SOUND_WOOD))->setTextureName(L"fire")->setDescriptionId(IDS_TILE_FIRE)->setNotCollectStatistics()->setUseDescriptionId(-1); + Tile::mobSpawner = (new MobSpawnerTile(52)) ->setDestroyTime(5.0f)->setSoundType(Tile::SOUND_METAL)->setTextureName(L"mobSpawner")->setDescriptionId(IDS_TILE_MOB_SPAWNER)->setNotCollectStatistics()->setUseDescriptionId(IDS_DESC_MOB_SPAWNER); + + Tile::chest = (ChestTile *)(new ChestTile(54)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_chest, Item::eMaterial_ender)->setDestroyTime(2.5f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"chest")->setDescriptionId(IDS_TILE_CHEST)->sendTileData()->setUseDescriptionId(IDS_DESC_CHEST); + Tile::redStoneDust = (RedStoneDustTile *)(new RedStoneDustTile(55)) ->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_NORMAL)->setTextureName(L"redstoneDust")->setDescriptionId(IDS_TILE_REDSTONE_DUST)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_REDSTONE_DUST); + Tile::workBench = (new WorkbenchTile(58)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_device, Item::eMaterial_wood)->setDestroyTime(2.5f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"workbench")->setDescriptionId(IDS_TILE_WORKBENCH)->setUseDescriptionId(IDS_DESC_CRAFTINGTABLE); + Tile::crops = (new CropTile(59)) ->setTextureName(L"crops")->setDescriptionId(IDS_TILE_CROPS)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_CROPS)->disableMipmap(); + Tile::farmland = (new FarmTile(60)) ->setDestroyTime(0.6f)->setSoundType(Tile::SOUND_GRAVEL)->setTextureName(L"farmland")->setDescriptionId(IDS_TILE_FARMLAND)->setUseDescriptionId(IDS_DESC_FARMLAND)->sendTileData(); + Tile::furnace = (new FurnaceTile(61, false)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_device, Item::eMaterial_stone)->setDestroyTime(3.5f)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"furnace")->setDescriptionId(IDS_TILE_FURNACE)->sendTileData()->setUseDescriptionId(IDS_DESC_FURNACE); + Tile::furnace_lit = (new FurnaceTile(62, true)) ->setDestroyTime(3.5f)->setSoundType(Tile::SOUND_STONE)->setLightEmission(14 / 16.0f)->setTextureName(L"furnace")->setDescriptionId(IDS_TILE_FURNACE)->sendTileData()->setUseDescriptionId(IDS_DESC_FURNACE); + Tile::sign = (new SignTile(63, eTYPE_SIGNTILEENTITY, true))->setDestroyTime(1.0f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"sign")->setDescriptionId(IDS_TILE_SIGN)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_SIGN); + Tile::door_wood = (new DoorTile(64, Material::wood)) ->setDestroyTime(3.0f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"doorWood")->setDescriptionId(IDS_TILE_DOOR_WOOD)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_DOOR_WOOD); + Tile::ladder = (new LadderTile(65)) ->setDestroyTime(0.4f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"ladder")->setDescriptionId(IDS_TILE_LADDER)->sendTileData()->setUseDescriptionId(IDS_DESC_LADDER)->disableMipmap(); + Tile::wallSign = (new SignTile(68, eTYPE_SIGNTILEENTITY, false))->setDestroyTime(1.0f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"sign")->setDescriptionId(IDS_TILE_SIGN)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_SIGN); + Tile::lever = (new LeverTile(69)) ->setDestroyTime(0.5f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"lever")->setDescriptionId(IDS_TILE_LEVER)->sendTileData()->setUseDescriptionId(IDS_DESC_LEVER); + Tile::door_iron = (new DoorTile(71, Material::metal)) ->setDestroyTime(5.0f)->setSoundType(Tile::SOUND_METAL)->setTextureName(L"doorIron")->setDescriptionId(IDS_TILE_DOOR_IRON)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_DOOR_IRON); + Tile::redStoneOre = (new RedStoneOreTile(73,false)) ->setDestroyTime(3.0f)->setExplodeable(5)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"oreRedstone")->setDescriptionId(IDS_TILE_ORE_REDSTONE)->sendTileData()->setUseDescriptionId(IDS_DESC_ORE_REDSTONE); + Tile::redStoneOre_lit = (new RedStoneOreTile(74, true)) ->setLightEmission(10 / 16.0f)->setDestroyTime(3.0f)->setExplodeable(5)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"oreRedstone")->setDescriptionId(IDS_TILE_ORE_REDSTONE)->sendTileData()->setUseDescriptionId(IDS_DESC_ORE_REDSTONE); + Tile::notGate_off = (new NotGateTile(75, false)) ->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"notGate")->setDescriptionId(IDS_TILE_NOT_GATE)->sendTileData()->setUseDescriptionId(IDS_DESC_REDSTONETORCH)->disableMipmap(); + Tile::notGate_on = (new NotGateTile(76, true)) ->setDestroyTime(0.0f)->setLightEmission(8 / 16.0f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"notGate")->setDescriptionId(IDS_TILE_NOT_GATE)->sendTileData()->setUseDescriptionId(IDS_DESC_REDSTONETORCH)->disableMipmap(); + Tile::button = (new ButtonTile(77,false)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_button, Item::eMaterial_stone)->setDestroyTime(0.5f)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"button")->setDescriptionId(IDS_TILE_BUTTON)->sendTileData()->setUseDescriptionId(IDS_DESC_BUTTON); + Tile::topSnow = (new TopSnowTile(78)) ->setDestroyTime(0.1f)->setSoundType(Tile::SOUND_CLOTH)->setTextureName(L"snow")->setDescriptionId(IDS_TILE_SNOW)->setUseDescriptionId(IDS_DESC_TOP_SNOW)->sendTileData()->setLightBlock(0); + Tile::ice = (new IceTile(79)) ->setDestroyTime(0.5f)->setLightBlock(3)->setSoundType(Tile::SOUND_GLASS)->setTextureName(L"ice")->setDescriptionId(IDS_TILE_ICE)->setUseDescriptionId(IDS_DESC_ICE); + Tile::cactus = (new CactusTile(81)) ->setDestroyTime(0.4f)->setSoundType(Tile::SOUND_CLOTH)->setTextureName(L"cactus")->setDescriptionId(IDS_TILE_CACTUS)->setUseDescriptionId(IDS_DESC_CACTUS)->disableMipmap(); + Tile::reeds = (new ReedTile(83)) ->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_GRASS)->setTextureName(L"reeds")->setDescriptionId(IDS_TILE_REEDS)->setNotCollectStatistics()->setUseDescriptionId(IDS_DESC_REEDS)->disableMipmap(); + Tile::recordPlayer = (new RecordPlayerTile(84)) ->setDestroyTime(2.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"jukebox")->setDescriptionId(IDS_TILE_JUKEBOX)->sendTileData()->setUseDescriptionId(IDS_DESC_JUKEBOX); + Tile::fence = (new FenceTile(85, L"wood", Material::wood)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_fence, Item::eMaterial_wood)->setDestroyTime(2.0f)->setExplodeable(5)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"fence")->setDescriptionId(IDS_TILE_FENCE)->setUseDescriptionId(IDS_DESC_FENCE); + + Tile::pumpkin = (new PumpkinTile(86, false)) ->setDestroyTime(1.0f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"pumpkin")->setDescriptionId(IDS_TILE_PUMPKIN)->sendTileData()->setUseDescriptionId(IDS_DESC_PUMPKIN); + + Tile::hellRock = (new HellStoneTile(87)) ->setDestroyTime(0.4f)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"hellrock")->setDescriptionId(IDS_TILE_HELL_ROCK)->setUseDescriptionId(IDS_DESC_HELL_ROCK); + Tile::hellSand = (new HellSandTile(88)) ->setDestroyTime(0.5f)->setSoundType(Tile::SOUND_SAND)->setTextureName(L"hellsand")->setDescriptionId(IDS_TILE_HELL_SAND)->setUseDescriptionId(IDS_DESC_HELL_SAND); + Tile::portalTile = (PortalTile *) ((new PortalTile(90)) ->setDestroyTime(-1)->setSoundType(Tile::SOUND_GLASS)->setLightEmission(0.75f))->setTextureName(L"portal")->setDescriptionId(IDS_TILE_PORTAL)->setUseDescriptionId(IDS_DESC_PORTAL); + Tile::cake = (new CakeTile(92)) ->setDestroyTime(0.5f)->setSoundType(Tile::SOUND_CLOTH)->setTextureName(L"cake")->setDescriptionId(IDS_TILE_CAKE)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_CAKE); + + // TODO Copy the translations from IDS_ITEM_DIODE to IDS_TILE_DIODE + Tile::diode_off = (RepeaterTile *)(new DiodeTile(93, false)) ->setDestroyTime(0.0f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"diode")->setDescriptionId(IDS_ITEM_DIODE)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_REDSTONEREPEATER)->disableMipmap(); + Tile::diode_on = (RepeaterTile *)(new DiodeTile(94, true)) ->setDestroyTime(0.0f)->setLightEmission(10 / 16.0f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"diode")->setDescriptionId(IDS_ITEM_DIODE)->setNotCollectStatistics()->sendTileData()->setUseDescriptionId(IDS_DESC_REDSTONEREPEATER)->disableMipmap(); + Tile::aprilFoolsJoke = (new LockedChestTile(95)) ->setDestroyTime(0.0f)->setLightEmission(16 / 16.0f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"lockedchest")->setDescriptionId(IDS_TILE_LOCKED_CHEST)->setTicking(true)->sendTileData()->setUseDescriptionId(-1); + + Tile::monsterStoneEgg = (new StoneMonsterTile(97)) ->setDestroyTime(0.75f)->setTextureName(L"monsterStoneEgg")->setDescriptionId(IDS_TILE_STONE_SILVERFISH)->setUseDescriptionId(IDS_DESC_STONE_SILVERFISH); + Tile::stoneBrickSmooth = (new SmoothStoneBrickTile(98)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_structblock, Item::eMaterial_stoneSmooth)->setDestroyTime(1.5f)->setExplodeable(10)->setSoundType(SOUND_STONE)->setTextureName(L"stonebricksmooth")->setDescriptionId(IDS_TILE_STONE_BRICK_SMOOTH)->setUseDescriptionId(IDS_DESC_STONE_BRICK_SMOOTH); + Tile::hugeMushroom1 = (new HugeMushroomTile(99, Material::wood, 0)) ->setDestroyTime(0.2f)->setSoundType(SOUND_WOOD)->setTextureName(L"mushroom")->setDescriptionId(IDS_TILE_HUGE_MUSHROOM_1)->setUseDescriptionId(IDS_DESC_MUSHROOM)->sendTileData(); + Tile::hugeMushroom2 = (new HugeMushroomTile(100, Material::wood, 1)) ->setDestroyTime(0.2f)->setSoundType(SOUND_WOOD)->setTextureName(L"mushroom")->setDescriptionId(IDS_TILE_HUGE_MUSHROOM_2)->setUseDescriptionId(IDS_DESC_MUSHROOM)->sendTileData(); + Tile::ironFence = (new ThinFenceTile(101, L"fenceIron", L"fenceIron", Material::metal, true)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_fence, Item::eMaterial_iron)->setDestroyTime(5.0f)->setExplodeable(10)->setSoundType(SOUND_METAL)->setTextureName(L"fenceIron")->setDescriptionId(IDS_TILE_IRON_FENCE)->setUseDescriptionId(IDS_DESC_IRON_FENCE); + Tile::thinGlass = (new ThinFenceTile(102, L"glass", L"thinglass_top", Material::glass, false)) ->setDestroyTime(0.3f)->setSoundType(SOUND_GLASS)->setTextureName(L"thinGlass")->setDescriptionId(IDS_TILE_THIN_GLASS)->setUseDescriptionId(IDS_DESC_THIN_GLASS); + Tile::melon = (new MelonTile(103)) ->setDestroyTime(1.0f)->setSoundType(SOUND_WOOD)->setTextureName(L"melon")->setDescriptionId(IDS_TILE_MELON)->setUseDescriptionId(IDS_DESC_MELON_BLOCK); + Tile::pumpkinStem = (new StemTile(104, Tile::pumpkin)) ->setDestroyTime(0.0f)->setSoundType(SOUND_WOOD)->setTextureName(L"pumpkinStem")->setDescriptionId(IDS_TILE_PUMPKIN_STEM)->sendTileData(); + Tile::melonStem = (new StemTile(105, Tile::melon)) ->setDestroyTime(0.0f)->setSoundType(SOUND_WOOD)->setTextureName(L"pumpkinStem")->setDescriptionId(IDS_TILE_MELON_STEM)->sendTileData(); + Tile::vine = (new VineTile(106))->setDestroyTime(0.2f) ->setSoundType(SOUND_GRASS)->setTextureName(L"vine")->setDescriptionId(IDS_TILE_VINE)->setUseDescriptionId(IDS_DESC_VINE)->sendTileData(); + Tile::fenceGate = (new FenceGateTile(107)) ->setDestroyTime(2.0f)->setExplodeable(5)->setSoundType(SOUND_WOOD)->setTextureName(L"fenceGate")->setDescriptionId(IDS_TILE_FENCE_GATE)->sendTileData()->setUseDescriptionId(IDS_DESC_FENCE_GATE); + + + Tile::stairs_wood = (new StairTile(53, Tile::wood,0)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_stairs, Item::eMaterial_wood) ->setTextureName(L"stairsWood")->setDescriptionId(IDS_TILE_STAIRS_WOOD) ->sendTileData()->setUseDescriptionId(IDS_DESC_STAIRS); + Tile::stairs_stone = (new StairTile(67, Tile::stoneBrick,0)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_stairs, Item::eMaterial_stone) ->setTextureName(L"stairsStone")->setDescriptionId(IDS_TILE_STAIRS_STONE) ->sendTileData()->setUseDescriptionId(IDS_DESC_STAIRS); + Tile::stairs_bricks = (new StairTile(108, Tile::redBrick,0)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_stairs, Item::eMaterial_brick) ->setTextureName(L"stairsBrick")->setDescriptionId(IDS_TILE_STAIRS_BRICKS) ->sendTileData()->setUseDescriptionId(IDS_DESC_STAIRS); + Tile::stairs_stoneBrickSmooth = (new StairTile(109, Tile::stoneBrickSmooth,0)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_stairs, Item::eMaterial_stoneSmooth)->setTextureName(L"stairsStoneBrickSmooth")->setDescriptionId(IDS_TILE_STAIRS_STONE_BRICKS_SMOOTH) ->sendTileData()->setUseDescriptionId(IDS_DESC_STAIRS); + + Tile::mycel = (MycelTile *)(new MycelTile(110)) ->setDestroyTime(0.6f)->setSoundType(SOUND_GRASS)->setTextureName(L"mycel")->setDescriptionId(IDS_TILE_MYCEL)->setUseDescriptionId(IDS_DESC_MYCEL); + Tile::waterLily = (new WaterlilyTile(111)) ->setDestroyTime(0.0f)->setSoundType(SOUND_GRASS)->setTextureName(L"waterlily")->setDescriptionId(IDS_TILE_WATERLILY)->setUseDescriptionId(IDS_DESC_WATERLILY); + Tile::netherBrick = (new Tile(112, Material::stone)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_structblock, Item::eMaterial_netherbrick)->setDestroyTime(2.0f)->setExplodeable(10)->setSoundType(SOUND_STONE)->setTextureName(L"netherBrick")->setDescriptionId(IDS_TILE_NETHERBRICK)->setUseDescriptionId(IDS_DESC_NETHERBRICK); + Tile::netherFence = (new FenceTile(113, L"netherBrick", Material::stone))->setBaseItemTypeAndMaterial(Item::eBaseItemType_fence, Item::eMaterial_netherbrick)->setDestroyTime(2.0f)->setExplodeable(10)->setSoundType(SOUND_STONE)->setTextureName(L"netherFence")->setDescriptionId(IDS_TILE_NETHERFENCE)->setUseDescriptionId(IDS_DESC_NETHERFENCE); + Tile::stairs_netherBricks = (new StairTile(114, Tile::netherBrick,0)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_stairs, Item::eMaterial_netherbrick)->setTextureName(L"stairsNetherBrick")->setDescriptionId(IDS_TILE_STAIRS_NETHERBRICK) ->sendTileData()->setUseDescriptionId(IDS_DESC_STAIRS); + Tile::netherStalk = (new NetherStalkTile(115)) ->setTextureName(L"netherStalk")->setDescriptionId(IDS_TILE_NETHERSTALK)->sendTileData()->setUseDescriptionId(IDS_DESC_NETHERSTALK); + Tile::enchantTable = (new EnchantmentTableTile(116)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_device, Item::eMaterial_magic)->setDestroyTime(5.0f)->setExplodeable(2000)->setTextureName(L"enchantmentTable")->setDescriptionId(IDS_TILE_ENCHANTMENTTABLE)->setUseDescriptionId(IDS_DESC_ENCHANTMENTTABLE); + Tile::brewingStand = (new BrewingStandTile(117)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_device, Item::eMaterial_blaze)->setDestroyTime(0.5f)->setLightEmission(2 / 16.0f)->setTextureName(L"brewingStand")->setDescriptionId(IDS_TILE_BREWINGSTAND)->sendTileData()->setUseDescriptionId(IDS_DESC_BREWING_STAND); + Tile::cauldron = (CauldronTile *)(new CauldronTile(118)) ->setDestroyTime(2.0f)->setTextureName(L"cauldron")->setDescriptionId(IDS_TILE_CAULDRON)->sendTileData()->setUseDescriptionId(IDS_DESC_CAULDRON); + Tile::endPortalTile = (new TheEndPortal(119, Material::portal)) ->setDestroyTime(INDESTRUCTIBLE_DESTROY_TIME)->setExplodeable(6000000)->setDescriptionId(IDS_TILE_END_PORTAL)->setUseDescriptionId(IDS_DESC_END_PORTAL); + Tile::endPortalFrameTile = (new TheEndPortalFrameTile(120)) ->setSoundType(SOUND_GLASS)->setLightEmission(2 / 16.0f)->setDestroyTime(INDESTRUCTIBLE_DESTROY_TIME)->setTextureName(L"endPortalFrame")->setDescriptionId(IDS_TILE_ENDPORTALFRAME)->sendTileData()->setExplodeable(6000000)->setUseDescriptionId(IDS_DESC_ENDPORTALFRAME); + Tile::whiteStone = (new Tile(121, Material::stone)) ->setDestroyTime(3.0f)->setExplodeable(15)->setSoundType(SOUND_STONE)->setTextureName(L"whiteStone")->setDescriptionId(IDS_TILE_WHITESTONE)->setUseDescriptionId(IDS_DESC_WHITESTONE); + Tile::dragonEgg = (new EggTile(122)) ->setDestroyTime(3.0f)->setExplodeable(15)->setSoundType(SOUND_STONE)->setLightEmission(2.0f / 16.0f)->setTextureName(L"dragonEgg")->setDescriptionId(IDS_TILE_DRAGONEGG)->setUseDescriptionId(IDS_DESC_DRAGONEGG); + Tile::redstoneLight = (new RedlightTile(123, false)) ->setDestroyTime(0.3f)->setSoundType(SOUND_GLASS)->setTextureName(L"redstoneLight")->setDescriptionId(IDS_TILE_REDSTONE_LIGHT)->setUseDescriptionId(IDS_DESC_REDSTONE_LIGHT); + Tile::redstoneLight_lit = (new RedlightTile(124, true)) ->setDestroyTime(0.3f)->setSoundType(SOUND_GLASS)->setTextureName(L"redstoneLight")->setDescriptionId(IDS_TILE_REDSTONE_LIGHT)->setUseDescriptionId(IDS_DESC_REDSTONE_LIGHT); + + // TU9 + Tile::stairs_sandstone = (new StairTile(128, Tile::sandStone,0)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_stairs, Item::eMaterial_sand) ->setTextureName(L"stairsSandstone")->setDescriptionId(IDS_TILE_STAIRS_SANDSTONE) ->sendTileData()->setUseDescriptionId(IDS_DESC_STAIRS); + Tile::woodStairsDark = (new StairTile(134, Tile::wood, TreeTile::DARK_TRUNK)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_stairs, Item::eMaterial_sprucewood)->setTextureName(L"stairsWoodSpruce")->setDescriptionId(IDS_TILE_STAIRS_SPRUCEWOOD) ->sendTileData()->setUseDescriptionId(IDS_DESC_STAIRS); + Tile::woodStairsBirch = (new StairTile(135, Tile::wood, TreeTile::BIRCH_TRUNK)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_stairs, Item::eMaterial_birchwood)->setTextureName(L"stairsWoodBirch")->setDescriptionId(IDS_TILE_STAIRS_BIRCHWOOD) ->sendTileData()->setUseDescriptionId(IDS_DESC_STAIRS); + Tile::woodStairsJungle = (new StairTile(136, Tile::wood, TreeTile::JUNGLE_TRUNK))->setBaseItemTypeAndMaterial(Item::eBaseItemType_stairs, Item::eMaterial_junglewood)->setTextureName(L"stairsWoodJungle")->setDescriptionId(IDS_TILE_STAIRS_JUNGLEWOOD) ->sendTileData()->setUseDescriptionId(IDS_DESC_STAIRS); + Tile::button_wood = (new ButtonTile(143, true)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_button, Item::eMaterial_wood)->setDestroyTime(0.5f)->setSoundType(Tile::SOUND_WOOD)->setTextureName(L"button")->setDescriptionId(IDS_TILE_BUTTON)->sendTileData()->setUseDescriptionId(IDS_DESC_BUTTON); + + Tile::woodSlab = (HalfSlabTile *) (new WoodSlabTile(Tile::woodSlab_Id, true)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_slab, Item::eMaterial_wood)->setDestroyTime(2.0f)->setExplodeable(5)->setSoundType(SOUND_WOOD)->setTextureName(L"woodSlab")->setDescriptionId(IDS_DESC_WOODSLAB)->setUseDescriptionId(IDS_DESC_WOODSLAB); + Tile::woodSlabHalf = (HalfSlabTile *) (new WoodSlabTile(Tile::woodSlabHalf_Id, false)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_halfslab, Item::eMaterial_wood)->setDestroyTime(2.0f)->setExplodeable(5)->setSoundType(SOUND_WOOD)->setTextureName(L"woodSlab")->setDescriptionId(IDS_DESC_WOODSLAB)->setUseDescriptionId(IDS_DESC_WOODSLAB); + Tile::stoneSlab = (HalfSlabTile *) (new StoneSlabTile(Tile::stoneSlab_Id, true)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_slab, Item::eMaterial_stone)->setDestroyTime(2.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"stoneSlab")->setDescriptionId(IDS_TILE_STONESLAB)->setUseDescriptionId(IDS_DESC_SLAB); + Tile::stoneSlabHalf = (HalfSlabTile *) (new StoneSlabTile(Tile::stoneSlabHalf_Id, false)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_halfslab, Item::eMaterial_stone)->setDestroyTime(2.0f)->setExplodeable(10)->setSoundType(Tile::SOUND_STONE)->setTextureName(L"stoneSlab")->setDescriptionId(IDS_TILE_STONESLAB)->setUseDescriptionId(IDS_DESC_HALFSLAB); + + Tile::emeraldOre = (new OreTile(129)) ->setDestroyTime(3.0f)->setExplodeable(5)->setSoundType(SOUND_STONE)->setTextureName(L"oreEmerald")->setDescriptionId(IDS_TILE_EMERALDORE)->setUseDescriptionId(IDS_DESC_EMERALDORE); + Tile::enderChest = (new EnderChestTile(130)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_chest, Item::eMaterial_ender)->setDestroyTime(22.5f)->setExplodeable(1000)->setSoundType(SOUND_STONE)->setTextureName(L"enderChest")->sendTileData()->setLightEmission(.5f)->setDescriptionId(IDS_TILE_ENDERCHEST)->setUseDescriptionId(IDS_DESC_ENDERCHEST); + Tile::tripWireSource = (TripWireSourceTile *)( new TripWireSourceTile(131) ) ->setTextureName(L"tripWireSource")->sendTileData()->setDescriptionId(IDS_TILE_TRIPWIRE_SOURCE)->setUseDescriptionId(IDS_DESC_TRIPWIRE_SOURCE); + Tile::tripWire = (new TripWireTile(132)) ->setTextureName(L"tripWire")->sendTileData()->setDescriptionId(IDS_TILE_TRIPWIRE)->setUseDescriptionId(IDS_DESC_TRIPWIRE); + Tile::emeraldBlock = (new MetalTile(133)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_block, Item::eMaterial_emerald)->setDestroyTime(5.0f)->setExplodeable(10)->setSoundType(SOUND_METAL)->setTextureName(L"blockEmerald")->setDescriptionId(IDS_TILE_EMERALDBLOCK)->setUseDescriptionId(IDS_DESC_EMERALDBLOCK); + + + Tile::cocoa = (new CocoaTile(127)) ->setDestroyTime(0.2f)->setExplodeable(5)->setSoundType(SOUND_WOOD)->setTextureName(L"cocoa")->sendTileData()->setDescriptionId(IDS_TILE_COCOA)->setUseDescriptionId(IDS_DESC_COCOA); + Tile::skull = (new SkullTile(144)) ->setDestroyTime(1.0f)->setSoundType(SOUND_STONE)->setTextureName(L"skull")->setDescriptionId(IDS_TILE_SKULL)->setUseDescriptionId(IDS_DESC_SKULL); + + Tile::cobbleWall = (new WallTile(139, Tile::stoneBrick)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_fence, Item::eMaterial_stone)->setTextureName(L"cobbleWall")->setDescriptionId(IDS_TILE_COBBLESTONE_WALL)->setUseDescriptionId(IDS_DESC_COBBLESTONE_WALL); + Tile::flowerPot = (new FlowerPotTile(140)) ->setDestroyTime(0.0f)->setSoundType(SOUND_NORMAL)->setTextureName(L"flowerPot")->setDescriptionId(IDS_TILE_FLOWERPOT)->setUseDescriptionId(IDS_DESC_FLOWERPOT); + Tile::carrots = (new CarrotTile(141)) ->setTextureName(L"carrots")->setDescriptionId(IDS_TILE_CARROTS)->setUseDescriptionId(IDS_DESC_CARROTS)->disableMipmap(); + Tile::potatoes = (new PotatoTile(142)) ->setTextureName(L"potatoes")->setDescriptionId(IDS_TILE_POTATOES)->setUseDescriptionId(IDS_DESC_POTATO)->disableMipmap(); + Tile::anvil = (new AnvilTile(145)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_device, Item::eMaterial_iron)->setDestroyTime(5.0f)->setSoundType(SOUND_ANVIL)->setExplodeable(2000)->setTextureName(L"anvil")->sendTileData()->setDescriptionId(IDS_TILE_ANVIL)->setUseDescriptionId(IDS_DESC_ANVIL); + Tile::netherQuartz = (new OreTile(153)) ->setDestroyTime(3.0f)->setExplodeable(5)->setSoundType(SOUND_STONE)->setTextureName(L"netherquartz")->setDescriptionId(IDS_TILE_NETHER_QUARTZ)->setUseDescriptionId(IDS_DESC_NETHER_QUARTZ_ORE); + Tile::quartzBlock = (new QuartzBlockTile(155)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_structblock, Item::eMaterial_quartz)->setSoundType(SOUND_STONE)->setDestroyTime(0.8f)->setTextureName(L"quartzBlock")->setDescriptionId(IDS_TILE_QUARTZ_BLOCK)->setUseDescriptionId(IDS_DESC_QUARTZ_BLOCK); + Tile::stairs_quartz = (new StairTile(156, Tile::quartzBlock, QuartzBlockTile::TYPE_DEFAULT)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_stairs, Item::eMaterial_quartz)->setTextureName(L"stairsQuartz")->setDescriptionId(IDS_TILE_STAIRS_QUARTZ)->setUseDescriptionId(IDS_DESC_STAIRS); + + Tile::woolCarpet = (new WoolCarpetTile(171)) ->setBaseItemTypeAndMaterial(Item::eBaseItemType_carpet, Item::eMaterial_cloth)->setDestroyTime(0.1f)->setSoundType(SOUND_CLOTH)->setTextureName(L"woolCarpet")->setLightBlock(0)->setDescriptionId(IDS_TILE_CARPET)->setUseDescriptionId(IDS_DESC_CARPET); + + // Special cases for certain items since they can have different icons + Item::items[Tile::cloth_Id] = ( new ClothTileItem(Tile::cloth_Id- 256) )->setTextureName(L"cloth")->setDescriptionId(IDS_TILE_CLOTH)->setUseDescriptionId(IDS_DESC_WOOL); + Item::items[Tile::woolCarpet_Id] = ( new ClothTileItem(Tile::woolCarpet_Id - 256))->setTextureName(L"woolCarpet")->setDescriptionId(IDS_TILE_CARPET)->setUseDescriptionId(IDS_DESC_CARPET); + Item::items[Tile::treeTrunk_Id] = ( new TreeTileItem(Tile::treeTrunk_Id - 256, treeTrunk) )->setTextureName(L"log")->setDescriptionId(IDS_TILE_LOG)->setUseDescriptionId(IDS_DESC_LOG); + Item::items[Tile::wood_Id] = ( new MultiTextureTileItem(Tile::wood_Id - 256, Tile::wood, (int *)WoodTile::WOOD_NAMES, 4))->setTextureName(L"wood")->setDescriptionId(IDS_TILE_OAKWOOD_PLANKS)->setUseDescriptionId(IDS_DESC_LOG); // <- TODO + Item::items[Tile::monsterStoneEgg_Id] = ( new StoneMonsterTileItem(Tile::monsterStoneEgg_Id - 256))->setTextureName(L"monsterStoneEgg")->setDescriptionId(IDS_TILE_STONE_SILVERFISH)->setUseDescriptionId(IDS_DESC_STONE_SILVERFISH); // 4J - Brought forward from post-1.2 to fix stacking problem + Item::items[Tile::stoneBrickSmooth_Id] = ( new SmoothStoneBrickTileItem(Tile::stoneBrickSmooth_Id - 256, stoneBrickSmooth))->setTextureName(L"stonebricksmooth")->setDescriptionId(IDS_TILE_STONE_BRICK_SMOOTH); + Item::items[Tile::sandStone_Id] = ( new MultiTextureTileItem(sandStone_Id - 256, sandStone, SandStoneTile::SANDSTONE_NAMES, SandStoneTile::SANDSTONE_BLOCK_NAMES) )->setTextureName(L"sandStone")->setDescriptionId(IDS_TILE_SANDSTONE)->setUseDescriptionId(IDS_DESC_SANDSTONE); + Item::items[Tile::quartzBlock_Id] = ( new MultiTextureTileItem(quartzBlock_Id - 256, quartzBlock, QuartzBlockTile::BLOCK_NAMES, QuartzBlockTile::QUARTZ_BLOCK_NAMES) )->setTextureName(L"quartzBlock")->setDescriptionId(IDS_TILE_QUARTZ_BLOCK)->setUseDescriptionId(IDS_DESC_QUARTZ_BLOCK); + Item::items[Tile::stoneSlabHalf_Id] = ( new StoneSlabTileItem(Tile::stoneSlabHalf_Id - 256, Tile::stoneSlabHalf, Tile::stoneSlab, false) )->setTextureName(L"stoneSlab")->setDescriptionId(IDS_TILE_STONESLAB)->setUseDescriptionId(IDS_DESC_HALFSLAB); + Item::items[Tile::stoneSlab_Id] = ( new StoneSlabTileItem(Tile::stoneSlab_Id - 256, Tile::stoneSlabHalf, Tile::stoneSlab, true))->setTextureName(L"stoneSlab")->setDescriptionId(IDS_DESC_STONESLAB)->setUseDescriptionId(IDS_DESC_SLAB); + Item::items[Tile::woodSlabHalf_Id] = ( new StoneSlabTileItem(Tile::woodSlabHalf_Id - 256, Tile::woodSlabHalf, Tile::woodSlab, false))->setTextureName(L"woodSlab")->setDescriptionId(IDS_DESC_WOODSLAB)->setUseDescriptionId(IDS_DESC_WOODSLAB); + Item::items[Tile::woodSlab_Id] = ( new StoneSlabTileItem(Tile::woodSlab_Id - 256, Tile::woodSlabHalf, Tile::woodSlab, true))->setTextureName(L"woodSlab")->setDescriptionId(IDS_DESC_WOODSLAB)->setUseDescriptionId(IDS_DESC_WOODSLAB); + Item::items[Tile::sapling_Id] = ( new SaplingTileItem(Tile::sapling_Id - 256) )->setTextureName(L"sapling")->setDescriptionId(IDS_TILE_SAPLING)->setUseDescriptionId(IDS_DESC_SAPLING); + Item::items[Tile::leaves_Id] = ( new LeafTileItem(Tile::leaves_Id - 256) )->setTextureName(L"leaves")->setDescriptionId(IDS_TILE_LEAVES)->setUseDescriptionId(IDS_DESC_LEAVES); + Item::items[Tile::vine_Id] = ( new ColoredTileItem(Tile::vine_Id - 256, false))->setDescriptionId(IDS_TILE_VINE)->setUseDescriptionId(IDS_DESC_VINE); + int idsData[3] = {IDS_TILE_SHRUB, IDS_TILE_GRASS, IDS_TILE_FERN}; + intArray ids = intArray(idsData, 3); + Item::items[Tile::tallgrass_Id] = ((ColoredTileItem *)(new ColoredTileItem(Tile::tallgrass_Id - 256, true))->setDescriptionId(IDS_TILE_TALL_GRASS))->setDescriptionPostfixes(ids); + Item::items[Tile::waterLily_Id] = ( new WaterLilyTileItem(Tile::waterLily_Id - 256)); + Item::items[Tile::pistonBase_Id] = ( new PistonTileItem(Tile::pistonBase_Id - 256) )->setDescriptionId(IDS_TILE_PISTON_BASE)->setUseDescriptionId(IDS_DESC_PISTON); + Item::items[Tile::pistonStickyBase_Id] = ( new PistonTileItem(Tile::pistonStickyBase_Id - 256) )->setDescriptionId(IDS_TILE_PISTON_STICK_BASE)->setUseDescriptionId(IDS_DESC_STICKY_PISTON); + Item::items[Tile::cobbleWall_Id] = ( new MultiTextureTileItem(cobbleWall_Id - 256, cobbleWall, (int *)WallTile::COBBLE_NAMES, 2) )->setDescriptionId(IDS_TILE_COBBLESTONE_WALL)->setUseDescriptionId(IDS_DESC_COBBLESTONE_WALL); + Item::items[Tile::anvil_Id] = ( new AnvilTileItem(anvil) )->setDescriptionId(IDS_TILE_ANVIL)->setUseDescriptionId(IDS_DESC_ANVIL); + + + for (int i = 0; i < 256; i++) + { + if ( Tile::tiles[i] != NULL ) + { + if( Item::items[i] == NULL) + { + Item::items[i] = new TileItem(i - 256); + Tile::tiles[i]->init(); + } + + bool propagate = false; + if (i > 0 && Tile::tiles[i]->getRenderShape() == Tile::SHAPE_STAIRS) propagate = true; + if (i > 0 && dynamic_cast(Tile::tiles[i]) != NULL) + { + propagate = true; + } + if (i == Tile::farmland_Id) propagate = true; + if (Tile::transculent[i]) + { + propagate = true; + } + if (Tile::lightBlock[i] == 0) + { + propagate = true; + } + Tile::propagate[i] = propagate; + } + } + Tile::transculent[0] = true; + + Stats::buildItemStats(); + + // */ +} + +// 4J - added for common ctor code +void Tile::_init(int id, Material *material, bool isSolidRender) +{ + destroySpeed = 0.0f; + explosionResistance = 0.0f; + isInventoryItem = true; + collectStatistics = true; + + // 4J Stu - Removed these in favour of TLS versions + //xx0 = yy0 = zz0 = xx1 = yy1 = zz1 = 0; + + soundType = Tile::SOUND_NORMAL; + gravity = 1.0f; + friction = 0.6f; + _isTicking = false; + _isEntityTile = false; + + /* 4J - TODO + if (Tile.tiles[id] != null) + { + throw new IllegalArgumentException("Slot " + id + " is already occupied by " + Tile.tiles[id] + " when adding " + this); + } + */ + this->material = material; + Tile::tiles[id] = this; + this->id = id; + updateDefaultShape(); + // 4J - note these used to call isSolidRender(), but that always calls Tile::isSolidRender in C++ so have added as a parameter that can be varied from + // derived ctors + solid[id] = isSolidRender; + lightBlock[id] = isSolidRender ? 255 : 0; + transculent[id] = !material->blocksLight(); + mipmapEnable[id] = true; // 4J added + m_textureName = L""; +} + +Tile::Tile(int id, Material *material, bool isSolidRender) +{ + _init(id,material, isSolidRender); + m_iMaterial=Item::eMaterial_undefined; + m_iBaseItemType=Item::eBaseItemType_undefined; + icon = NULL; +} + +Tile *Tile::sendTileData(unsigned char importantMask/*=15*/) +{ + Tile::_sendTileData[id] = importantMask; // 4J - changed was bool, now bitfield to indicate which bits are important to be sent. Default behaviour with this method is all 4 bits + return this; +} + +void Tile::init() +{ +} + + + +// 4J-PB - adding so we can class different items together for the new crafting menu +// so pickaxe_stone would get tagged with pickaxe and stone +Tile *Tile::setBaseItemTypeAndMaterial(int iType,int iMaterial) +{ + this->m_iBaseItemType = iType; + this->m_iMaterial = iMaterial; + return this; +} + +int Tile::getBaseItemType() +{ + return this->m_iBaseItemType; +} + +int Tile::getMaterial() +{ + return this->m_iMaterial; +} + + +Tile *Tile::setSoundType(const SoundType *soundType) +{ + this->soundType = soundType; + return this; +} + +Tile *Tile::setLightBlock(int i) +{ + lightBlock[id] = i; + return this; +} + +Tile *Tile::setLightEmission(float f) +{ + Tile::lightEmission[id] = (int) (Level::MAX_BRIGHTNESS * f); + return this; +} + +Tile *Tile::setExplodeable(float explosionResistance) +{ + this->explosionResistance = explosionResistance * 3; + return this; +} + +bool Tile::isSolidBlockingTile(int t) +{ + Tile *tile = Tile::tiles[t]; + if (tile == NULL) return false; + return tile->material->isSolidBlocking() && tile->isCubeShaped(); +} + +bool Tile::isCubeShaped() +{ + return true; +} + +bool Tile::isPathfindable(LevelSource *level, int x, int y, int z) +{ + return !material->blocksMotion(); +} + +int Tile::getRenderShape() +{ + return SHAPE_BLOCK; +} + +Tile *Tile::setDestroyTime(float destroySpeed) +{ + this->destroySpeed = destroySpeed; + if (explosionResistance < destroySpeed * 5) explosionResistance = destroySpeed * 5; + return this; +} + +Tile *Tile::setIndestructible() +{ + setDestroyTime(INDESTRUCTIBLE_DESTROY_TIME); + return this; +} + +float Tile::getDestroySpeed(Level *level, int x, int y, int z) +{ + return destroySpeed; +} + +Tile *Tile::setTicking(bool tick) +{ + _isTicking = tick; + return this; +} + +bool Tile::isTicking() +{ + return _isTicking; +} + +bool Tile::isEntityTile() +{ + return _isEntityTile; +} + +Tile *Tile::disableMipmap() +{ + mipmapEnable[id] = false; + return this; +} + +void Tile::setShape(float x0, float y0, float z0, float x1, float y1, float z1) +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + tls->xx0 = x0; + tls->yy0 = y0; + tls->zz0 = z0; + tls->xx1 = x1; + tls->yy1 = y1; + tls->zz1 = z1; + tls->tileId = this->id; + + //this->xx0 = x0; + //this->yy0 = y0; + //this->zz0 = z0; + //this->xx1 = x1; + //this->yy1 = y1; + //this->zz1 = z1; +} + +float Tile::getBrightness(LevelSource *level, int x, int y, int z) +{ + // Lighting fix brought forward from ~1.5 here - used to use the lightEmission level for this tile rather than getting the for the passed in x/y/z coords + return level->getBrightness(x, y, z, Tile::lightEmission[level->getTile(x,y,z)]); +} + +// 4J - brought forward from 1.8.2 +int Tile::getLightColor(LevelSource *level, int x, int y, int z, int tileId/*=-1*/) +{ + // Lighting fix brought forward from ~1.5 here - used to use the lightEmission level for this tile rather than getting the for the passed in x/y/z coords + if( tileId == -1 ) + { + return level->getLightColor(x, y, z, Tile::lightEmission[level->getTile(x,y,z)], -1); + } + else + { + return level->getLightColor(x, y, z, Tile::lightEmission[tileId], tileId); + } +} + +bool Tile::isFaceVisible(Level *level, int x, int y, int z, int f) +{ + if (f == 0) y--; + if (f == 1) y++; + if (f == 2) z--; + if (f == 3) z++; + if (f == 4) x--; + if (f == 5) x++; + return !level->isSolidRenderTile(x, y, z); +} + +bool Tile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + if (face == 0 && tls->yy0 > 0) return true; + if (face == 1 && tls->yy1 < 1) return true; + if (face == 2 && tls->zz0 > 0) return true; + if (face == 3 && tls->zz1 < 1) return true; + if (face == 4 && tls->xx0 > 0) return true; + if (face == 5 && tls->xx1 < 1) return true; + return (!level->isSolidRenderTile(x, y, z)); +} + +// AP - added this function so we can generate the faceFlags for a block in a single fast function +int Tile::getFaceFlags(LevelSource *level, int x, int y, int z) +{ + int faceFlags = 0; + + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + + if( tls->yy0 > 0 || (!level->isSolidRenderTile(x, y - 1, z))) faceFlags |= 0x01; + if( tls->yy1 < 1 || (!level->isSolidRenderTile(x, y + 1, z))) faceFlags |= 0x02; + if( tls->zz0 > 0 || (!level->isSolidRenderTile(x, y, z - 1))) faceFlags |= 0x04; + if( tls->zz1 < 1 || (!level->isSolidRenderTile(x, y, z + 1))) faceFlags |= 0x08; + if( tls->xx0 > 0 || (!level->isSolidRenderTile(x - 1, y, z))) faceFlags |= 0x10; + if( tls->xx1 < 1 || (!level->isSolidRenderTile(x + 1, y, z))) faceFlags |= 0x20; + + return faceFlags; +} + +bool Tile::isSolidFace(LevelSource *level, int x, int y, int z, int face) +{ + return (level->getMaterial(x, y, z)->isSolid()); +} + +Icon *Tile::getTexture(LevelSource *level, int x, int y, int z, int face) +{ + // 4J - addition here to make rendering big blocks of leaves more efficient. Normally leaves never consider themselves as solid, so + // blocks of leaves will have all sides of each block completely visible. Changing to consider as solid if this block is surrounded by + // other leaves (or solid things). This is paired with another change in Level::isSolidRenderTile/Region::isSolidRenderTile which makes things solid + // code-wise (ie for determining visible sides of neighbouring blocks). This change just makes the texture a solid one (tex + 1) which + // we already have in the texture map for doing non-fancy graphics. Note: this tile-specific code is here rather than making some new virtual + // method in the tiles, for the sake of efficiency - I don't imagine we'll be doing much more of this sort of thing + + int tileId = level->getTile(x, y, z); + int tileData = level->getData(x, y, z); + + if( tileId == Tile::leaves_Id ) + { + bool opaque = true; + + int axo[6] = { 1,-1, 0, 0, 0, 0}; + int ayo[6] = { 0, 0, 1,-1, 0, 0}; + int azo[6] = { 0, 0, 0, 0, 1,-1}; + for( int i = 0; (i < 6) && opaque; i++ ) + { + int t = level->getTile(x + axo[i], y + ayo[i] , z + azo[i]); + if( ( t != Tile::leaves_Id ) && ( ( Tile::tiles[t] == NULL ) || !Tile::tiles[t]->isSolidRender() ) ) + { + opaque = false; + } + } + + Icon *icon = NULL; + if(opaque) + { + Tile::leaves->setFancy(false); + icon = getTexture(face, tileData); + Tile::leaves->setFancy(true); + } + else + { + icon = getTexture(face, tileData); + } + return icon; + } + return getTexture(face, tileData); +} + +Icon *Tile::getTexture(int face, int data) +{ + return icon; +} + +Icon *Tile::getTexture(int face) +{ + return getTexture(face, 0); +} + +AABB *Tile::getTileAABB(Level *level, int x, int y, int z) +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return AABB::newTemp(x + tls->xx0, y + tls->yy0, z + tls->zz0, x + tls->xx1, y + tls->yy1, z + tls->zz1); +} + +void Tile::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source) +{ + AABB *aabb = getAABB(level, x, y, z); + if (aabb != NULL && box->intersects(aabb)) boxes->push_back(aabb); +} + +AABB *Tile::getAABB(Level *level, int x, int y, int z) +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return AABB::newTemp(x + tls->xx0, y + tls->yy0, z + tls->zz0, x + tls->xx1, y + tls->yy1, z + tls->zz1); +} + +bool Tile::isSolidRender(bool isServerLevel) +{ + return true; +} + +bool Tile::mayPick(int data, bool liquid) +{ + return mayPick(); +} + +bool Tile::mayPick() +{ + return true; +} + +void Tile::tick(Level *level, int x, int y, int z, Random *random) +{ +} + +void Tile::animateTick(Level *level, int x, int y, int z, Random *random) +{ +} + +void Tile::destroy(Level *level, int x, int y, int z, int data) +{ +} + +void Tile::neighborChanged(Level *level, int x, int y, int z, int type) +{ +} + +void Tile::addLights(Level *level, int x, int y, int z) +{ +} + +int Tile::getTickDelay() +{ + return 10; +} + +void Tile::onPlace(Level *level, int x, int y, int z) +{ +} + +void Tile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ +} + +int Tile::getResourceCount(Random *random) +{ + return 1; +} + +int Tile::getResource(int data, Random *random, int playerBonusLevel) +{ + return id; +} + +float Tile::getDestroyProgress(shared_ptr player, Level *level, int x, int y, int z) +{ + float destroySpeed = getDestroySpeed(level, x, y, z); + if (destroySpeed < 0) return 0; + if (!player->canDestroy(this)) return 1 / destroySpeed / 100.0f; + return (player->getDestroySpeed(this) / destroySpeed) / 30; +} + +void Tile::spawnResources(Level *level, int x, int y, int z, int data, int playerBonusLevel) +{ + spawnResources(level, x, y, z, data, 1, playerBonusLevel); +} + +void Tile::spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel) +{ + if (level->isClientSide) return; + int count = getResourceCountForLootBonus(playerBonusLevel, level->random); + for (int i = 0; i < count; i++) + { + if (level->random->nextFloat() > odds) continue; + int type = getResource(data, level->random, playerBonusLevel); + if (type <= 0) continue; + + popResource(level, x, y, z, shared_ptr( new ItemInstance(type, 1, getSpawnResourcesAuxValue(data) ) ) ); + } +} + +void Tile::popResource(Level *level, int x, int y, int z, shared_ptr itemInstance) +{ + if( level->isClientSide ) return; + + float s = 0.7f; + double xo = level->random->nextFloat() * s + (1 - s) * 0.5; + double yo = level->random->nextFloat() * s + (1 - s) * 0.5; + double zo = level->random->nextFloat() * s + (1 - s) * 0.5; + shared_ptr item = shared_ptr( new ItemEntity(level, x + xo, y + yo, z + zo, itemInstance ) ); + item->throwTime = 10; + level->addEntity(item); +} + +// Brought forward for TU7 +void Tile::popExperience(Level *level, int x, int y, int z, int amount) +{ + if (!level->isClientSide) + { + while (amount > 0) + { + int newCount = ExperienceOrb::getExperienceValue(amount); + amount -= newCount; + level->addEntity(shared_ptr( new ExperienceOrb(level, x + .5, y + .5, z + .5, newCount))); + } + } +} + +int Tile::getSpawnResourcesAuxValue(int data) +{ + return 0; +} + +float Tile::getExplosionResistance(shared_ptr source) +{ + return explosionResistance / 5.0f; +} + +HitResult *Tile::clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b) +{ + updateShape(level, xt, yt, zt); + + a = a->add(-xt, -yt, -zt); + b = b->add(-xt, -yt, -zt); + + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + Vec3 *xh0 = a->clipX(b, tls->xx0); + Vec3 *xh1 = a->clipX(b, tls->xx1); + + Vec3 *yh0 = a->clipY(b, tls->yy0); + Vec3 *yh1 = a->clipY(b, tls->yy1); + + Vec3 *zh0 = a->clipZ(b, tls->zz0); + Vec3 *zh1 = a->clipZ(b, tls->zz1); + + Vec3 *closest = NULL; + + if (containsX(xh0) && (closest == NULL || a->distanceToSqr(xh0) < a->distanceToSqr(closest))) closest = xh0; + if (containsX(xh1) && (closest == NULL || a->distanceToSqr(xh1) < a->distanceToSqr(closest))) closest = xh1; + if (containsY(yh0) && (closest == NULL || a->distanceToSqr(yh0) < a->distanceToSqr(closest))) closest = yh0; + if (containsY(yh1) && (closest == NULL || a->distanceToSqr(yh1) < a->distanceToSqr(closest))) closest = yh1; + if (containsZ(zh0) && (closest == NULL || a->distanceToSqr(zh0) < a->distanceToSqr(closest))) closest = zh0; + if (containsZ(zh1) && (closest == NULL || a->distanceToSqr(zh1) < a->distanceToSqr(closest))) closest = zh1; + + if (closest == NULL) return NULL; + + int face = -1; + + if (closest == xh0) face = Facing::WEST; + if (closest == xh1) face = Facing::EAST; + if (closest == yh0) face = Facing::DOWN; + if (closest == yh1) face = Facing::UP; + if (closest == zh0) face = Facing::NORTH; + if (closest == zh1) face = Facing::SOUTH; + + return new HitResult(xt, yt, zt, face, closest->add(xt, yt, zt)); +} + +bool Tile::containsX(Vec3 *v) +{ + if( v == NULL) return false; + + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return v->y >= tls->yy0 && v->y <= tls->yy1 && v->z >= tls->zz0 && v->z <= tls->zz1; +} + +bool Tile::containsY(Vec3 *v) +{ + if( v == NULL) return false; + + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return v->x >= tls->xx0 && v->x <= tls->xx1 && v->z >= tls->zz0 && v->z <= tls->zz1; +} + +bool Tile::containsZ(Vec3 *v) +{ + if( v == NULL) return false; + + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return v->x >= tls->xx0 && v->x <= tls->xx1 && v->y >= tls->yy0 && v->y <= tls->yy1; +} + +void Tile::wasExploded(Level *level, int x, int y, int z) +{ +} + +int Tile::getRenderLayer() +{ + return 0; +} + +bool Tile::mayPlace(Level *level, int x, int y, int z, int face) +{ + return mayPlace(level, x, y, z); +} + +bool Tile::mayPlace(Level *level, int x, int y, int z) +{ + int t = level->getTile(x, y, z); + return t == 0 || Tile::tiles[t]->material->isReplaceable(); +} + +// 4J-PB - Adding a TestUse for tooltip display +bool Tile::TestUse() +{ + return false; +} + +bool Tile::TestUse(Level *level, int x, int y, int z, shared_ptr player) +{ + return false; +} + +bool Tile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + return false; +} + +void Tile::stepOn(Level *level, int x, int y, int z, shared_ptr entity) +{ +} + +int Tile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) +{ + return itemValue; +} + +void Tile::prepareRender(Level *level, int x, int y, int z) +{ +} + +void Tile::attack(Level *level, int x, int y, int z, shared_ptr player) +{ +} + +void Tile::handleEntityInside(Level *level, int x, int y, int z, shared_ptr e, Vec3 *current) +{ +} + +void Tile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); +} + +double Tile::getShapeX0() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return tls->xx0; +} + +double Tile::getShapeX1() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return tls->xx1; +} + +double Tile::getShapeY0() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return tls->yy0; +} + +double Tile::getShapeY1() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return tls->yy1; +} + +double Tile::getShapeZ0() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return tls->zz0; +} + +double Tile::getShapeZ1() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return tls->zz1; +} + +int Tile::getColor() const +{ + return 0xffffff; +} + +int Tile::getColor(int auxData) +{ + return 0xffffff; +} + +int Tile::getColor(LevelSource *level, int x, int y, int z) +{ + return 0xffffff; +} + +int Tile::getColor(LevelSource *level, int x, int y, int z, int data) +{ + return 0xffffff; +} + +bool Tile::getSignal(LevelSource *level, int x, int y, int z) +{ + return false; +} + +bool Tile::getSignal(LevelSource *level, int x, int y, int z, int dir) +{ + return false; +} + +bool Tile::isSignalSource() +{ + return false; +} + +void Tile::entityInside(Level *level, int x, int y, int z, shared_ptr entity) +{ +} + +bool Tile::getDirectSignal(Level *level, int x, int y, int z, int dir) +{ + return false; +} + +void Tile::updateDefaultShape() +{ + setShape(0,0,0,1,1,1); +} + +void Tile::playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data) +{ + // 4J Stu - Special case - only record a crop destroy if is fully grown + if( id==Tile::crops_Id ) + { + if( Tile::crops->getResource(data, NULL, 0) > 0 ) + player->awardStat( + GenericStats::blocksMined(id), + GenericStats::param_blocksMined(id,data,1) + ); + } + else if (id == Tile::potatoes_Id) + { + if (Tile::potatoes->getResource(data, NULL, 0) > 0) + player->awardStat( + GenericStats::blocksMined(id), + GenericStats::param_blocksMined(id,data,1) + ); + } + else if (id == Tile::carrots_Id) + { + if (Tile::potatoes->getResource(data, NULL, 0) > 0) + player->awardStat( + GenericStats::blocksMined(id), + GenericStats::param_blocksMined(id,data,1) + ); + } + else + { + player->awardStat( + GenericStats::blocksMined(id), + GenericStats::param_blocksMined(id,data,1) + ); + } + player->awardStat(GenericStats::totalBlocksMined(), GenericStats::param_noArgs()); // 4J : WESTY : Added for other award. + player->causeFoodExhaustion(FoodConstants::EXHAUSTION_MINE); + + if( id == Tile::treeTrunk_Id ) + player->awardStat(GenericStats::mineWood(), GenericStats::param_noArgs()); + + + if (isSilkTouchable() && EnchantmentHelper::hasSilkTouch(player->inventory)) + { + shared_ptr item = getSilkTouchItemInstance(data); + if (item != NULL) + { + popResource(level, x, y, z, item); + } + } + else + { + int playerBonusLevel = EnchantmentHelper::getDiggingLootBonus(player->inventory); + spawnResources(level, x, y, z, data, playerBonusLevel); + } +} + +bool Tile::isSilkTouchable() +{ + return isCubeShaped() && !_isEntityTile; +} + +shared_ptr Tile::getSilkTouchItemInstance(int data) +{ + int popData = 0; + if (id >= 0 && id < Item::items.length && Item::items[id]->isStackedByData()) + { + popData = data; + } + return shared_ptr(new ItemInstance(id, 1, popData)); +} + +int Tile::getResourceCountForLootBonus(int bonusLevel, Random *random) +{ + return getResourceCount(random); +} + +bool Tile::canSurvive(Level *level, int x, int y, int z) +{ + return true; +} + +void Tile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ +} + +void Tile::finalizePlacement(Level *level, int x, int y, int z, int data) +{ +} + +Tile *Tile::setDescriptionId(unsigned int id) +{ + this->descriptionId = id; + return this; +} + +wstring Tile::getName() +{ + return L"";//I18n::get(getDescriptionId() + L".name"); +} + +unsigned int Tile::getDescriptionId(int iData /*= -1*/) +{ + return descriptionId; +} + +Tile *Tile::setUseDescriptionId(unsigned int id) +{ + this->useDescriptionId = id; + return this; +} + +unsigned int Tile::getUseDescriptionId() +{ + return useDescriptionId; +} + +void Tile::triggerEvent(Level *level, int x, int y, int z, int b0, int b1) +{ +} + +bool Tile::isCollectStatistics() +{ + return collectStatistics; +} + +Tile *Tile::setNotCollectStatistics() +{ + collectStatistics = false; + return this; +} + +int Tile::getPistonPushReaction() +{ + return material->getPushReaction(); +} + +// 4J - brought forward from 1.8.2 +float Tile::getShadeBrightness(LevelSource *level, int x, int y, int z) +{ + return level->isSolidBlockingTile(x, y, z) ? 0.2f : 1.0f; +} + +void Tile::fallOn(Level *level, int x, int y, int z, shared_ptr entity, float fallDistance) +{ +} + +int Tile::cloneTileId(Level *level, int x, int y, int z) +{ + return id; +} + +int Tile::cloneTileData(Level *level, int x, int y, int z) +{ + return getSpawnResourcesAuxValue(level->getData(x, y, z)); +} + +void Tile::playerWillDestroy(Level *level, int x, int y, int z, int data, shared_ptr player) +{ +} + +void Tile::onRemoving(Level *level, int x, int y, int z, int data) +{ +} + +void Tile::handleRain(Level *level, int x, int y, int z) +{ +} + + void Tile::levelTimeChanged(Level *level, __int64 delta, __int64 newTime) +{ +} + +void Tile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(m_textureName); +} + +wstring Tile::getTileItemIconName() +{ + return L""; +} + +Tile *Tile::setTextureName(const wstring &name) +{ + m_textureName = name; + return this; +} + +Tile::SoundType::SoundType(eMATERIALSOUND_TYPE eMaterialSound, float volume, float pitch, int iBreakSound, int iPlaceSound) +{ + this->eMaterialSound = eMaterialSound; + if( iBreakSound>-1 ) + { + this->iBreakSound = iBreakSound; + } + else + { + switch(eMaterialSound) + { + case eMaterialSoundType_STONE: + this->iBreakSound=eSoundType_DIG_STONE; + break; + case eMaterialSoundType_WOOD: + this->iBreakSound=eSoundType_DIG_WOOD; + break; + case eMaterialSoundType_GRAVEL: + this->iBreakSound=eSoundType_DIG_GRAVEL; + break; + case eMaterialSoundType_GRASS: + this->iBreakSound=eSoundType_DIG_GRASS; + break; + case eMaterialSoundType_METAL: + this->iBreakSound=eSoundType_DIG_STONE; + break; + case eMaterialSoundType_GLASS: + this->iBreakSound=eSoundType_RANDOM_GLASS; + break; + case eMaterialSoundType_CLOTH: + this->iBreakSound=eSoundType_DIG_CLOTH; + break; + case eMaterialSoundType_SAND: + this->iBreakSound=eSoundType_DIG_SAND; + break; + case eMaterialSoundType_SNOW: + this->iBreakSound=eSoundType_DIG_SNOW; + break; + case eMaterialSoundType_LADDER: + this->iBreakSound=eSoundType_DIG_WOOD; + break; + default: + app.DebugPrintf("NO BREAK SOUND!\n"); + this->iBreakSound=-1; + break; + } + //this->breakSound = L"step." + this->name; + } + + if( iPlaceSound>-1 ) + { + this->iPlaceSound = iPlaceSound; + } + else + { + this->iPlaceSound = this->iBreakSound; + } + + switch(eMaterialSound) + { + case eMaterialSoundType_STONE: + this->iStepSound=eSoundType_STEP_STONE; + break; + case eMaterialSoundType_WOOD: + this->iStepSound=eSoundType_STEP_WOOD; + break; + case eMaterialSoundType_GRAVEL: + this->iStepSound=eSoundType_STEP_GRAVEL; + break; + case eMaterialSoundType_GRASS: + this->iStepSound=eSoundType_STEP_GRASS; + break; + case eMaterialSoundType_METAL: + this->iStepSound=eSoundType_STEP_METAL; + break; + case eMaterialSoundType_CLOTH: + this->iStepSound=eSoundType_STEP_CLOTH; + break; + case eMaterialSoundType_SAND: + this->iStepSound=eSoundType_STEP_SAND; + break; + case eMaterialSoundType_SNOW: + this->iStepSound=eSoundType_STEP_SNOW; + break; + case eMaterialSoundType_LADDER: + this->iStepSound=eSoundType_STEP_LADDER; + break; + default: + app.DebugPrintf("NO STEP SOUND!\n"); + + this->iStepSound=-1; + break; + + } + + //this->stepSound = L"step." + this->name; + this->volume = volume; + this->pitch = pitch; +} + +float Tile::SoundType::getVolume() const +{ + return volume; +} +float Tile::SoundType::getPitch() const +{ + return pitch; +} +//wstring getBreakSound() const { return breakSound; } +//wstring getStepSound() const { return stepSound; } +int Tile::SoundType::getBreakSound() const +{ + return iBreakSound; +} +int Tile::SoundType::getStepSound() const +{ + return iStepSound; +} +int Tile::SoundType::getPlaceSound() const +{ + return iPlaceSound; +} + + +/* + 4J: These are necessary on the PS3. + (and 4 and Vita). +*/ +#if (defined __PS3__ || defined __ORBIS__ || defined __PSVITA__) +const int Tile::rock_Id; +const int Tile::grass_Id; +const int Tile::dirt_Id; +const int Tile::stoneBrick_Id; +const int Tile::wood_Id; +const int Tile::sapling_Id; +const int Tile::unbreakable_Id; +const int Tile::water_Id; +const int Tile::calmWater_Id; +const int Tile::lava_Id; +const int Tile::calmLava_Id; +const int Tile::sand_Id; +const int Tile::gravel_Id; +const int Tile::goldOre_Id; +const int Tile::ironOre_Id; +const int Tile::coalOre_Id; +const int Tile::treeTrunk_Id; +const int Tile::leaves_Id; +const int Tile::sponge_Id; +const int Tile::glass_Id; +const int Tile::lapisOre_Id; +const int Tile::lapisBlock_Id; +const int Tile::dispenser_Id; +const int Tile::sandStone_Id; +const int Tile::musicBlock_Id; +const int Tile::bed_Id; +const int Tile::goldenRail_Id; +const int Tile::detectorRail_Id; +const int Tile::pistonStickyBase_Id; +const int Tile::web_Id; +const int Tile::tallgrass_Id; +const int Tile::deadBush_Id; +const int Tile::pistonBase_Id; +const int Tile::pistonExtensionPiece_Id; +const int Tile::cloth_Id; +const int Tile::pistonMovingPiece_Id; +const int Tile::flower_Id; +const int Tile::rose_Id; +const int Tile::mushroom1_Id; +const int Tile::mushroom2_Id; +const int Tile::goldBlock_Id; +const int Tile::ironBlock_Id; +const int Tile::stoneSlab_Id; +const int Tile::stoneSlabHalf_Id; +const int Tile::redBrick_Id; +const int Tile::tnt_Id; +const int Tile::bookshelf_Id; +const int Tile::mossStone_Id; +const int Tile::obsidian_Id; +const int Tile::torch_Id; +const int Tile::fire_Id; +const int Tile::mobSpawner_Id; +const int Tile::stairs_wood_Id; +const int Tile::chest_Id; +const int Tile::redStoneDust_Id; +const int Tile::diamondOre_Id; +const int Tile::diamondBlock_Id; +const int Tile::workBench_Id; +const int Tile::crops_Id; +const int Tile::farmland_Id; +const int Tile::furnace_Id; +const int Tile::furnace_lit_Id; +const int Tile::sign_Id; +const int Tile::door_wood_Id; +const int Tile::ladder_Id; +const int Tile::rail_Id; +const int Tile::stairs_stone_Id; +const int Tile::wallSign_Id; +const int Tile::lever_Id; +const int Tile::pressurePlate_stone_Id; +const int Tile::door_iron_Id; +const int Tile::pressurePlate_wood_Id; +const int Tile::redStoneOre_Id; +const int Tile::redStoneOre_lit_Id; +const int Tile::notGate_off_Id; +const int Tile::notGate_on_Id; +const int Tile::button_stone_Id; +const int Tile::topSnow_Id; +const int Tile::ice_Id; +const int Tile::snow_Id; +const int Tile::cactus_Id; +const int Tile::clay_Id; +const int Tile::reeds_Id; +const int Tile::recordPlayer_Id; +const int Tile::fence_Id; +const int Tile::pumpkin_Id; +const int Tile::hellRock_Id; +const int Tile::hellSand_Id; +const int Tile::lightGem_Id; +const int Tile::portalTile_Id; +const int Tile::litPumpkin_Id; +const int Tile::cake_Id; +const int Tile::diode_off_Id; +const int Tile::diode_on_Id; +const int Tile::aprilFoolsJoke_Id; +const int Tile::trapdoor_Id; +const int Tile::monsterStoneEgg_Id; +const int Tile::stoneBrickSmooth_Id; +const int Tile::hugeMushroom1_Id; +const int Tile::hugeMushroom2_Id; +const int Tile::ironFence_Id; +const int Tile::thinGlass_Id; +const int Tile::melon_Id; +const int Tile::pumpkinStem_Id; +const int Tile::melonStem_Id; +const int Tile::vine_Id; +const int Tile::fenceGate_Id; +const int Tile::stairs_bricks_Id; +const int Tile::stairs_stoneBrickSmooth_Id; +const int Tile::mycel_Id; +const int Tile::waterLily_Id; +const int Tile::netherBrick_Id; +const int Tile::netherFence_Id; +const int Tile::stairs_netherBricks_Id; +const int Tile::netherStalk_Id; +const int Tile::enchantTable_Id; +const int Tile::brewingStand_Id; +const int Tile::cauldron_Id; +const int Tile::endPortalTile_Id; +const int Tile::endPortalFrameTile_Id; +const int Tile::whiteStone_Id; +const int Tile::dragonEgg_Id; +const int Tile::redstoneLight_Id; +const int Tile::redstoneLight_lit_Id; +const int Tile::woodSlab_Id; +const int Tile::woodSlabHalf_Id; +const int Tile::cocoa_Id; +const int Tile::stairs_sandstone_Id; +const int Tile::stairs_sprucewood_Id; +const int Tile::stairs_birchwood_Id; +const int Tile::stairs_junglewood_Id; +const int Tile::emeraldOre_Id; +const int Tile::enderChest_Id; +const int Tile::tripWireSource_Id; +const int Tile::tripWire_Id; +const int Tile::emeraldBlock_Id; +const int Tile::cobbleWall_Id; +const int Tile::flowerPot_Id; +const int Tile::carrots_Id; +const int Tile::potatoes_Id; +const int Tile::anvil_Id; +const int Tile::button_wood_Id; +const int Tile::skull_Id; +const int Tile::netherQuartz_Id; +const int Tile::quartzBlock_Id; +const int Tile::stairs_quartz_Id; +const int Tile::woolCarpet_Id; +#endif diff --git a/Minecraft.World/Tile.h b/Minecraft.World/Tile.h new file mode 100644 index 00000000..fa2f5786 --- /dev/null +++ b/Minecraft.World/Tile.h @@ -0,0 +1,648 @@ +#pragma once +#include "Material.h" +#include "Vec3.h" +#include "Definitions.h" +#include "SoundTypes.h" +using namespace std; + +class GrassTile; +class LeafTile; +class TallGrass; +class DeadBushTile; +class FireTile; +class PortalTile; +class MycelTile; +class PistonExtensionTile; +class PistonMovingPiece; +class StoneTile; +class stoneBrick; +class Bush; +class StairTile; +class LiquidTile; +class PistonBaseTile; +class ChestTile; +class RedStoneDustTile; +class RepeaterTile; +class CauldronTile; +class TripWireSourceTile; +class Random; +class HitResult; +class Level; + +class Player; +class LevelSource; +class Mob; +class TileEntity; +class HalfSlabTile; +class Icon; +class IconRegister; + +class ChunkRebuildData; + +class Tile +{ + // 4J Stu - Stair tile accesses the protected members of a Tile object passed in + friend class StairTile; + friend class ChunkRebuildData; + friend class WallTile; + +protected: + // 4J added so we can have separate shapes for different threads + class ThreadStorage + { + public: + double xx0, yy0, zz0, xx1, yy1, zz1; + int tileId; + ThreadStorage(); + }; + static DWORD tlsIdxShape; +public: + // Each new thread that needs to use Vec3 pools will need to call one of the following 2 functions, to either create its own + // local storage, or share the default storage already allocated by the main thread + static void CreateNewThreadStorage(); + static void ReleaseThreadStorage(); + +public: + static const int TILE_NUM_COUNT = 4096; + static const int TILE_NUM_MASK = 0xfff; // 4096 - 1 + static const int TILE_NUM_SHIFT = 12; // 4096 is 12 bits + +private: + // 4J Stu - Was const but had to change it so that we can initialise it in TileStaticInit + static wstring TILE_DESCRIPTION_PREFIX; +protected: + static const float INDESTRUCTIBLE_DESTROY_TIME; + +public: + + class SoundType + { + public: +// wstring name; +// wstring breakSound; +// wstring stepSound; + eMATERIALSOUND_TYPE eMaterialSound; + int iBreakSound,iStepSound,iPlaceSound; + float volume; + float pitch; + + SoundType(eMATERIALSOUND_TYPE eMaterialSound, float volume, float pitch, int iBreakSound = -1, int iPlaceSound = -1); + + float getVolume() const; + float getPitch() const; + //wstring getBreakSound() const { return breakSound; } + //wstring getStepSound() const { return stepSound; } + int getBreakSound() const; + int getStepSound() const; + int getPlaceSound() const; + }; + + static SoundType *SOUND_NORMAL; + static SoundType *SOUND_WOOD; + static SoundType *SOUND_GRAVEL; + static SoundType *SOUND_GRASS; + static SoundType *SOUND_STONE; + static SoundType *SOUND_METAL; + static SoundType *SOUND_GLASS; + static SoundType *SOUND_CLOTH; + static SoundType *SOUND_SAND; + static SoundType *SOUND_SNOW; + static SoundType *SOUND_LADDER; + static SoundType *SOUND_ANVIL; + + static const int SHAPE_INVISIBLE = -1; + static const int SHAPE_BLOCK = 0; + static const int SHAPE_CROSS_TEXTURE = 1; + static const int SHAPE_TORCH = 2; + static const int SHAPE_FIRE = 3; + static const int SHAPE_WATER = 4; + static const int SHAPE_RED_DUST = 5; + static const int SHAPE_ROWS = 6; + static const int SHAPE_DOOR = 7; + static const int SHAPE_LADDER = 8; + static const int SHAPE_RAIL = 9; + static const int SHAPE_STAIRS = 10; + static const int SHAPE_FENCE = 11; + static const int SHAPE_LEVER = 12; + static const int SHAPE_CACTUS = 13; + static const int SHAPE_BED = 14; + static const int SHAPE_DIODE = 15; + static const int SHAPE_PISTON_BASE = 16; + static const int SHAPE_PISTON_EXTENSION = 17; + static const int SHAPE_IRON_FENCE = 18; + static const int SHAPE_STEM = 19; + static const int SHAPE_VINE = 20; + static const int SHAPE_FENCE_GATE = 21; + static const int SHAPE_ENTITYTILE_ANIMATED = 22; + static const int SHAPE_LILYPAD = 23; + static const int SHAPE_CAULDRON = 24; + static const int SHAPE_BREWING_STAND = 25; + static const int SHAPE_PORTAL_FRAME = 26; + static const int SHAPE_EGG = 27; + static const int SHAPE_COCOA = 28; + static const int SHAPE_TRIPWIRE_SOURCE = 29; + static const int SHAPE_TRIPWIRE = 30; + static const int SHAPE_TREE = 31; + static const int SHAPE_WALL = 32; + static const int SHAPE_FLOWER_POT = 33; + static const int SHAPE_BEACON = 34; + static const int SHAPE_ANVIL = 35; + static const int SHAPE_QUARTZ = 39; + + static Tile **tiles; + + static bool mipmapEnable[TILE_NUM_COUNT]; + static bool solid[TILE_NUM_COUNT]; + static int lightBlock[TILE_NUM_COUNT]; + static bool transculent[TILE_NUM_COUNT]; + static int lightEmission[TILE_NUM_COUNT]; + static unsigned char _sendTileData[TILE_NUM_COUNT]; // 4J - was bool, changed to bitfield so we can indicate which bits are important to be sent + static bool propagate[TILE_NUM_COUNT]; + + // 4J - this array of simple constants made so the compiler can optimise references to Ids that were previous of the form Tile::->id, and are now simply Tile::whatever_Id + static const int rock_Id = 1; + static const int grass_Id = 2; + static const int dirt_Id = 3; + static const int stoneBrick_Id = 4; + static const int wood_Id = 5; + static const int sapling_Id = 6; + static const int unbreakable_Id = 7; + static const int water_Id = 8; + static const int calmWater_Id = 9; + static const int lava_Id = 10; + static const int calmLava_Id = 11; + static const int sand_Id = 12; + static const int gravel_Id = 13; + static const int goldOre_Id = 14; + static const int ironOre_Id = 15; + static const int coalOre_Id = 16; + static const int treeTrunk_Id = 17; + static const int leaves_Id = 18; + static const int sponge_Id = 19; + static const int glass_Id = 20; + static const int lapisOre_Id = 21; + static const int lapisBlock_Id = 22; + static const int dispenser_Id = 23; + static const int sandStone_Id = 24; + static const int musicBlock_Id = 25; + static const int bed_Id = 26; + static const int goldenRail_Id = 27; + static const int detectorRail_Id = 28; + static const int pistonStickyBase_Id = 29; + static const int web_Id = 30; + static const int tallgrass_Id = 31; + static const int deadBush_Id = 32; + static const int pistonBase_Id = 33; + static const int pistonExtensionPiece_Id = 34; + static const int cloth_Id = 35; + static const int pistonMovingPiece_Id = 36; + static const int flower_Id = 37; + static const int rose_Id = 38; + static const int mushroom1_Id = 39; + static const int mushroom2_Id = 40; + static const int goldBlock_Id = 41; + static const int ironBlock_Id = 42; + static const int stoneSlab_Id = 43; + static const int stoneSlabHalf_Id = 44; + static const int redBrick_Id = 45; + static const int tnt_Id = 46; + static const int bookshelf_Id = 47; + static const int mossStone_Id = 48; + static const int obsidian_Id = 49; + static const int torch_Id = 50; + static const int fire_Id = 51; + static const int mobSpawner_Id = 52; + static const int stairs_wood_Id = 53; + static const int chest_Id = 54; + static const int redStoneDust_Id = 55; + static const int diamondOre_Id = 56; + static const int diamondBlock_Id = 57; + static const int workBench_Id = 58; + static const int crops_Id = 59; + static const int farmland_Id = 60; + static const int furnace_Id = 61; + static const int furnace_lit_Id = 62; + static const int sign_Id = 63; + static const int door_wood_Id = 64; + static const int ladder_Id = 65; + static const int rail_Id = 66; + static const int stairs_stone_Id = 67; + static const int wallSign_Id = 68; + static const int lever_Id = 69; + static const int pressurePlate_stone_Id = 70; + static const int door_iron_Id = 71; + static const int pressurePlate_wood_Id = 72; + static const int redStoneOre_Id = 73; + static const int redStoneOre_lit_Id = 74; + static const int notGate_off_Id = 75; + static const int notGate_on_Id = 76; + static const int button_stone_Id = 77; + static const int topSnow_Id = 78; + static const int ice_Id = 79; + static const int snow_Id = 80; + static const int cactus_Id = 81; + static const int clay_Id = 82; + static const int reeds_Id = 83; + static const int recordPlayer_Id = 84; + static const int fence_Id = 85; + static const int pumpkin_Id = 86; + static const int hellRock_Id = 87; + static const int hellSand_Id = 88; + static const int lightGem_Id = 89; + static const int portalTile_Id = 90; + static const int litPumpkin_Id = 91; + static const int cake_Id = 92; + static const int diode_off_Id = 93; + static const int diode_on_Id = 94; + static const int aprilFoolsJoke_Id = 95; + static const int trapdoor_Id = 96; + + static const int monsterStoneEgg_Id = 97; + static const int stoneBrickSmooth_Id = 98; + static const int hugeMushroom1_Id = 99; + static const int hugeMushroom2_Id = 100; + static const int ironFence_Id = 101; + static const int thinGlass_Id = 102; + static const int melon_Id = 103; + static const int pumpkinStem_Id = 104; + static const int melonStem_Id = 105; + static const int vine_Id = 106; + static const int fenceGate_Id = 107; + static const int stairs_bricks_Id = 108; + static const int stairs_stoneBrickSmooth_Id = 109; + + static const int mycel_Id = 110; + static const int waterLily_Id = 111; + static const int netherBrick_Id = 112; + static const int netherFence_Id = 113; + static const int stairs_netherBricks_Id = 114; + static const int netherStalk_Id = 115; + static const int enchantTable_Id = 116; + static const int brewingStand_Id = 117; + static const int cauldron_Id = 118; + static const int endPortalTile_Id = 119; + static const int endPortalFrameTile_Id = 120; + static const int whiteStone_Id = 121; + static const int dragonEgg_Id = 122; + static const int redstoneLight_Id = 123; + static const int redstoneLight_lit_Id = 124; + + + static const int woodSlab_Id = 125; + static const int woodSlabHalf_Id = 126; + static const int cocoa_Id = 127; + static const int stairs_sandstone_Id = 128; + static const int stairs_sprucewood_Id = 134; + static const int stairs_birchwood_Id = 135; + static const int stairs_junglewood_Id = 136; + static const int emeraldOre_Id = 129; + static const int enderChest_Id = 130; + static const int tripWireSource_Id = 131; + static const int tripWire_Id = 132; + static const int emeraldBlock_Id = 133; + + static const int cobbleWall_Id = 139; + static const int flowerPot_Id = 140; + static const int carrots_Id = 141; + static const int potatoes_Id = 142; + static const int anvil_Id = 145; + static const int button_wood_Id = 143; + static const int skull_Id = 144; + static const int netherQuartz_Id = 153; + static const int quartzBlock_Id = 155; + static const int stairs_quartz_Id = 156; + + static const int woolCarpet_Id = 171; + + + static Tile *rock; + static GrassTile *grass; + static Tile *dirt; + static Tile *stoneBrick; + static Tile *wood; + static Tile *sapling; + static Tile *unbreakable; + static LiquidTile *water; + static Tile *calmWater; + static LiquidTile *lava; + static Tile *calmLava; + static Tile *sand; + static Tile *gravel; + static Tile *goldOre; + static Tile *ironOre; + static Tile *coalOre; + static Tile *treeTrunk; + static LeafTile *leaves; + static Tile *sponge; + static Tile *glass; + static Tile *lapisOre; + static Tile *lapisBlock; + static Tile *dispenser; + static Tile *sandStone; + static Tile *musicBlock; + static Tile *bed; + static Tile *goldenRail; + static Tile *detectorRail; + static PistonBaseTile *pistonStickyBase; + static Tile *web; + static TallGrass *tallgrass; + static DeadBushTile *deadBush; + static PistonBaseTile *pistonBase; + static PistonExtensionTile *pistonExtension; + static Tile *cloth; + static PistonMovingPiece *pistonMovingPiece; + static Bush *flower; + static Bush *rose; + static Bush *mushroom1; + static Bush *mushroom2; + static Tile *goldBlock; + static Tile *ironBlock; +// static Tile *stoneSlab; +// static Tile *stoneSlabHalf; + static Tile *redBrick; + static Tile *tnt; + static Tile *bookshelf; + static Tile *mossStone; + static Tile *obsidian; + static Tile *torch; + static FireTile *fire; + static Tile *mobSpawner; + static Tile *stairs_wood; + static ChestTile *chest; + static RedStoneDustTile *redStoneDust; + static Tile *diamondOre; + static Tile *diamondBlock; + static Tile *workBench; + static Tile *crops; + static Tile *farmland; + static Tile *furnace; + static Tile *furnace_lit; + static Tile *sign; + static Tile *door_wood; + static Tile *ladder; + static Tile *rail; + static Tile *stairs_stone; + static Tile *wallSign; + static Tile *lever; + static Tile *pressurePlate_stone; + static Tile *door_iron; + static Tile *pressurePlate_wood; + static Tile *redStoneOre; + static Tile *redStoneOre_lit; + static Tile *notGate_off; + static Tile *notGate_on; + static Tile *button; + static Tile *topSnow; + static Tile *ice; + static Tile *snow; + static Tile *cactus; + static Tile *clay; + static Tile *reeds; + static Tile *recordPlayer; + static Tile *fence; + static Tile *pumpkin; + static Tile *hellRock; + static Tile *hellSand; + static Tile *lightGem; + static PortalTile *portalTile; + static Tile *litPumpkin; + static Tile *cake; + static RepeaterTile *diode_off; + static RepeaterTile *diode_on; + static Tile *aprilFoolsJoke; + static Tile *trapdoor; + + static Tile *monsterStoneEgg; + static Tile *stoneBrickSmooth; + static Tile *hugeMushroom1; + static Tile *hugeMushroom2; + static Tile *ironFence; + static Tile *thinGlass; + static Tile *melon; + static Tile *pumpkinStem; + static Tile *melonStem; + static Tile *vine; + static Tile *fenceGate; + static Tile *stairs_bricks; + static Tile *stairs_stoneBrickSmooth; + + static MycelTile *mycel; + static Tile *waterLily; + static Tile *netherBrick; + static Tile *netherFence; + static Tile *stairs_netherBricks; + static Tile *netherStalk; + static Tile *enchantTable; + static Tile *brewingStand; + static CauldronTile *cauldron; + static Tile *endPortalTile; + static Tile *endPortalFrameTile; + static Tile *whiteStone; + static Tile *dragonEgg; + static Tile *redstoneLight; + static Tile *redstoneLight_lit; + + static Tile *stairs_sandstone; + static Tile *woodStairsDark; + static Tile *woodStairsBirch; + static Tile *woodStairsJungle; + static Tile *button_wood; + static HalfSlabTile *woodSlab; + static HalfSlabTile *woodSlabHalf; + static HalfSlabTile *stoneSlab; + static HalfSlabTile *stoneSlabHalf; + static Tile *emeraldOre; + static Tile *enderChest; + static TripWireSourceTile *tripWireSource; + static Tile *tripWire; + static Tile *emeraldBlock; + + static Tile *cocoa; + static Tile *skull; + + static Tile *cobbleWall; + static Tile *flowerPot; + static Tile *carrots; + static Tile *potatoes; + static Tile *anvil; + static Tile *netherQuartz; + static Tile *quartzBlock; + static Tile *stairs_quartz; + + static Tile *woolCarpet; + + static void staticCtor(); + + int id; +protected: + float destroySpeed; + float explosionResistance; + bool isInventoryItem; + bool collectStatistics; + bool _isTicking; + bool _isEntityTile; + int m_iMaterial; + int m_iBaseItemType; + + // 4J Stu - Removed this in favour of a TLS version + //double xx0, yy0, zz0, xx1, yy1, zz1; + +public: + const SoundType *soundType; + + float gravity; + Material *material; + float friction; + +private: + unsigned int descriptionId; + unsigned int useDescriptionId; // 4J Added + + wstring m_textureName; + +protected: + Icon *icon; + +protected: + void _init(int id, Material *material, bool isSolidRender); + Tile(int id, Material *material, bool isSolidRender = true); + virtual ~Tile() {} +protected: + virtual Tile *sendTileData(unsigned char importantMask=15); // 4J - added importantMask to indicate which bits in the data are important +protected: + virtual void init(); + virtual Tile *setSoundType(const SoundType *soundType); + virtual Tile *setLightBlock(int i); + virtual Tile *setLightEmission(float f); + virtual Tile *setExplodeable(float explosionResistance); + Tile *setBaseItemTypeAndMaterial(int iType,int iMaterial); +public: + static bool isSolidBlockingTile(int t); + virtual bool isCubeShaped(); + virtual bool isPathfindable(LevelSource *level, int x, int y, int z); + virtual int getRenderShape(); + // 4J-PB added + int getBaseItemType(); + int getMaterial(); +protected: + virtual Tile *setDestroyTime(float destroySpeed); + virtual Tile *setIndestructible(); +public: + virtual float getDestroySpeed(Level *level, int x, int y, int z); +protected: + virtual Tile *setTicking(bool tick); + virtual Tile *disableMipmap(); +public: + virtual bool isTicking(); + virtual bool isEntityTile(); + virtual void setShape(float x0, float y0, float z0, float x1, float y1, float z1); + virtual float getBrightness(LevelSource *level, int x, int y, int z); + virtual int getLightColor(LevelSource *level, int x, int y, int z, int tileId=-1); // 4J - brought forward from 1.8.2 + static bool isFaceVisible(Level *level, int x, int y, int z, int f); + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + virtual bool isSolidFace(LevelSource *level, int x, int y, int z, int face); + virtual Icon *getTexture(LevelSource *level, int x, int y, int z, int face); + virtual Icon *getTexture(int face, int data); + virtual Icon *getTexture(int face); + virtual AABB *getTileAABB(Level *level, int x, int y, int z); + virtual void addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual bool isSolidRender(bool isServerLevel = false); // 4J - Added isServerLevel param + virtual bool mayPick(int data, bool liquid); + virtual bool mayPick(); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual void animateTick(Level *level, int x, int y, int z, Random *random); + virtual void destroy(Level *level, int x, int y, int z, int data); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual void addLights(Level *level, int x, int y, int z); + virtual int getTickDelay(); + virtual void onPlace(Level *level, int x, int y, int z); + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); + virtual int getResourceCount(Random *random); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual float getDestroyProgress(shared_ptr player, Level *level, int x, int y, int z); + virtual void spawnResources(Level *level, int x, int y, int z, int data, int playerBonusLevel); + virtual void spawnResources(Level *level, int x, int y, int z, int data, float odds, int playerBonusLevel); +protected: + virtual void popResource(Level *level, int x, int y, int z, shared_ptr itemInstance); + virtual void popExperience(Level *level, int x, int y, int z, int amount); + +public: + virtual int getSpawnResourcesAuxValue(int data); + virtual float getExplosionResistance(shared_ptr source); + virtual HitResult *clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b); +private: + virtual bool containsX(Vec3 *v); + virtual bool containsY(Vec3 *v); + virtual bool containsZ(Vec3 *v); +public: + virtual void wasExploded(Level *level, int x, int y, int z); + virtual int getRenderLayer(); + virtual bool mayPlace(Level *level, int x, int y, int z, int face); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual bool TestUse(); + virtual bool TestUse(Level *level, int x, int y, int z, shared_ptr player); + virtual bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + virtual void stepOn(Level *level, int x, int y, int z, shared_ptr entity); + virtual int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + virtual void prepareRender(Level *level, int x, int y, int z); + virtual void attack(Level *level, int x, int y, int z, shared_ptr player); + virtual void handleEntityInside(Level *level, int x, int y, int z, shared_ptr e, Vec3 *current); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual double getShapeX0(); + virtual double getShapeX1(); + virtual double getShapeY0(); + virtual double getShapeY1(); + virtual double getShapeZ0(); + virtual double getShapeZ1(); + virtual int getColor() const; + virtual int getColor(int auxData); + virtual int getColor(LevelSource *level, int x, int y, int z); + virtual int getColor(LevelSource *level, int x, int y, int z, int data); // 4J added + virtual bool getSignal(LevelSource *level, int x, int y, int z); + virtual bool getSignal(LevelSource *level, int x, int y, int z, int dir); + virtual bool isSignalSource(); + virtual void entityInside(Level *level, int x, int y, int z, shared_ptr entity); + virtual bool getDirectSignal(Level *level, int x, int y, int z, int dir); + virtual void updateDefaultShape(); + virtual void playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data); + virtual bool canSurvive(Level *level, int x, int y, int z); +protected: + virtual bool isSilkTouchable(); + virtual shared_ptr getSilkTouchItemInstance(int data); +public: + virtual int getResourceCountForLootBonus(int bonusLevel, Random *random); + virtual void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + virtual void finalizePlacement(Level *level, int x, int y, int z, int data); + virtual Tile *setDescriptionId(unsigned int id); + virtual wstring getName(); + virtual unsigned int getDescriptionId(int iData = -1); + virtual Tile *setUseDescriptionId(unsigned int id); // 4J Added + virtual unsigned int getUseDescriptionId(); // 4J Added + virtual void triggerEvent(Level *level, int x, int y, int z, int b0, int b1); + virtual bool isCollectStatistics(); + + // 4J Added so we can check before we try to add a tile to the tick list if it's actually going to do seomthing + // Default to true (it's also checking a bool array) and just override when we need to be able to say no + virtual bool shouldTileTick(Level *level, int x,int y,int z) { return true; } +protected: + virtual Tile *setNotCollectStatistics(); +public: + virtual int getPistonPushReaction(); + virtual float getShadeBrightness(LevelSource *level, int x, int y, int z); // 4J - brought forward from 1.8.2 + virtual void fallOn(Level *level, int x, int y, int z, shared_ptr entity, float fallDistance); + virtual int cloneTileId(Level *level, int x, int y, int z); + virtual int cloneTileData(Level *level, int x, int y, int z); + virtual void playerWillDestroy(Level *level, int x, int y, int z, int data, shared_ptr player); + virtual void onRemoving(Level *level, int x, int y, int z, int data); + virtual void handleRain(Level *level, int x, int y, int z); + virtual void levelTimeChanged(Level *level, __int64 delta, __int64 newTime); + virtual void registerIcons(IconRegister *iconRegister); + virtual wstring getTileItemIconName(); + // 4J Using per-item textures now + Tile *setTextureName(const wstring &name); + // AP - added this function so we can generate the faceFlags for a block in a single fast function + int getFaceFlags(LevelSource *level, int x, int y, int z); +}; + +class stoneBrick : public Tile {}; diff --git a/Minecraft.World/TileDestructionPacket.cpp b/Minecraft.World/TileDestructionPacket.cpp new file mode 100644 index 00000000..e009e0e0 --- /dev/null +++ b/Minecraft.World/TileDestructionPacket.cpp @@ -0,0 +1,85 @@ +#include "stdafx.h" +#include "net.minecraft.network.packet.h" +#include "TileDestructionPacket.h" + +TileDestructionPacket::TileDestructionPacket() +{ + id = 0; + x = 0; + y = 0; + z = 0; + state = 0; +} + +TileDestructionPacket::TileDestructionPacket(int id, int x, int y, int z, int state) +{ + this->id = id; + this->x = x; + this->y = y; + this->z = z; + this->state = state; +} + +void TileDestructionPacket::read(DataInputStream *dis) +{ + id = dis->readInt(); + x = dis->readInt(); + y = dis->readInt(); + z = dis->readInt(); + state = dis->read(); +} + +void TileDestructionPacket::write(DataOutputStream *dos) +{ + dos->writeInt(id); + dos->writeInt(x); + dos->writeInt(y); + dos->writeInt(z); + dos->write(state); +} + +void TileDestructionPacket::handle(PacketListener *listener) +{ + listener->handleTileDestruction(shared_from_this()); +} + +int TileDestructionPacket::getEstimatedSize() +{ + return 13; +} + +int TileDestructionPacket::getEntityId() +{ + return id; +} + +int TileDestructionPacket::getX() +{ + return x; +} + +int TileDestructionPacket::getY() +{ + return y; +} + +int TileDestructionPacket::getZ() +{ + return z; +} + +int TileDestructionPacket::getState() +{ + return state; +} + +bool TileDestructionPacket::canBeInvalidated() +{ + return true; +} + +bool TileDestructionPacket::isInvalidatedBy(shared_ptr packet) +{ + shared_ptr target = dynamic_pointer_cast(packet); + return target->id == id; +} \ No newline at end of file diff --git a/Minecraft.World/TileDestructionPacket.h b/Minecraft.World/TileDestructionPacket.h new file mode 100644 index 00000000..20cd7db9 --- /dev/null +++ b/Minecraft.World/TileDestructionPacket.h @@ -0,0 +1,35 @@ +#pragma once + +#include "Packet.h" + +class TileDestructionPacket : public Packet, public enable_shared_from_this +{ +private: + int id; + int x; + int y; + int z; + int state; + +public: + TileDestructionPacket(); + TileDestructionPacket(int id, int x, int y, int z, int state); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + + int getEntityId(); + int getX(); + int getY(); + int getZ(); + int getState(); + + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + +public: + static shared_ptr create() { return shared_ptr(new TileDestructionPacket()); } + virtual int getId() { return 55; } +}; \ No newline at end of file diff --git a/Minecraft.World/TileEntity.cpp b/Minecraft.World/TileEntity.cpp new file mode 100644 index 00000000..0790601d --- /dev/null +++ b/Minecraft.World/TileEntity.cpp @@ -0,0 +1,211 @@ +#include "stdafx.h" +#include "net.minecraft.network.packet.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "TileEntity.h" +#include "PistonPieceEntity.h" + + + +TileEntity::idToCreateMapType TileEntity::idCreateMap = unordered_map(); +TileEntity::classToIdMapType TileEntity::classIdMap = unordered_map(); + +void TileEntity::staticCtor() +{ + TileEntity::setId(FurnaceTileEntity::create, eTYPE_FURNACETILEENTITY, L"Furnace"); + TileEntity::setId(ChestTileEntity::create, eTYPE_CHESTTILEENTITY, L"Chest"); + TileEntity::setId(EnderChestTileEntity::create, eTYPE_ENDERCHESTTILEENTITY, L"EnderChest"); + TileEntity::setId(RecordPlayerTile::Entity::create, eTYPE_RECORDPLAYERTILE, L"RecordPlayer"); + TileEntity::setId(DispenserTileEntity::create, eTYPE_DISPENSERTILEENTITY, L"Trap"); + TileEntity::setId(SignTileEntity::create, eTYPE_SIGNTILEENTITY, L"Sign"); + TileEntity::setId(MobSpawnerTileEntity::create, eTYPE_MOBSPAWNERTILEENTITY, L"MobSpawner"); + TileEntity::setId(MusicTileEntity::create, eTYPE_MUSICTILEENTITY, L"Music"); + TileEntity::setId(PistonPieceEntity::create, eTYPE_PISTONPIECEENTITY, L"Piston"); + TileEntity::setId(BrewingStandTileEntity::create, eTYPE_BREWINGSTANDTILEENTITY, L"Cauldron"); + TileEntity::setId(EnchantmentTableEntity::create, eTYPE_ENCHANTMENTTABLEENTITY, L"EnchantTable"); + TileEntity::setId(TheEndPortalTileEntity::create, eTYPE_THEENDPORTALTILEENTITY, L"Airportal"); + TileEntity::setId(SkullTileEntity::create,eTYPE_SKULLTILEENTITY, L"Skull"); +} + +void TileEntity::setId(tileEntityCreateFn createFn, eINSTANCEOF clas, wstring id) +{ + // 4J Stu - Java has classIdMap.containsKey(id) which would never work as id is not of the type of the key in classIdMap + // I have changed to use idClassMap instead so that we can still search from the string key + // TODO 4J Stu - Exceptions + if (idCreateMap.find(id) != idCreateMap.end() ) {}//throw new IllegalArgumentException("Duplicate id: " + id); + idCreateMap.insert( idToCreateMapType::value_type(id, createFn) ); + classIdMap.insert( classToIdMapType::value_type( clas, id ) ); +} + +TileEntity::TileEntity() +{ + level = NULL; + x = y = z = 0; + remove = false; + data = -1; + tile = NULL; + renderRemoveStage = e_RenderRemoveStageKeep; +} + +Level *TileEntity::getLevel() +{ + return level; +} + +void TileEntity::setLevel(Level *level) +{ + this->level = level; +} + +bool TileEntity::hasLevel() +{ + return level != NULL; +} + +void TileEntity::load(CompoundTag *tag) +{ + x = tag->getInt(L"x"); + y = tag->getInt(L"y"); + z = tag->getInt(L"z"); +} + +void TileEntity::save(CompoundTag *tag) +{ + AUTO_VAR(it, classIdMap.find( this->GetType() )); + if ( it == classIdMap.end() ) + { + // TODO 4J Stu - Some sort of exception handling + //throw new RuntimeException(this->getClass() + " is missing a mapping! This is a bug!"); + return; + } + tag->putString(L"id", ( (*it).second ) ); + tag->putInt(L"x", x); + tag->putInt(L"y", y); + tag->putInt(L"z", z); +} + +void TileEntity::tick() +{ +} + +shared_ptr TileEntity::loadStatic(CompoundTag *tag) +{ + shared_ptr entity = nullptr; + + //try + //{ + AUTO_VAR(it, idCreateMap.find(tag->getString(L"id"))); + if (it != idCreateMap.end() ) entity = shared_ptr(it->second()); + //} + //catch (Exception e) + //{ + // TODO 4J Stu - Exception handling? + // e->printStackTrace(); + //} + if (entity != NULL) + { + entity->load(tag); + } + else + { +#ifdef _DEBUG + app.DebugPrintf("Skipping TileEntity with id %ls.\n" , tag->getString(L"id").c_str() ); +#endif + } + + return entity; +} + +int TileEntity::getData() +{ + if (data == -1) data = level->getData(x, y, z); + return data; +} + +void TileEntity::setData(int data) +{ + this->data = data; + level->setData(x, y, z, data); +} + +void TileEntity::setChanged() +{ + if (level != NULL) + { + data = level->getData(x, y, z); + level->tileEntityChanged(x, y, z, shared_from_this()); + } +} + +double TileEntity::distanceToSqr(double xPlayer, double yPlayer, double zPlayer) +{ + double xd = (x + 0.5) - xPlayer; + double yd = (y + 0.5) - yPlayer; + double zd = (z + 0.5) - zPlayer; + return xd * xd + yd * yd + zd * zd; +} + +Tile *TileEntity::getTile() +{ + if( tile == NULL ) tile = Tile::tiles[level->getTile(x, y, z)]; + return tile; +} + +shared_ptr TileEntity::getUpdatePacket() +{ + return nullptr; +} + +bool TileEntity::isRemoved() +{ + return remove; +} + +void TileEntity::setRemoved() +{ + remove = true; +} + +void TileEntity::clearRemoved() +{ + remove = false; +} +void TileEntity::triggerEvent(int b0, int b1) +{ +} + +void TileEntity::clearCache() +{ + tile = NULL; + data = -1; +} + +void TileEntity::setRenderRemoveStage( unsigned char stage ) +{ + renderRemoveStage = stage; +} + +bool TileEntity::shouldRemoveForRender() +{ + return (renderRemoveStage == e_RenderRemoveStageRemove); +} + +void TileEntity::upgradeRenderRemoveStage() +{ + if( renderRemoveStage == e_RenderRemoveStageFlaggedAtChunk ) + { + renderRemoveStage = e_RenderRemoveStageRemove; + } +} + +// 4J Added +void TileEntity::clone(shared_ptr tileEntity) +{ + tileEntity->level = this->level; + tileEntity->x = this->x; + tileEntity->y = this->y; + tileEntity->z = this->z; + tileEntity->data = this->data; + tileEntity->tile = this->tile; +} \ No newline at end of file diff --git a/Minecraft.World/TileEntity.h b/Minecraft.World/TileEntity.h new file mode 100644 index 00000000..aa3ced4f --- /dev/null +++ b/Minecraft.World/TileEntity.h @@ -0,0 +1,74 @@ +#pragma once +using namespace std; + +#include "HashExtension.h" +#include "..\Minecraft.World\JavaIntHash.h" + +class Level; +class Packet; +class CompoundTag; + +typedef TileEntity *(*tileEntityCreateFn)(); + +class TileEntity : public enable_shared_from_this +{ +public: + static void staticCtor(); + virtual eINSTANCEOF GetType() { return eTYPE_TILEENTITY; } +private: + typedef unordered_map idToCreateMapType; + typedef unordered_map classToIdMapType; + static idToCreateMapType idCreateMap; + static classToIdMapType classIdMap; + static void setId(tileEntityCreateFn createFn, eINSTANCEOF clas, wstring id); + bool remove; + unsigned char renderRemoveStage; // 4J added + +public: + Level *level; + int x, y, z; + + // 4J added + enum RenderRemoveStage + { + e_RenderRemoveStageKeep, + e_RenderRemoveStageFlaggedAtChunk, + e_RenderRemoveStageRemove + }; + + int data; + Tile *tile; + +public: + // 4J Java does not have a ctor, but we need one to do some initialisation of the member variables + TileEntity(); + virtual ~TileEntity() {} + + void setRenderRemoveStage(unsigned char stage); // 4J added + void upgradeRenderRemoveStage(); // 4J added + bool shouldRemoveForRender(); // 4J added + + Level *getLevel(); + void setLevel(Level *level); + bool hasLevel(); + virtual void load(CompoundTag *tag); + virtual void save(CompoundTag *tag); + virtual void tick(); + static shared_ptr loadStatic(CompoundTag *tag); + int getData(); + void setData(int data); + void setChanged(); + double distanceToSqr(double xPlayer, double yPlayer, double zPlayer); + Tile *getTile(); + virtual shared_ptr getUpdatePacket(); + virtual bool isRemoved(); + virtual void setRemoved(); + virtual void clearRemoved(); + virtual void triggerEvent(int b0, int b1); + virtual void clearCache(); + + // 4J Added + virtual shared_ptr clone() = 0; +protected: + void clone(shared_ptr tileEntity); +}; \ No newline at end of file diff --git a/Minecraft.World/TileEntityDataPacket.cpp b/Minecraft.World/TileEntityDataPacket.cpp new file mode 100644 index 00000000..41be4526 --- /dev/null +++ b/Minecraft.World/TileEntityDataPacket.cpp @@ -0,0 +1,65 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "TileEntityDataPacket.h" + + + +void TileEntityDataPacket::_init() +{ + x = y = z = 0; + type = TYPE_MOB_SPAWNER; + tag = NULL; +} + + +TileEntityDataPacket::TileEntityDataPacket() +{ + _init(); + shouldDelay = true; +} + +TileEntityDataPacket::TileEntityDataPacket(int x, int y, int z, int type, CompoundTag *tag) +{ + _init(); + shouldDelay = true; + this->x = x; + this->y = y; + this->z = z; + this->type = type; + this->tag = tag; +} + +TileEntityDataPacket::~TileEntityDataPacket() +{ + delete tag; +} + +void TileEntityDataPacket::read(DataInputStream *dis) +{ + x = dis->readInt(); + y = dis->readShort(); + z = dis->readInt(); + type = dis->readByte(); + tag = readNbt(dis); +} + +void TileEntityDataPacket::write(DataOutputStream *dos) +{ + dos->writeInt(x); + dos->writeShort(y); + dos->writeInt(z); + dos->writeByte((byte) type); + writeNbt(tag, dos); +} + +void TileEntityDataPacket::handle(PacketListener *listener) +{ + listener->handleTileEntityData(shared_from_this()); +} + +int TileEntityDataPacket::getEstimatedSize() +{ + return 6 * 4 + 1; +} \ No newline at end of file diff --git a/Minecraft.World/TileEntityDataPacket.h b/Minecraft.World/TileEntityDataPacket.h new file mode 100644 index 00000000..2ee998f3 --- /dev/null +++ b/Minecraft.World/TileEntityDataPacket.h @@ -0,0 +1,36 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class CompoundTag; + +class TileEntityDataPacket : public Packet, public enable_shared_from_this +{ +public: + static const int TYPE_MOB_SPAWNER = 1; + static const int TYPE_ADV_COMMAND = 2; + static const int TYPE_BEACON = 3; + static const int TYPE_SKULL = 4; + + int x, y, z; + int type; + CompoundTag *tag; + +private: + void _init(); + +public: + TileEntityDataPacket(); + ~TileEntityDataPacket(); + TileEntityDataPacket(int x, int y, int z, int type, CompoundTag *tag); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new TileEntityDataPacket()); } + virtual int getId() { return 132; } +}; \ No newline at end of file diff --git a/Minecraft.World/TileEventData.cpp b/Minecraft.World/TileEventData.cpp new file mode 100644 index 00000000..07be82d5 --- /dev/null +++ b/Minecraft.World/TileEventData.cpp @@ -0,0 +1,48 @@ +#include "stdafx.h" + +#include "TileEventData.h" + +TileEventData::TileEventData(int x, int y, int z, int tile, int paramA, int paramB) +{ + this->x = x; + this->y = y; + this->z = z; + this->paramA = paramA; + this->paramB = paramB; + this->tile = tile; +} + +int TileEventData::getX() +{ + return x; +} + +int TileEventData::getY() +{ + return y; +} + +int TileEventData::getZ() +{ + return z; +} + +int TileEventData::getParamA() +{ + return paramA; +} + +int TileEventData::getParamB() +{ + return paramB; +} + +int TileEventData::getTile() +{ + return tile; +} + +bool TileEventData::equals(TileEventData &ted) +{ + return x == ted.x && y == ted.y && z == ted.z && paramA == ted.paramA && paramB == ted.paramB && tile == ted.tile; +} \ No newline at end of file diff --git a/Minecraft.World/TileEventData.h b/Minecraft.World/TileEventData.h new file mode 100644 index 00000000..bbec478e --- /dev/null +++ b/Minecraft.World/TileEventData.h @@ -0,0 +1,21 @@ +#pragma once + +class TileEventData +{ +private: + int x, y, z; + int tile; + int paramA; + int paramB; + +public: + TileEventData(int x, int y, int z, int tile, int paramA, int paramB); + + int getX(); + int getY(); + int getZ(); + int getParamA(); + int getParamB(); + int getTile(); + bool equals(TileEventData &ted); +}; \ No newline at end of file diff --git a/Minecraft.World/TileEventPacket.cpp b/Minecraft.World/TileEventPacket.cpp new file mode 100644 index 00000000..51e6857c --- /dev/null +++ b/Minecraft.World/TileEventPacket.cpp @@ -0,0 +1,56 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "TileEventPacket.h" +#include "net.minecraft.world.level.tile.h" + +TileEventPacket::TileEventPacket() +{ + x = 0; + y = 0; + z = 0; + b0 = 0; + b1 = 0; + tile = 0; +} + +TileEventPacket::TileEventPacket(int x, int y, int z, int tile, int b0, int b1) +{ + this->x = x; + this->y = y; + this->z = z; + this->b0 = b0; + this->b1 = b1; + this->tile = tile; +} + +void TileEventPacket::read(DataInputStream *dis) //throws IOException +{ + x = dis->readInt(); + y = dis->readShort(); + z = dis->readInt(); + b0 = dis->read(); + b1 = dis->read(); + tile = dis->readShort() & Tile::TILE_NUM_MASK; +} + +void TileEventPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(x); + dos->writeShort(y); + dos->writeInt(z); + dos->write(b0); + dos->write(b1); + dos->writeShort(tile & Tile::TILE_NUM_MASK); +} + +void TileEventPacket::handle(PacketListener *listener) +{ + listener->handleTileEvent(shared_from_this()); +} + +int TileEventPacket::getEstimatedSize() +{ + return 2 * 4 + 2 + 2 + 2; +} diff --git a/Minecraft.World/TileEventPacket.h b/Minecraft.World/TileEventPacket.h new file mode 100644 index 00000000..ca2685fa --- /dev/null +++ b/Minecraft.World/TileEventPacket.h @@ -0,0 +1,22 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class TileEventPacket : public Packet, public enable_shared_from_this +{ +public: + int x, y, z, b0, b1, tile; + + TileEventPacket(); + TileEventPacket(int x, int y, int z, int tile, int b0, int b1); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new TileEventPacket()); } + virtual int getId() { return 54; } +}; \ No newline at end of file diff --git a/Minecraft.World/TileItem.cpp b/Minecraft.World/TileItem.cpp new file mode 100644 index 00000000..8f624bff --- /dev/null +++ b/Minecraft.World/TileItem.cpp @@ -0,0 +1,221 @@ +using namespace std; + +#include "stdafx.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.h" +#include "net.minecraft.stats.h" +#include "TileItem.h" +#include "facing.h" + +// 4J-PB - for the debug option of not removing items +#include +#include + + +TileItem::TileItem(int id) : Item(id) +{ + this->tileId = id + 256; + itemIcon = NULL; +} + +int TileItem::getTileId() +{ + return tileId; +} + +int TileItem::getIconType() +{ + if (!Tile::tiles[tileId]->getTileItemIconName().empty()) + { + return Icon::TYPE_ITEM; + } + return Icon::TYPE_TERRAIN; +} + +Icon *TileItem::getIcon(int auxValue) +{ + if (itemIcon != NULL) + { + return itemIcon; + } + return Tile::tiles[tileId]->getTexture(Facing::UP, auxValue); +} + +bool TileItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + // 4J-PB - Adding a test only version to allow tooltips to be displayed + int currentTile = level->getTile(x, y, z); + if (currentTile == Tile::topSnow_Id) + { + face = Facing::UP; + } + else if (currentTile == Tile::vine_Id || currentTile == Tile::tallgrass_Id || currentTile == Tile::deadBush_Id) + { + } + else + { + if (face == 0) y--; + if (face == 1) y++; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + } + + if (instance->count == 0) return false; + if (!player->mayBuild(x, y, z)) return false; + + + if (y == Level::maxBuildHeight - 1 && Tile::tiles[tileId]->material->isSolid()) return false; + + int undertile = level->getTile(x,y-1,z); // For 'BodyGuard' achievement. + + if (level->mayPlace(tileId, x, y, z, false, face, player)) + { + if(!bTestUseOnOnly) + { + Tile *tile = Tile::tiles[tileId]; + // 4J - Adding this from 1.6 + int itemValue = getLevelDataForAuxValue(instance->getAuxValue()); + int dataValue = Tile::tiles[tileId]->getPlacedOnFaceDataValue(level, x, y, z, face, clickX, clickY, clickZ, itemValue); + if (level->setTileAndData(x, y, z, tileId, dataValue)) + { + // 4J-JEV: Snow/Iron Golems do not have owners apparently. + int newTileId = level->getTile(x,y,z); + if ( (tileId == Tile::pumpkin_Id || tileId == Tile::litPumpkin_Id) && newTileId == 0 ) + { + eINSTANCEOF golemType; + switch (undertile) + { + case Tile::ironBlock_Id: golemType = eTYPE_VILLAGERGOLEM; break; + case Tile::snow_Id: golemType = eTYPE_SNOWMAN; break; + default: golemType = eTYPE_NOTSET; break; + } + + if (golemType != eTYPE_NOTSET) + { + player->awardStat(GenericStats::craftedEntity(golemType),GenericStats::param_craftedEntity(golemType)); + } + } + + // 4J-JEV: Hook for durango 'BlockPlaced' event. + player->awardStat(GenericStats::blocksPlaced(tileId),GenericStats::param_blocksPlaced(tileId,instance->getAuxValue(),1)); + + // 4J - Original comment + // ok this may look stupid, but neighbor updates can cause the + // placed block to become something else before these methods + // are called + if (level->getTile(x, y, z) == tileId) + { + Tile::tiles[tileId]->setPlacedBy(level, x, y, z, player); + Tile::tiles[tileId]->finalizePlacement(level, x, y, z, dataValue); + } + + // 4J-PB - Java 1.4 change - getStepSound replaced with getPlaceSound + //level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, tile->soundType->getStepSound(), (tile->soundType->getVolume() + 1) / 2, tile->soundType->getPitch() * 0.8f); +#ifdef _DEBUG + int iPlaceSound=tile->soundType->getPlaceSound(); + int iStepSound=tile->soundType->getStepSound(); + +// char szPlaceSoundName[256]; +// char szStepSoundName[256]; +// Minecraft *pMinecraft = Minecraft::GetInstance(); +// +// if(iPlaceSound==-1) +// { +// strcpy(szPlaceSoundName,"NULL"); +// } +// else +// { +// pMinecraft->soundEngine->GetSoundName(szPlaceSoundName,iPlaceSound); +// } +// if(iStepSound==-1) +// { +// strcpy(szStepSoundName,"NULL"); +// } +// else +// { +// pMinecraft->soundEngine->GetSoundName(szStepSoundName,iStepSound); +// } + + //app.DebugPrintf("Place Sound - %s, Step Sound - %s\n",szPlaceSoundName,szStepSoundName); + app.DebugPrintf("Place Sound - %d, Step Sound - %d\n",iPlaceSound,iStepSound); +#endif + level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, tile->soundType->getPlaceSound(), (tile->soundType->getVolume() + 1) / 2, tile->soundType->getPitch() * 0.8f); +#ifndef _FINAL_BUILD + // 4J-PB - If we have the debug option on, don't reduce the number of this item + if(!(app.DebugSettingsOn() && app.GetGameSettingsDebugMask()&(1L<count--; + } + } + } + return true; + } + return false; +} + + +bool TileItem::mayPlace(Level *level, int x, int y, int z, int face, shared_ptr player, shared_ptr item) +{ + int currentTile = level->getTile(x, y, z); + if (currentTile == Tile::topSnow_Id) + { + face = Facing::UP; + } + else if (currentTile != Tile::vine_Id && currentTile != Tile::tallgrass_Id && currentTile != Tile::deadBush_Id) + { + if (face == 0) y--; + if (face == 1) y++; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + } + + return level->mayPlace(getTileId(), x, y, z, false, face, nullptr); +} + +// 4J Added to colourise some tile types in the hint popups +int TileItem::getColor(int itemAuxValue, int spriteLayer) +{ + return Tile::tiles[tileId]->getColor(); +} + +unsigned int TileItem::getDescriptionId(shared_ptr instance) +{ + return Tile::tiles[tileId]->getDescriptionId(); +} + + +unsigned int TileItem::getDescriptionId(int iData /*= -1*/) +{ + return Tile::tiles[tileId]->getDescriptionId(iData); +} + + +unsigned int TileItem::getUseDescriptionId(shared_ptr instance) +{ + return Tile::tiles[tileId]->getUseDescriptionId(); +} + + +unsigned int TileItem::getUseDescriptionId() +{ + return Tile::tiles[tileId]->getUseDescriptionId(); +} + +void TileItem::registerIcons(IconRegister *iconRegister) +{ + wstring iconName = Tile::tiles[tileId]->getTileItemIconName(); + if (!iconName.empty()) + { + itemIcon = iconRegister->registerIcon(iconName); + } +} diff --git a/Minecraft.World/TileItem.h b/Minecraft.World/TileItem.h new file mode 100644 index 00000000..ac341acc --- /dev/null +++ b/Minecraft.World/TileItem.h @@ -0,0 +1,44 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class Player; +class Level; + +class TileItem : public Item +{ +public: static const int _class = 0; +using Item::getColor; + +private: + int tileId; + Icon *itemIcon; + +public: + TileItem(int id); + + virtual int getTileId(); + + //@Override + int getIconType(); + + //@Override + Icon *getIcon(int auxValue); + + virtual bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); + virtual unsigned int getDescriptionId(shared_ptr instance); + virtual unsigned int getDescriptionId(int iData = -1); + + // 4J Added + virtual int getColor(int itemAuxValue, int spriteLayer); + + // 4J Added + virtual unsigned int getUseDescriptionId(shared_ptr instance); + virtual unsigned int getUseDescriptionId(); + + virtual bool mayPlace(Level *level, int x, int y, int z, int face, shared_ptr player, shared_ptr item); + + //@Override + virtual void registerIcons(IconRegister *iconRegister); +}; diff --git a/Minecraft.World/TilePlanterItem.cpp b/Minecraft.World/TilePlanterItem.cpp new file mode 100644 index 00000000..883da76e --- /dev/null +++ b/Minecraft.World/TilePlanterItem.cpp @@ -0,0 +1,80 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.h" +#include "TilePlanterItem.h" +#include "GenericStats.h" +// 4J-PB - for the debug option of not removing items +#include +#include + +TilePlanterItem::TilePlanterItem(int id, Tile *tile) : Item(id) +{ + this->tileId = tile->id; +} + +bool TilePlanterItem::useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly) +{ + // 4J-PB - Adding a test only version to allow tooltips to be displayed + int currentTile = level->getTile(x, y, z); + if (currentTile == Tile::topSnow_Id) + { + face = Facing::UP; + } + else if (currentTile == Tile::vine_Id || currentTile == Tile::tallgrass_Id || currentTile == Tile::deadBush_Id) + { + } + else + { + if (face == 0) y--; + if (face == 1) y++; + if (face == 2) z--; + if (face == 3) z++; + if (face == 4) x--; + if (face == 5) x++; + } + + if (!player->mayBuild(x, y, z)) return false; + if (instance->count == 0) return false; + + if (level->mayPlace(tileId, x, y, z, false, face, nullptr)) + { + if(!bTestUseOnOnly) + { + Tile *tile = Tile::tiles[tileId]; + int dataValue = tile->getPlacedOnFaceDataValue(level, x, y, z, face, clickX, clickY, clickZ, 0); + if (level->setTileAndData(x, y, z, tileId, dataValue)) + { + // 4J-JEV: Hook for durango 'BlockPlaced' event. + player->awardStat(GenericStats::blocksPlaced(tileId),GenericStats::param_blocksPlaced(tileId,instance->getAuxValue(),1)); + + // 4J Original comment + // ok this may look stupid, but neighbor updates can cause the + // placed block to become something else before these methods + // are called + if (level->getTile(x, y, z) == tileId) + { + Tile::tiles[tileId]->setPlacedBy(level, x, y, z, player); + Tile::tiles[tileId]->finalizePlacement(level, x, y, z, dataValue); + } + level->playSound(x + 0.5f, y + 0.5f, z + 0.5f, tile->soundType->getStepSound(), (tile->soundType->getVolume() + 1) / 2, tile->soundType->getPitch() * 0.8f); + // 4J-PB - If we have the debug option on, don't reduce the number of this item + #ifndef _FINAL_BUILD + if(!(app.DebugSettingsOn() && app.GetGameSettingsDebugMask()&(1L<count--; + } + + } + } + } + else + { + // Can't place, so return false + if(bTestUseOnOnly) return false; + } + return true; +} diff --git a/Minecraft.World/TilePlanterItem.h b/Minecraft.World/TilePlanterItem.h new file mode 100644 index 00000000..d1577cd1 --- /dev/null +++ b/Minecraft.World/TilePlanterItem.h @@ -0,0 +1,15 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class TilePlanterItem : public Item +{ +private: + int tileId; + +public: + TilePlanterItem(int id, Tile *tile); + + virtual bool useOn(shared_ptr instance, shared_ptr player, Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, bool bTestUseOnOnly=false); +}; \ No newline at end of file diff --git a/Minecraft.World/TilePos.cpp b/Minecraft.World/TilePos.cpp new file mode 100644 index 00000000..a254d336 --- /dev/null +++ b/Minecraft.World/TilePos.cpp @@ -0,0 +1,29 @@ +#include "stdafx.h" + +#include "TilePos.h" +#include "Vec3.h" + +TilePos::TilePos(int x, int y, int z) +{ + this->x = x; + this->y = y; + this->z = z; +} + +// 4J - brought forward from 1.2.3 +TilePos::TilePos(Vec3 *p) +{ + this->x = Mth::floor(p->x); + this->y = Mth::floor(p->y); + this->z = Mth::floor(p->z); +} + +int TilePos::hash_fnct(const TilePos &k) +{ + return k.x * 8976890 + k.y * 981131 + k.z; +} + +bool TilePos::eq_test(const TilePos &x, const TilePos &y) +{ + return x.x == y.x && x.y == y.y && x.z == y.z; +} \ No newline at end of file diff --git a/Minecraft.World/TilePos.h b/Minecraft.World/TilePos.h new file mode 100644 index 00000000..d4a3e84e --- /dev/null +++ b/Minecraft.World/TilePos.h @@ -0,0 +1,27 @@ +#pragma once + +class Vec3; +class TilePos +{ +public: + int x, y, z; + +public: + TilePos(int x, int y, int z); + TilePos(Vec3 *p); // 4J - brought forward from 1.2.3 + + static int hash_fnct(const TilePos &k); + static bool eq_test(const TilePos &x, const TilePos &y); +}; + +typedef struct +{ + int operator() (const TilePos &k) const { return TilePos::hash_fnct (k); } + +} TilePosKeyHash; + +typedef struct +{ + bool operator() (const TilePos &x, const TilePos &y) const { return TilePos::eq_test (x, y); } +} TilePosKeyEq; + diff --git a/Minecraft.World/TileUpdatePacket.cpp b/Minecraft.World/TileUpdatePacket.cpp new file mode 100644 index 00000000..6ca37faf --- /dev/null +++ b/Minecraft.World/TileUpdatePacket.cpp @@ -0,0 +1,92 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.level.h" +#include "PacketListener.h" +#include "TileUpdatePacket.h" +#include "Dimension.h" + + + +TileUpdatePacket::TileUpdatePacket() +{ + shouldDelay = true; +} + +TileUpdatePacket::TileUpdatePacket(int x, int y, int z, Level *level) +{ + shouldDelay = true; + this->x = x; + this->y = y; + this->z = z; + block = level->getTile(x, y, z); + data = level->getData(x, y, z); + levelIdx = ( ( level->dimension->id == 0 ) ? 0 : ( (level->dimension->id == -1) ? 1 : 2 ) ); +} + +void TileUpdatePacket::read(DataInputStream *dis) //throws IOException +{ +#ifdef _LARGE_WORLDS + x = dis->readInt(); + y = dis->read(); + z = dis->readInt(); + + block = (int)dis->readShort() & 0xffff; + + BYTE dataLevel = dis->readByte(); + data = dataLevel & 0xf; + levelIdx = (dataLevel>>4) & 0xf; +#else + // 4J - See comments in write for packing + int xyzdata = dis->readInt(); + x = ( xyzdata >> 22 ) & 0x3ff; + y = ( xyzdata >> 14 ) & 0xff; + z = ( xyzdata >> 4 ) & 0x3ff; + x = ( x << 22 ) >> 22; + z = ( z << 22 ) >> 22; + data = xyzdata & 0xf; + block = (int)dis->readShort() & 0xffff; + //levelIdx = ( xyzdata >> 31 ) & 1; + + // Can't pack this as it's now 2 bits + levelIdx = (int)dis->readByte(); +#endif +} + +void TileUpdatePacket::write(DataOutputStream *dos) //throws IOException +{ +#ifdef _LARGE_WORLDS + dos->writeInt(x); + dos->write(y); + dos->writeInt(z); + dos->writeShort(block); + + BYTE dataLevel = ((levelIdx & 0xf ) << 4) | (data & 0xf); + dos->writeByte(dataLevel); +#else + // 4J - for our fixed size map, we can pack x & z into 10 bits each (-512 -> 511), y into 8 bits (0 to 255) + // block type could really be 7 bits but leaving that as 8 for future ease of expansion. Data only needs to be 4-bits as that is how it + // is ultimately stored + int xyzdata = ( ( x & 0x3ff ) << 22 ) | ( ( y & 0xff ) << 14 ) | ( ( z & 0x3ff ) << 4 ) | ( data & 0xf); + //xyzdata |= levelIdx << 31; + dos->writeInt(xyzdata); + dos->writeShort(block); + + // Can't pack this as it's now 2 bits + dos->write(levelIdx); +#endif +} + +void TileUpdatePacket::handle(PacketListener *listener) +{ + listener->handleTileUpdate(shared_from_this()); +} + +int TileUpdatePacket::getEstimatedSize() +{ +#ifdef _LARGE_WORLDS + return 12; +#else + return 5; +#endif +} diff --git a/Minecraft.World/TileUpdatePacket.h b/Minecraft.World/TileUpdatePacket.h new file mode 100644 index 00000000..fe69c763 --- /dev/null +++ b/Minecraft.World/TileUpdatePacket.h @@ -0,0 +1,23 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class TileUpdatePacket : public Packet, public enable_shared_from_this +{ +public: + int x, y, z; + int block, data; + int levelIdx; + + TileUpdatePacket(); + TileUpdatePacket(int x, int y, int z, Level *level); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); +public: + static shared_ptr create() { return shared_ptr(new TileUpdatePacket()); } + virtual int getId() { return 53; } +}; \ No newline at end of file diff --git a/Minecraft.World/TimeCommand.cpp b/Minecraft.World/TimeCommand.cpp new file mode 100644 index 00000000..e667a420 --- /dev/null +++ b/Minecraft.World/TimeCommand.cpp @@ -0,0 +1,80 @@ +#include "stdafx.h" +#include "net.minecraft.commands.h" +#include "..\Minecraft.Client\MinecraftServer.h" +#include "..\Minecraft.Client\ServerLevel.h" +#include "net.minecraft.network.packet.h" +#include "TimeCommand.h" + +EGameCommand TimeCommand::getId() +{ + return eGameCommand_Time; +} + +void TimeCommand::execute(shared_ptr source, byteArray commandData) +{ + ByteArrayInputStream bais(commandData); + DataInputStream dis(&bais); + + bool night = dis.readBoolean(); + + bais.reset(); + + int amount = 0; + if(night) amount = 12500; + doSetTime(source, amount); + //logAdminAction(source, "commands.time.set", amount); + logAdminAction(source, ChatPacket::e_ChatCustom, L"commands.time.set"); + + //if (args.length > 1) { + // if (args[0].equals("set")) { + // int amount; + + // if (args[1].equals("day")) { + // amount = 0; + // } else if (args[1].equals("night")) { + // amount = 12500; + // } else { + // amount = convertArgToInt(source, args[1], 0); + // } + + // doSetTime(source, amount); + // logAdminAction(source, "commands.time.set", amount); + // return; + // } else if (args[0].equals("add")) { + // int amount = convertArgToInt(source, args[1], 0); + // doAddTime(source, amount); + + // logAdminAction(source, "commands.time.added", amount); + // return; + // } + //} + + //throw new UsageException("commands.time.usage"); +} + +void TimeCommand::doSetTime(shared_ptr source, int value) +{ + for (int i = 0; i < MinecraftServer::getInstance()->levels.length; i++) + { + MinecraftServer::getInstance()->levels[i]->setTimeAndAdjustTileTicks(value); + } +} + +void TimeCommand::doAddTime(shared_ptr source, int value) +{ + for (int i = 0; i < MinecraftServer::getInstance()->levels.length; i++) + { + ServerLevel *level = MinecraftServer::getInstance()->levels[i]; + level->setTimeAndAdjustTileTicks(level->getTime() + value); + } +} + +shared_ptr TimeCommand::preparePacket(bool night) +{ + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + + dos.writeBoolean(night); + + return shared_ptr( new GameCommandPacket(eGameCommand_Time, baos.toByteArray() )); +} \ No newline at end of file diff --git a/Minecraft.World/TimeCommand.h b/Minecraft.World/TimeCommand.h new file mode 100644 index 00000000..f87fb27c --- /dev/null +++ b/Minecraft.World/TimeCommand.h @@ -0,0 +1,17 @@ +#pragma once + +#include "Command.h" + +class TimeCommand : public Command +{ +public: + virtual EGameCommand getId(); + virtual void execute(shared_ptr source, byteArray commandData); + +protected: + void doSetTime(shared_ptr source, int value); + void doAddTime(shared_ptr source, int value); + +public: + static shared_ptr preparePacket(bool night); +}; \ No newline at end of file diff --git a/Minecraft.World/TntTile.cpp b/Minecraft.World/TntTile.cpp new file mode 100644 index 00000000..b422959c --- /dev/null +++ b/Minecraft.World/TntTile.cpp @@ -0,0 +1,120 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.h" +#include "net.minecraft.h" + +#include "TntTile.h" +#include "SoundTypes.h" + + +TntTile::TntTile(int id) : Tile(id, Material::explosive) +{ + iconTop = NULL; + iconBottom = NULL; +} + +Icon *TntTile::getTexture(int face, int data) +{ + if (face == Facing::DOWN) return iconBottom; + if (face == Facing::UP) return iconTop; + return icon; +} + +void TntTile::onPlace(Level *level, int x, int y, int z) +{ + Tile::onPlace(level, x, y, z); + if (level->hasNeighborSignal(x, y, z) && app.GetGameHostOption(eGameHostOption_TNT)) + { + destroy(level, x, y, z, EXPLODE_BIT); + level->setTile(x, y, z, 0); + } +} + +void TntTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (type > 0 && Tile::tiles[type]->isSignalSource()) + { + if (level->hasNeighborSignal(x, y, z) && app.GetGameHostOption(eGameHostOption_TNT)) + { + destroy(level, x, y, z, EXPLODE_BIT); + level->setTile(x, y, z, 0); + } + } +} + +int TntTile::getResourceCount(Random *random) +{ + return 1; +} + +void TntTile::wasExploded(Level *level, int x, int y, int z) +{ + // 4J - added - don't every create on the client, I think this must be the cause of a bug reported in the java + // version where white tnts are created in the network game + if (level->isClientSide) return; + + // 4J - added condition to have finite limit of these + // 4J-JEV: Fix for #90934 - Customer Encountered: TU11: Content: Gameplay: TNT blocks are triggered by explosions even though "TNT explodes" option is unchecked. + if( level->newPrimedTntAllowed() && app.GetGameHostOption(eGameHostOption_TNT) ) + { + shared_ptr primed = shared_ptr( new PrimedTnt(level, x + 0.5f, y + 0.5f, z + 0.5f) ); + primed->life = level->random->nextInt(primed->life / 4) + primed->life / 8; + level->addEntity(primed); + } +} + +void TntTile::destroy(Level *level, int x, int y, int z, int data) +{ + if (level->isClientSide) return; + + if ((data & EXPLODE_BIT) == 1 ) + { + // 4J - added condition to have finite limit of these + if( level->newPrimedTntAllowed() && app.GetGameHostOption(eGameHostOption_TNT) ) + { + shared_ptr tnt = shared_ptr( new PrimedTnt(level, x + 0.5f, y + 0.5f, z + 0.5f) ); + level->addEntity(tnt); + level->playSound(tnt, eSoundType_RANDOM_FUSE, 1, 1.0f); + } + } +} + +bool TntTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if (soundOnly) return false; + if (player->getSelectedItem() != NULL && player->getSelectedItem()->id == Item::flintAndSteel_Id) + { + destroy(level, x, y, z, EXPLODE_BIT); + level->setTile(x, y, z, 0); + return true; + } + return Tile::use(level, x, y, z, player, clickedFace, clickX, clickY, clickZ); +} + +void TntTile::entityInside(Level *level, int x, int y, int z, shared_ptr entity) +{ + if (entity->GetType() == eTYPE_ARROW && !level->isClientSide) + { + // 4J Stu - Don't need to cast this + //shared_ptr arrow = dynamic_pointer_cast(entity); + if (entity->isOnFire()) + { + destroy(level, x, y, z, EXPLODE_BIT); + level->setTile(x, y, z, 0); + } + } +} + +shared_ptr TntTile::getSilkTouchItemInstance(int data) +{ + return nullptr; +} + +void TntTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"tnt_side"); + iconTop = iconRegister->registerIcon(L"tnt_top"); + iconBottom = iconRegister->registerIcon(L"tnt_bottom"); +} \ No newline at end of file diff --git a/Minecraft.World/TntTile.h b/Minecraft.World/TntTile.h new file mode 100644 index 00000000..27b788c6 --- /dev/null +++ b/Minecraft.World/TntTile.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Tile.h" +class ChunkRebuildData; +class TntTile : public Tile +{ + friend class ChunkRebuildData; +private: + Icon *iconTop; + Icon *iconBottom; +public: + static const int EXPLODE_BIT = 1; + TntTile(int id); + + Icon *getTexture(int face, int data); + virtual void onPlace(Level *level, int x, int y, int z); + + void neighborChanged(Level *level, int x, int y, int z, int type); + + int getResourceCount(Random *random); + + void wasExploded(Level *level, int x, int y, int z); + + void destroy(Level *level, int x, int y, int z, int data); + + bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + + void entityInside(Level *level, int x, int y, int z, shared_ptr entity); + virtual shared_ptr getSilkTouchItemInstance(int data); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/ToggleDownfallCommand.cpp b/Minecraft.World/ToggleDownfallCommand.cpp new file mode 100644 index 00000000..1ae2f3a9 --- /dev/null +++ b/Minecraft.World/ToggleDownfallCommand.cpp @@ -0,0 +1,30 @@ +#include "stdafx.h" +#include "..\Minecraft.Client\MinecraftServer.h" +#include "..\Minecraft.Client\ServerLevel.h" +#include "net.minecraft.commands.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.storage.h" +#include "net.minecraft.network.packet.h" +#include "ToggleDownfallCommand.h" + +EGameCommand ToggleDownfallCommand::getId() +{ + return eGameCommand_ToggleDownfall; +} + +void ToggleDownfallCommand::execute(shared_ptr source, byteArray commandData) +{ + doToggleDownfall(); + logAdminAction(source, ChatPacket::e_ChatCustom, L"commands.downfall.success"); +} + +void ToggleDownfallCommand::doToggleDownfall() +{ + MinecraftServer::getInstance()->levels[0]->toggleDownfall(); + MinecraftServer::getInstance()->levels[0]->getLevelData()->setThundering(true); +} + +shared_ptr ToggleDownfallCommand::preparePacket() +{ + return shared_ptr( new GameCommandPacket(eGameCommand_ToggleDownfall, byteArray() )); +} \ No newline at end of file diff --git a/Minecraft.World/ToggleDownfallCommand.h b/Minecraft.World/ToggleDownfallCommand.h new file mode 100644 index 00000000..2954962b --- /dev/null +++ b/Minecraft.World/ToggleDownfallCommand.h @@ -0,0 +1,17 @@ +#pragma once +#include "Command.h" + +class GameCommandPacket; + +class ToggleDownfallCommand : public Command +{ +public: + virtual EGameCommand getId(); + virtual void execute(shared_ptr source, byteArray commandData); + +protected: + void doToggleDownfall(); + +public: + static shared_ptr preparePacket(); +}; \ No newline at end of file diff --git a/Minecraft.World/ToolRecipies.cpp b/Minecraft.World/ToolRecipies.cpp new file mode 100644 index 00000000..8524924b --- /dev/null +++ b/Minecraft.World/ToolRecipies.cpp @@ -0,0 +1,128 @@ +//package net.minecraft.world.item.crafting; + +//import net.minecraft.world.item.*; +//import net.minecraft.world.level.tile.Tile; +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "Tile.h" +#include "Recipy.h" +#include "Recipes.h" +#include "ToolRecipies.h" + +// 4J-PB - adding "" on the end of these so we can detect it +wstring ToolRecipies::shapes[][4] = +{ + {L"XXX", // + L" # ",// + L" # "},// + + {L"X",// + L"#",// + L"#"},// + + {L"XX",// + L"X#",// + L" #"},// + + {L"XX",// + L" #",// + L" #"},// +}; + +/* + Object[][] map = { + {Tile.wood, Tile.stoneBrick, Item.ironIngot, Item.diamond, Item.goldIngot}, + {Item.pickAxe_wood, Item.pickAxe_stone, Item.pickAxe_iron, Item.pickAxe_diamond, Item.pickAxe_gold}, + {Item.shovel_wood, Item.shovel_stone, Item.shovel_iron, Item.shovel_diamond, Item.shovel_gold}, + {Item.hatchet_wood, Item.hatchet_stone, Item.hatchet_iron, Item.hatchet_diamond, Item.hatchet_gold}, + {Item.hoe_wood, Item.hoe_stone, Item.hoe_iron, Item.hoe_diamond, Item.hoe_gold}, + }; + */ +//#define ADD_OBJECT(a,b) a.push_back(new Object(b)) + +void ToolRecipies::_init() +{ + map = new vector [MAX_TOOL_RECIPES]; + + ADD_OBJECT(map[0],Tile::wood); + ADD_OBJECT(map[0],Tile::stoneBrick); + ADD_OBJECT(map[0],Item::ironIngot); + ADD_OBJECT(map[0],Item::diamond); + ADD_OBJECT(map[0],Item::goldIngot); + + ADD_OBJECT(map[1],Item::pickAxe_wood); + ADD_OBJECT(map[1],Item::pickAxe_stone); + ADD_OBJECT(map[1],Item::pickAxe_iron); + ADD_OBJECT(map[1],Item::pickAxe_diamond); + ADD_OBJECT(map[1],Item::pickAxe_gold); + + ADD_OBJECT(map[2],Item::shovel_wood); + ADD_OBJECT(map[2],Item::shovel_stone); + ADD_OBJECT(map[2],Item::shovel_iron); + ADD_OBJECT(map[2],Item::shovel_diamond); + ADD_OBJECT(map[2],Item::shovel_gold); + + ADD_OBJECT(map[3],Item::hatchet_wood); + ADD_OBJECT(map[3],Item::hatchet_stone); + ADD_OBJECT(map[3],Item::hatchet_iron); + ADD_OBJECT(map[3],Item::hatchet_diamond); + ADD_OBJECT(map[3],Item::hatchet_gold); + + ADD_OBJECT(map[4],Item::hoe_wood); + ADD_OBJECT(map[4],Item::hoe_stone); + ADD_OBJECT(map[4],Item::hoe_iron); + ADD_OBJECT(map[4],Item::hoe_diamond); + ADD_OBJECT(map[4],Item::hoe_gold); +} + +void ToolRecipies::addRecipes(Recipes *r) +{ + wchar_t wchTypes[7]; + wchTypes[6]=0; + + for (unsigned int m = 0; m < map[0].size(); m++) + { + Object *pObjMaterial = map[0].at(m); + + for (int t=0; titem; + + wchTypes[0]=L'w'; + wchTypes[1]=L'c'; + wchTypes[2]=L'i'; + wchTypes[3]=L'c'; + wchTypes[5]=L'g'; + if(pObjMaterial->GetType()==eType_TILE) + { + wchTypes[4]=L't'; + r->addShapedRecipy(new ItemInstance(target), + wchTypes, + shapes[t], + + L'#', Item::stick, + L'X', pObjMaterial->tile, + L'T'); + } + else + { + // must be Item + wchTypes[4]=L'i'; + r->addShapedRecipy(new ItemInstance(target), + wchTypes, + shapes[t], + + L'#', Item::stick, + L'X', pObjMaterial->item, + L'T'); + } + } + } + r->addShapedRecipy(new ItemInstance((Item *)Item::shears), + L"sscig", + L" #", // + L"# ", // + L'#', Item::ironIngot, + L'T' + ); +} \ No newline at end of file diff --git a/Minecraft.World/ToolRecipies.h b/Minecraft.World/ToolRecipies.h new file mode 100644 index 00000000..7c433b4c --- /dev/null +++ b/Minecraft.World/ToolRecipies.h @@ -0,0 +1,24 @@ +//package net.minecraft.world.item.crafting; + +//import net.minecraft.world.item.*; +//import net.minecraft.world.level.tile.Tile; +#pragma once + +#define MAX_TOOL_RECIPES 5 + +class Recipes; + +class ToolRecipies +{ +public: + // 4J - added for common ctor code + void _init(); + ToolRecipies() {_init();} + +private: + static wstring shapes[][4]; + vector *map; + +public: + void addRecipes(Recipes *r); +}; diff --git a/Minecraft.World/TopSnowTile.cpp b/Minecraft.World/TopSnowTile.cpp new file mode 100644 index 00000000..9bcf5527 --- /dev/null +++ b/Minecraft.World/TopSnowTile.cpp @@ -0,0 +1,182 @@ +#include "stdafx.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.item.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.h" +#include "TopSnowTile.h" + +const int TopSnowTile::MAX_HEIGHT = 6; + +const int TopSnowTile::HEIGHT_MASK = 7; // max 8 steps + + +TopSnowTile::TopSnowTile(int id) : Tile(id, Material::topSnow,isSolidRender()) +{ + setShape(0, 0, 0, 1, 2 / 16.0f, 1); + setTicking(true); +} + +void TopSnowTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"snow"); +} + +AABB *TopSnowTile::getAABB(Level *level, int x, int y, int z) +{ + int height = level->getData(x, y, z) & HEIGHT_MASK; + if (height >= (MAX_HEIGHT / 2)) + { + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + return AABB::newTemp(x + tls->xx0, y + tls->yy0, z + tls->zz0, x + tls->xx1, y + .5f, z + tls->zz1); + } + return NULL; +} + +float TopSnowTile::getHeight(Level *level, int x, int y, int z) +{ + int height = level->getData(x, y, z) & HEIGHT_MASK; + return 2 * (1 + height) / 16.0f; +} + + +bool TopSnowTile::blocksLight() +{ + return false; +} + + +bool TopSnowTile::isSolidRender(bool isServerLevel) +{ + return false; +} + + +bool TopSnowTile::isCubeShaped() +{ + return false; +} + +void TopSnowTile::updateDefaultShape() +{ + updateShape(0); +} + +void TopSnowTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + updateShape(level->getData(x, y, z)); +} + +void TopSnowTile::updateShape(int data) +{ + int height = data & HEIGHT_MASK; + float o = 2 * (1 + height) / 16.0f; + setShape(0, 0, 0, 1, o, 1); +} + +bool TopSnowTile::mayPlace(Level *level, int x, int y, int z) +{ + int t = level->getTile(x, y - 1, z); + // 4J Stu - Assume when placing that this is the server level and we don't care how it's going to be rendered + // Fix for #9407 - Gameplay: Destroying a block of snow on top of trees, removes any adjacent snow. + if (t == 0 || (t != Tile::leaves_Id && !Tile::tiles[t]->isSolidRender(true))) return false; + return level->getMaterial(x, y - 1, z)->blocksMotion(); +} + + +void TopSnowTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + checkCanSurvive(level, x, y, z); +} + + +bool TopSnowTile::checkCanSurvive(Level *level, int x, int y, int z) +{ + if (!mayPlace(level, x, y, z)) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + return false; + } + return true; +} + + +void TopSnowTile::playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data) +{ + int type = Item::snowBall->id; + float s = 0.7f; + double xo = level->random->nextFloat() * s + (1 - s) * 0.5; + double yo = level->random->nextFloat() * s + (1 - s) * 0.5; + double zo = level->random->nextFloat() * s + (1 - s) * 0.5; + shared_ptr item = shared_ptr( new ItemEntity(level, x + xo, y + yo, z + zo, shared_ptr( new ItemInstance(type, 1, 0) ) ) ); + item->throwTime = 10; + level->addEntity(item); + level->setTile(x, y, z, 0); +} + + +int TopSnowTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::snowBall->id; +} + + +int TopSnowTile::getResourceCount(Random *random) +{ + return 0; +} + + +void TopSnowTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (level->getBrightness(LightLayer::Block, x, y, z) > 11) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } +} + + +bool TopSnowTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + if (face == 1) return true; + // 4J - don't render faces if neighbouring tiles are also TopSnowTile with at least the same height as this one + // Otherwise we get horrible artifacts from the non-manifold geometry created. Fixes bug #8506 + if ( ( level->getTile(x,y,z) == Tile::topSnow_Id ) && ( face >= 2 ) ) + { + int h0 = level->getData(x,y,z) & HEIGHT_MASK; + int xx = x; + int yy = y; + int zz = z; + // Work out coords of tile who's face we're considering (rather than it's neighbour which is passed in here as x,y,z already + // offsetting by the face direction) + switch(face) + { + case 2: + zz += 1; + break; + case 3: + zz -= 1; + break; + case 4: + xx += 1; + break; + case 5: + xx -= 1; + break; + default: + break; + } + int h1 = level->getData(xx,yy,zz) & HEIGHT_MASK; + if( h0 >= h1 ) return false; + } + return Tile::shouldRenderFace(level, x, y, z, face); +} + +bool TopSnowTile::shouldTileTick(Level *level, int x,int y,int z) +{ + return level->getBrightness(LightLayer::Block, x, y, z) > 11; +} diff --git a/Minecraft.World/TopSnowTile.h b/Minecraft.World/TopSnowTile.h new file mode 100644 index 00000000..059f0f42 --- /dev/null +++ b/Minecraft.World/TopSnowTile.h @@ -0,0 +1,66 @@ +#pragma once + +#include "Tile.h" + +class Player; + +class TopSnowTile : public Tile +{ + friend class Tile; +public: + static const int MAX_HEIGHT; + static const int HEIGHT_MASK; + +protected: + TopSnowTile(int id); + +public: + void registerIcons(IconRegister *iconRegister); + AABB *getAABB(Level *level, int x, int y, int z); + +public: + static float getHeight(Level *level, int x, int y, int z); + +public: + bool blocksLight(); + +public: + bool isSolidRender(bool isServerLevel = false); + +public: + bool isCubeShaped(); + +public: + void updateDefaultShape(); + void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + +protected: + void updateShape(int data); + +public: + bool mayPlace(Level *level, int x, int y, int z); + +public: + void neighborChanged(Level *level, int x, int y, int z, int type); + +private: + bool checkCanSurvive(Level *level, int x, int y, int z); + +public: + void playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data); + +public: + int getResource(int data, Random *random, int playerBonusLevel); + +public: + int getResourceCount(Random *random); + +public: + void tick(Level *level, int x, int y, int z, Random *random); + +public: + bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + + // 4J Added so we can check before we try to add a tile to the tick list if it's actually going to do seomthing + virtual bool shouldTileTick(Level *level, int x,int y,int z); +}; diff --git a/Minecraft.World/TorchTile.cpp b/Minecraft.World/TorchTile.cpp new file mode 100644 index 00000000..ea1d2ab7 --- /dev/null +++ b/Minecraft.World/TorchTile.cpp @@ -0,0 +1,238 @@ +#include "stdafx.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "TorchTile.h" + +TorchTile::TorchTile(int id) : Tile(id, Material::decoration,isSolidRender()) +{ + this->setTicking(true); +} + +AABB *TorchTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +AABB *TorchTile::getTileAABB(Level *level, int x, int y, int z) +{ + updateShape(level, x, y, z); + return Tile::getTileAABB(level, x, y, z); +} + +void TorchTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + setShape(level->getData(x, y, z)); +} + +void TorchTile::setShape(int data) +{ + int dir = data & 7; + + float r = 0.15f; + if (dir == 1) + { + setShape(0, 0.2f, 0.5f - r, r * 2, 0.8f, 0.5f + r); + } + else if (dir == 2) + { + setShape(1 - r * 2, 0.2f, 0.5f - r, 1, 0.8f, 0.5f + r); + } + else if (dir == 3) + { + setShape(0.5f - r, 0.2f, 0, 0.5f + r, 0.8f, r * 2); + } + else if (dir == 4) + { + setShape(0.5f - r, 0.2f, 1 - r * 2, 0.5f + r, 0.8f, 1); + } + else + { + r = 0.1f; + setShape(0.5f - r, 0.0f, 0.5f - r, 0.5f + r, 0.6f, 0.5f + r); + } +} + +bool TorchTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool TorchTile::isCubeShaped() +{ + return false; +} + +int TorchTile::getRenderShape() +{ + return Tile::SHAPE_TORCH; +} + +bool TorchTile::isConnection(Level *level, int x, int y, int z) +{ + if (level->isTopSolidBlocking(x, y, z)) + { + return true; + } + int tile = level->getTile(x, y, z); + if (tile == Tile::fence_Id || tile == Tile::netherFence_Id + || tile == Tile::glass_Id || tile == Tile::cobbleWall_Id) + { + return true; + } + return false; +} + +bool TorchTile::mayPlace(Level *level, int x, int y, int z) +{ + if (level->isSolidBlockingTileInLoadedChunk(x - 1, y, z, true)) + { + return true; + } + else if (level->isSolidBlockingTileInLoadedChunk(x + 1, y, z, true)) + { + return true; + } + else if (level->isSolidBlockingTileInLoadedChunk(x, y, z - 1, true)) + { + return true; + } + else if (level->isSolidBlockingTileInLoadedChunk(x, y, z + 1, true)) + { + return true; + } + else if (isConnection(level, x, y - 1, z)) + { + return true; + } + return false; +} + +int TorchTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) +{ + int dir = itemValue; + + if (face == 1 && isConnection(level, x, y - 1, z)) dir = 5; + if (face == 2 && level->isSolidBlockingTileInLoadedChunk(x, y, z + 1, true)) dir = 4; + if (face == 3 && level->isSolidBlockingTileInLoadedChunk(x, y, z - 1, true)) dir = 3; + if (face == 4 && level->isSolidBlockingTileInLoadedChunk(x + 1, y, z, true)) dir = 2; + if (face == 5 && level->isSolidBlockingTileInLoadedChunk(x - 1, y, z, true)) dir = 1; + + return dir; +} + +void TorchTile::tick(Level *level, int x, int y, int z, Random *random) +{ + Tile::tick(level, x, y, z, random); + if (level->getData(x, y, z) == 0) onPlace(level, x, y, z); +} + +void TorchTile::onPlace(Level *level, int x, int y, int z) +{ + if(level->getData(x,y,z) == 0) + { + if (level->isSolidBlockingTileInLoadedChunk(x - 1, y, z, true)) + { + level->setData(x, y, z, 1); + } + else if (level->isSolidBlockingTileInLoadedChunk(x + 1, y, z, true)) + { + level->setData(x, y, z, 2); + } + else if (level->isSolidBlockingTileInLoadedChunk(x, y, z - 1, true)) + { + level->setData(x, y, z, 3); + } + else if (level->isSolidBlockingTileInLoadedChunk(x, y, z + 1, true)) + { + level->setData(x, y, z, 4); + } + else if (isConnection(level, x, y - 1, z)) + { + level->setData(x, y, z, 5); + } + } + checkCanSurvive(level, x, y, z); +} + +void TorchTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (checkCanSurvive(level, x, y, z)) + { + int dir = level->getData(x, y, z); + bool replace = false; + + if (!level->isSolidBlockingTileInLoadedChunk(x - 1, y, z, true) && dir == 1) replace = true; + if (!level->isSolidBlockingTileInLoadedChunk(x + 1, y, z, true) && dir == 2) replace = true; + if (!level->isSolidBlockingTileInLoadedChunk(x, y, z - 1, true) && dir == 3) replace = true; + if (!level->isSolidBlockingTileInLoadedChunk(x, y, z + 1, true) && dir == 4) replace = true; + if (!isConnection(level, x, y - 1, z) && dir == 5) replace = true; + + if (replace) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } + } +} + +bool TorchTile::checkCanSurvive(Level *level, int x, int y, int z) +{ + if (!mayPlace(level, x, y, z)) + { + if (level->getTile(x, y, z) == id) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } + return false; + } + return true; +} + +HitResult *TorchTile::clip(Level *level, int x, int y, int z, Vec3 *a, Vec3 *b) +{ + setShape(level->getData(x, y, z)); + + return Tile::clip(level, x, y, z, a, b); +} + +void TorchTile::animateTick(Level *level, int xt, int yt, int zt, Random *random) +{ + int dir = level->getData(xt, yt, zt); + double x = xt + 0.5f; + double y = yt + 0.7f; + double z = zt + 0.5f; + double h = 0.22f; + double r = 0.27f; + if (dir == 1) + { + level->addParticle(eParticleType_smoke, x - r, y + h, z, 0, 0, 0); + level->addParticle(eParticleType_flame, x - r, y + h, z, 0, 0, 0); + } + else if (dir == 2) + { + level->addParticle(eParticleType_smoke, x + r, y + h, z, 0, 0, 0); + level->addParticle(eParticleType_flame, x + r, y + h, z, 0, 0, 0); + } + else if (dir == 3) + { + level->addParticle(eParticleType_smoke, x, y + h, z - r, 0, 0, 0); + level->addParticle(eParticleType_flame, x, y + h, z - r, 0, 0, 0); + } + else if (dir == 4) + { + level->addParticle(eParticleType_smoke, x, y + h, z + r, 0, 0, 0); + level->addParticle(eParticleType_flame, x, y + h, z + r, 0, 0, 0); + } + else + { + level->addParticle(eParticleType_smoke, x, y, z, 0, 0, 0); + level->addParticle(eParticleType_flame, x, y, z, 0, 0, 0); + } +} + +bool TorchTile::shouldTileTick(Level *level, int x,int y,int z) +{ + return level->getData(x, y, z) == 0; +} diff --git a/Minecraft.World/TorchTile.h b/Minecraft.World/TorchTile.h new file mode 100644 index 00000000..7e27f2ee --- /dev/null +++ b/Minecraft.World/TorchTile.h @@ -0,0 +1,36 @@ +#pragma once +#include "Tile.h" +#include "Definitions.h" + +class Random; +class HitResult; + +class TorchTile : public Tile +{ + friend class Tile; +protected: + TorchTile(int id); +public: + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual AABB *getTileAABB(Level *level, int x, int y, int z); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + using Tile::setShape; + virtual void setShape(int data); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual int getRenderShape(); + bool isConnection(Level *level, int x, int y, int z); + virtual bool mayPlace(Level *level, int x, int y, int z); + virtual int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual void onPlace(Level *level, int x, int y, int z); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); +private: + virtual bool checkCanSurvive(Level *level, int x, int y, int z); +public: + virtual HitResult *clip(Level *level, int x, int y, int z, Vec3 *a, Vec3 *b); + virtual void animateTick(Level *level, int xt, int yt, int zt, Random *random); + + // 4J Added so we can check before we try to add a tile to the tick list if it's actually going to do seomthing + virtual bool shouldTileTick(Level *level, int x,int y,int z); +}; diff --git a/Minecraft.World/TownFeature.h b/Minecraft.World/TownFeature.h new file mode 100644 index 00000000..446daaea --- /dev/null +++ b/Minecraft.World/TownFeature.h @@ -0,0 +1,5 @@ +#pragma once + +class TownFeature : public LargeFeature +{ +}; \ No newline at end of file diff --git a/Minecraft.World/TradeItemPacket.cpp b/Minecraft.World/TradeItemPacket.cpp new file mode 100644 index 00000000..4148071f --- /dev/null +++ b/Minecraft.World/TradeItemPacket.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "TradeItemPacket.h" + + + +TradeItemPacket::TradeItemPacket() +{ + containerId = 0; + offer = 0; +} + +TradeItemPacket::TradeItemPacket(int containerId, int offer) +{ + this->containerId = containerId; + this->offer = offer; +} + +void TradeItemPacket::handle(PacketListener *listener) +{ + listener->handleTradeItem(shared_from_this()); +} + +void TradeItemPacket::read(DataInputStream *dis) //throws IOException +{ + containerId = dis->readInt(); + offer = dis->readInt(); +} + +void TradeItemPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(containerId); + dos->writeInt(offer); +} + +int TradeItemPacket::getEstimatedSize() +{ + return 8; +} diff --git a/Minecraft.World/TradeItemPacket.h b/Minecraft.World/TradeItemPacket.h new file mode 100644 index 00000000..ecd0f707 --- /dev/null +++ b/Minecraft.World/TradeItemPacket.h @@ -0,0 +1,32 @@ +#pragma once + +// 4J ADDED PACKET + +using namespace std; + +#include "Packet.h" + +#include "stdafx.h" +#include +#include "PacketListener.h" + +class TradeItemPacket : public Packet, public enable_shared_from_this +{ +public: + int containerId; + int offer; + + TradeItemPacket(); + TradeItemPacket(int containerId, int offer); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new TradeItemPacket()); } + virtual int getId() { return 151; } +}; + + diff --git a/Minecraft.World/TradeWithPlayerGoal.cpp b/Minecraft.World/TradeWithPlayerGoal.cpp new file mode 100644 index 00000000..e8b791d1 --- /dev/null +++ b/Minecraft.World/TradeWithPlayerGoal.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.npc.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.inventory.h" +#include "TradeWithPlayerGoal.h" + +TradeWithPlayerGoal::TradeWithPlayerGoal(Villager *mob) +{ + this->mob = mob; + setRequiredControlFlags(Control::JumpControlFlag | Control::MoveControlFlag); +} + +bool TradeWithPlayerGoal::canUse() +{ + if (!mob->isAlive()) return false; + if (mob->isInWater()) return false; + if (!mob->onGround) return false; + if (mob->hurtMarked) return false; + + shared_ptr trader = mob->getTradingPlayer(); + if (trader == NULL) + { + // no interaction + return false; + } + + if (mob->distanceToSqr(trader) > (4 * 4)) + { + // too far away + return false; + } + + if (!(trader->containerMenu == trader->inventoryMenu)) + { + // closed container + return false; + } + + return true; +} + +void TradeWithPlayerGoal::start() +{ + mob->getNavigation()->stop(); +} + +void TradeWithPlayerGoal::stop() +{ + mob->setTradingPlayer(nullptr); +} \ No newline at end of file diff --git a/Minecraft.World/TradeWithPlayerGoal.h b/Minecraft.World/TradeWithPlayerGoal.h new file mode 100644 index 00000000..1a0e9040 --- /dev/null +++ b/Minecraft.World/TradeWithPlayerGoal.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Goal.h" + +class Villager; + +class TradeWithPlayerGoal : public Goal +{ +private: + Villager *mob; // This is the owner of the goal + +public: + TradeWithPlayerGoal(Villager *mob); + + bool canUse(); + void start(); + void stop(); +}; \ No newline at end of file diff --git a/Minecraft.World/TransparentTile.cpp b/Minecraft.World/TransparentTile.cpp new file mode 100644 index 00000000..129b16b2 --- /dev/null +++ b/Minecraft.World/TransparentTile.cpp @@ -0,0 +1,25 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "TransparentTile.h" + +TransparentTile::TransparentTile(int id, Material *material, bool allowSame, bool isSolidRender) : Tile(id, material,isSolidRender) +{ + this->allowSame = allowSame; +} + +bool TransparentTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool TransparentTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + int id = level->getTile(x, y, z); + if (!allowSame && id == this->id) return false; + return Tile::shouldRenderFace(level, x, y, z, face); +} + +bool TransparentTile::blocksLight() +{ + return false; +} \ No newline at end of file diff --git a/Minecraft.World/TransparentTile.h b/Minecraft.World/TransparentTile.h new file mode 100644 index 00000000..0601be4b --- /dev/null +++ b/Minecraft.World/TransparentTile.h @@ -0,0 +1,14 @@ +#pragma once +#include "Tile.h" + +class TransparentTile : public Tile +{ +protected: + bool allowSame; +protected: + TransparentTile(int id, Material *material, bool allowSame, bool isSolidRender = false); +public: + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + virtual bool blocksLight(); +}; \ No newline at end of file diff --git a/Minecraft.World/TrapDoorTile.cpp b/Minecraft.World/TrapDoorTile.cpp new file mode 100644 index 00000000..fc9d6580 --- /dev/null +++ b/Minecraft.World/TrapDoorTile.cpp @@ -0,0 +1,209 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.h" +#include "TrapDoorTile.h" + + +TrapDoorTile::TrapDoorTile(int id, Material *material) : Tile(id, material,isSolidRender()) +{ + float r = 0.5f; + float h = 1.0f; + setShape(0.5f - r, 0, 0.5f - r, 0.5f + r, h, 0.5f + r); +} + +bool TrapDoorTile::blocksLight() +{ + return false; +} + + +bool TrapDoorTile::isSolidRender(bool isServerLevel) +{ + return false; +} + + +bool TrapDoorTile::isCubeShaped() +{ + return false; +} + +bool TrapDoorTile::isPathfindable(LevelSource *level, int x, int y, int z) +{ + return !isOpen(level->getData(x, y, z)); +} + +int TrapDoorTile::getRenderShape() +{ + return Tile::SHAPE_BLOCK; +} + + +AABB *TrapDoorTile::getTileAABB(Level *level, int x, int y, int z) +{ + updateShape(level, x, y, z); + return Tile::getTileAABB(level, x, y, z); +} + + +AABB *TrapDoorTile::getAABB(Level *level, int x, int y, int z) +{ + updateShape(level, x, y, z); + return Tile::getAABB(level, x, y, z); +} + + +void TrapDoorTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + setShape(level->getData(x, y, z)); +} + + +void TrapDoorTile::updateDefaultShape() +{ + float r = 3 / 16.0f; + setShape(0, 0.5f - r / 2, 0, 1, 0.5f + r / 2, 1); +} + + +void TrapDoorTile::setShape(int data) +{ + + float r = 3 / 16.0f; + Tile::setShape(0, 0, 0, 1, r, 1); + if (isOpen(data)) + { + if ((data & 3) == 0) setShape(0, 0, 1 - r, 1, 1, 1); + if ((data & 3) == 1) setShape(0, 0, 0, 1, 1, r); + if ((data & 3) == 2) setShape(1 - r, 0, 0, 1, 1, 1); + if ((data & 3) == 3) setShape(0, 0, 0, r, 1, 1); + } +} + + +void TrapDoorTile::attack(Level *level, int x, int y, int z, shared_ptr player) +{ + use(level, x, y, z, player, 0, 0, 0, 0); +} + +// 4J-PB - Adding a TestUse for tooltip display +bool TrapDoorTile::TestUse() +{ + return true; +} + +bool TrapDoorTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if (material == Material::metal) return true; + + if (soundOnly) + { + // 4J - added - just do enough to play the sound + level->levelEvent(player, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); + return false; + } + + int dir = level->getData(x, y, z); + level->setData(x, y, z, dir ^ 4); + + level->levelEvent(player, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); + return true; +} + + +void TrapDoorTile::setOpen(Level *level, int x, int y, int z, bool shouldOpen) +{ + int dir = level->getData(x, y, z); + + bool wasOpen = (dir & 4) > 0; + if (wasOpen == shouldOpen) return; + + level->setData(x, y, z, dir ^ 4); + + level->levelEvent(nullptr, LevelEvent::SOUND_OPEN_DOOR, x, y, z, 0); +} + +void TrapDoorTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (level->isClientSide) return; + + int data = level->getData(x, y, z); + int xt = x; + int zt = z; + if ((data & 3) == 0) zt++; + if ((data & 3) == 1) zt--; + if ((data & 3) == 2) xt++; + if ((data & 3) == 3) xt--; + + + if (!attachesTo(level->getTile(xt, y, zt))) + { + level->setTile(x, y, z, 0); + spawnResources(level, x, y, z, data, 0); + } + + bool signal = level->hasNeighborSignal(x, y, z); + if( signal || ((type > 0 && Tile::tiles[type]->isSignalSource())) ) + { + setOpen(level, x, y, z, signal); + } +} + +HitResult *TrapDoorTile::clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b) +{ + updateShape(level, xt, yt, zt); + return Tile::clip(level, xt, yt, zt, a, b); +} + +int TrapDoorTile::getDir(int dir) +{ + if ((dir & 4) == 0) + { + return ((dir - 1) & 3); + } + else + { + return (dir & 3); + } +} + +int TrapDoorTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) +{ + int dir = 0; + if (face == 2) dir = 0; + if (face == 3) dir = 1; + if (face == 4) dir = 2; + if (face == 5) dir = 3; + if (face != Facing::UP && face != Facing::DOWN && clickY > 0.5f) dir |= TOP_MASK; + return dir; +} + +bool TrapDoorTile::mayPlace(Level *level, int x, int y, int z, int face) +{ + if (face == 0) return false; + if (face == 1) return false; + if (face == 2) z++; + if (face == 3) z--; + if (face == 4) x++; + if (face == 5) x--; + + return attachesTo(level->getTile(x, y, z)); +} + +bool TrapDoorTile::isOpen(int data) +{ + return (data & 4) != 0; +} + +bool TrapDoorTile::attachesTo(int id) +{ + if (id <= 0) + { + return false; + } + Tile *tile = Tile::tiles[id]; + + return tile != NULL && (tile->material->isSolidBlocking() && tile->isCubeShaped()) || tile == Tile::lightGem || (dynamic_cast(tile) != NULL) || (dynamic_cast(tile) != NULL); +} \ No newline at end of file diff --git a/Minecraft.World/TrapDoorTile.h b/Minecraft.World/TrapDoorTile.h new file mode 100644 index 00000000..19cec150 --- /dev/null +++ b/Minecraft.World/TrapDoorTile.h @@ -0,0 +1,87 @@ +#pragma once + +#include "Tile.h" + +class Player; +class HitResult; + +class TrapDoorTile : public Tile +{ + friend class Tile; +private: + static const int TOP_MASK = 0x8; + +protected: + TrapDoorTile(int id, Material *material); + +/* + * public int getTexture(int face, int data) { if (face == 0 || face == 1) + * return tex; int dir = getDir(data); if ((dir == 0 || dir == 2) ^ (face <= 3)) + * { return tex; } int tt = (dir / 2 + ((face & 1) ^ dir)); tt += ((data & 4) / + * 4); int texture = tex - (data & 8) * 2; if ((tt & 1) != 0) { texture = + * -texture; } // if (getDir(data)==0 // tt-=((face+data&3)&1)^((data&4)>>2); + * return texture; } + */ + +public: + bool blocksLight(); + +public: + bool isSolidRender(bool isServerLevel = false); + +public: + bool isCubeShaped(); + bool isPathfindable(LevelSource *level, int x, int y, int z); + +public: + int getRenderShape(); + +public: + AABB *getTileAABB(Level *level, int x, int y, int z); + +public: + AABB *getAABB(Level *level, int x, int y, int z); + +public: + void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + +public: + void updateDefaultShape(); + +public: + using Tile::setShape; + void setShape(int data); + +public: + void attack(Level *level, int x, int y, int z, shared_ptr player); + +public: + virtual bool TestUse(); + bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param + +public: + void setOpen(Level *level, int x, int y, int z, bool shouldOpen); + + +public: + void neighborChanged(Level *level, int x, int y, int z, int type); + + +public: + HitResult *clip(Level *level, int xt, int yt, int zt, Vec3 *a, Vec3 *b); + +public: + int getDir(int dir); + +public: + int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + +public: + bool mayPlace(Level *level, int x, int y, int z, int face); + +public: + static bool isOpen(int data); + +private: + static bool attachesTo(int id); +}; \ No newline at end of file diff --git a/Minecraft.World/TrapMenu.cpp b/Minecraft.World/TrapMenu.cpp new file mode 100644 index 00000000..5e59c6fe --- /dev/null +++ b/Minecraft.World/TrapMenu.cpp @@ -0,0 +1,81 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "Container.h" +#include "Slot.h" +#include "TrapMenu.h" + +TrapMenu::TrapMenu(shared_ptr inventory, shared_ptr trap) +{ + this->trap = trap; + + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 3; x++) + { + addSlot(new Slot(trap, x + y * 3, 62 + x * 18, 17 + y * 18)); + } + } + + for (int y = 0; y < 3; y++) + { + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x + y * 9 + 9, 8 + x * 18, 84 + y * 18)); + } + } + for (int x = 0; x < 9; x++) + { + addSlot(new Slot(inventory, x, 8 + x * 18, 70 + 4 * 18)); + } +} + +bool TrapMenu::stillValid(shared_ptr player) +{ + return trap->stillValid(player); +} + +// 4J Stu - Brought forward from 1.2 +shared_ptr TrapMenu::quickMoveStack(shared_ptr player, int slotIndex) +{ + shared_ptr clicked = nullptr; + Slot *slot = slots->at(slotIndex); + if (slot != NULL && slot->hasItem()) + { + shared_ptr stack = slot->getItem(); + clicked = stack->copy(); + + if (slotIndex < INV_SLOT_START) + { + if (!moveItemStackTo(stack, INV_SLOT_START, USE_ROW_SLOT_END, true)) + { + return nullptr; + } + } + else + { + if (!moveItemStackTo(stack, 0, INV_SLOT_START, false)) + { + return nullptr; + } + } + if (stack->count == 0) + { + slot->set(nullptr); + } + else + { + slot->setChanged(); + } + if (stack->count == clicked->count) + { + // nothing moved + return nullptr; + } + else + { + slot->onTake(player, stack); + } + } + return clicked; +} \ No newline at end of file diff --git a/Minecraft.World/TrapMenu.h b/Minecraft.World/TrapMenu.h new file mode 100644 index 00000000..e3fb4965 --- /dev/null +++ b/Minecraft.World/TrapMenu.h @@ -0,0 +1,22 @@ +#pragma once + +#include "AbstractContainerMenu.h" + +class DispenserTileEntity; + +class TrapMenu : public AbstractContainerMenu +{ +private: + static const int INV_SLOT_START = 9; + static const int INV_SLOT_END = INV_SLOT_START + 9 * 3; + static const int USE_ROW_SLOT_START = INV_SLOT_END; + static const int USE_ROW_SLOT_END = USE_ROW_SLOT_START + 9; +private: + shared_ptr trap; + +public: + TrapMenu(shared_ptr inventory, shared_ptr trap); + + virtual bool stillValid(shared_ptr player); + virtual shared_ptr quickMoveStack(shared_ptr player, int slotIndex); +}; \ No newline at end of file diff --git a/Minecraft.World/TreeFeature.cpp b/Minecraft.World/TreeFeature.cpp new file mode 100644 index 00000000..0ef4e320 --- /dev/null +++ b/Minecraft.World/TreeFeature.cpp @@ -0,0 +1,179 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "TreeFeature.h" + +TreeFeature::TreeFeature(bool doUpdate) : Feature(doUpdate), baseHeight(4), trunkType(0), leafType(0), addJungleFeatures(false) +{ +} + +TreeFeature::TreeFeature(bool doUpdate, int baseHeight, int trunkType, int leafType, bool addJungleFeatures) : Feature(doUpdate), baseHeight(baseHeight), trunkType(trunkType), leafType(leafType), addJungleFeatures(addJungleFeatures) +{ +} + +bool TreeFeature::place(Level *level, Random *random, int x, int y, int z) +{ + int treeHeight = random->nextInt(3) + baseHeight; + + bool free = true; + if (y < 1 || y + treeHeight + 1 > Level::maxBuildHeight) return false; + + // 4J Stu Added to stop tree features generating areas previously place by game rule generation + if(app.getLevelGenerationOptions() != NULL) + { + PIXBeginNamedEvent(0,"TreeFeature checking intersects"); + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + bool intersects = levelGenOptions->checkIntersects(x - 2, y - 1, z - 2, x + 2, y + treeHeight, z + 2); + PIXEndNamedEvent(); + if(intersects) + { + //app.DebugPrintf("Skipping reeds feature generation as it overlaps a game rule structure\n"); + return false; + } + } + + for (int yy = y; yy <= y + 1 + treeHeight; yy++) + { + int r = 1; + if (yy == y) r = 0; + if (yy >= y + 1 + treeHeight - 2) r = 2; + for (int xx = x - r; xx <= x + r && free; xx++) + { + for (int zz = z - r; zz <= z + r && free; zz++) + { + if (yy >= 0 && yy < Level::maxBuildHeight) + { + int tt = level->getTile(xx, yy, zz); + if (tt != 0 && tt != Tile::leaves_Id && tt != Tile::grass_Id && tt != Tile::dirt_Id && tt != Tile::treeTrunk_Id) free = false; + } + else + { + free = false; + } + } + } + } + + if (!free) return false; + + int belowTile = level->getTile(x, y - 1, z); + if ((belowTile != Tile::grass_Id && belowTile != Tile::dirt_Id) || y >= Level::maxBuildHeight - treeHeight - 1) return false; + + placeBlock(level, x, y - 1, z, Tile::dirt_Id, 0); + + PIXBeginNamedEvent(0,"Placing TreeFeature leaves"); + int grassHeight = 3; + int extraWidth = 0; + // 4J Stu - Generate leaves from the top down to stop having to recalc heightmaps + for (int yy = y + treeHeight; yy >= y - grassHeight + treeHeight; yy--) + { + int yo = yy - (y + treeHeight); + int offs = extraWidth + 1 - yo / 2; + for (int xx = x - offs; xx <= x + offs; xx++) + { + int xo = xx - (x); + for (int zz = z - offs; zz <= z + offs; zz++) + { + int zo = zz - (z); + if (abs(xo) == offs && abs(zo) == offs && (random->nextInt(2) == 0 || yo == 0)) continue; + if (!Tile::solid[level->getTile(xx, yy, zz)]) placeBlock(level, xx, yy, zz, Tile::leaves_Id, leafType); + } + } + } + PIXEndNamedEvent(); + PIXBeginNamedEvent(0,"Placing TreeFeature trunks"); + for (int hh = 0; hh < treeHeight; hh++) + { + int t = level->getTile(x, y + hh, z); + if (t == 0 || t == Tile::leaves_Id) + { + placeBlock(level, x, y + hh, z, Tile::treeTrunk_Id, trunkType); + if (addJungleFeatures && hh > 0) + { + if (random->nextInt(3) > 0 && level->isEmptyTile(x - 1, y + hh, z)) + { + placeBlock(level, x - 1, y + hh, z, Tile::vine_Id, VineTile::VINE_EAST); + } + if (random->nextInt(3) > 0 && level->isEmptyTile(x + 1, y + hh, z)) + { + placeBlock(level, x + 1, y + hh, z, Tile::vine_Id, VineTile::VINE_WEST); + } + if (random->nextInt(3) > 0 && level->isEmptyTile(x, y + hh, z - 1)) + { + placeBlock(level, x, y + hh, z - 1, Tile::vine_Id, VineTile::VINE_SOUTH); + } + if (random->nextInt(3) > 0 && level->isEmptyTile(x, y + hh, z + 1)) + { + placeBlock(level, x, y + hh, z + 1, Tile::vine_Id, VineTile::VINE_NORTH); + } + } + } + } + PIXEndNamedEvent(); + if (addJungleFeatures) + { + PIXBeginNamedEvent(0,"TreeFeature adding vines"); + for (int yy = y - 3 + treeHeight; yy <= y + treeHeight; yy++) + { + int yo = yy - (y + treeHeight); + int offs = 2 - yo / 2; + for (int xx = x - offs; xx <= x + offs; xx++) + { + for (int zz = z - offs; zz <= z + offs; zz++) + { + if (level->getTile(xx, yy, zz) == Tile::leaves_Id) + { + if (random->nextInt(4) == 0 && level->getTile(xx - 1, yy, zz) == 0) + { + addVine(level, xx - 1, yy, zz, VineTile::VINE_EAST); + } + if (random->nextInt(4) == 0 && level->getTile(xx + 1, yy, zz) == 0) + { + addVine(level, xx + 1, yy, zz, VineTile::VINE_WEST); + } + if (random->nextInt(4) == 0 && level->getTile(xx, yy, zz - 1) == 0) + { + addVine(level, xx, yy, zz - 1, VineTile::VINE_SOUTH); + } + if (random->nextInt(4) == 0 && level->getTile(xx, yy, zz + 1) == 0) + { + addVine(level, xx, yy, zz + 1, VineTile::VINE_NORTH); + } + } + } + } + } + PIXEndNamedEvent(); + // also chance for cocoa plants around stem + if (random->nextInt(5) == 0 && treeHeight > 5) + { + PIXBeginNamedEvent(0,"TreeFeature adding cocoa"); + for (int rows = 0; rows < 2; rows++) + { + for (int dir = 0; dir < 4; dir++) + { + if (random->nextInt(4 - rows) == 0) + { + int age = random->nextInt(3); + placeBlock(level, x + Direction::STEP_X[Direction::DIRECTION_OPPOSITE[dir]], y + treeHeight - 5 + rows, z + Direction::STEP_Z[Direction::DIRECTION_OPPOSITE[dir]], + Tile::cocoa_Id, (age << 2) | dir); + } + } + } + PIXEndNamedEvent(); + } + } + + return true; +} + +void TreeFeature::addVine(Level *level, int xx, int yy, int zz, int dir) +{ + placeBlock(level, xx, yy, zz, Tile::vine_Id, dir); + int maxDir = 4; + while (level->getTile(xx, --yy, zz) == 0 && maxDir > 0) + { + placeBlock(level, xx, yy, zz, Tile::vine_Id, dir); + maxDir--; + } +} \ No newline at end of file diff --git a/Minecraft.World/TreeFeature.h b/Minecraft.World/TreeFeature.h new file mode 100644 index 00000000..e29c0b0c --- /dev/null +++ b/Minecraft.World/TreeFeature.h @@ -0,0 +1,20 @@ +#pragma once +#include "Feature.h" + +class TreeFeature : public Feature +{ +private: + const int baseHeight; + const bool addJungleFeatures; + const int trunkType; + const int leafType; + +public: + TreeFeature(bool doUpdate); + TreeFeature(bool doUpdate, int baseHeight, int trunkType, int leafType, bool addJungleFeatures); + + virtual bool place(Level *level, Random *random, int x, int y, int z); + +private: + void addVine(Level *level, int xx, int yy, int zz, int dir); +}; diff --git a/Minecraft.World/TreeTile.cpp b/Minecraft.World/TreeTile.cpp new file mode 100644 index 00000000..29871625 --- /dev/null +++ b/Minecraft.World/TreeTile.cpp @@ -0,0 +1,141 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.piston.h" +#include "net.minecraft.h" +#include "net.minecraft.world.h" +#include "LeafTile.h" + +#include "TreeTile.h" + +const unsigned int TreeTile::TREE_NAMES[TREE_NAMES_LENGTH] = { IDS_TILE_LOG_OAK, + IDS_TILE_LOG_SPRUCE, + IDS_TILE_LOG_BIRCH, + IDS_TILE_LOG_JUNGLE + }; + +const wstring TreeTile::TREE_TEXTURES[] = {L"tree_side", L"tree_spruce", L"tree_birch", L"tree_jungle"}; + +TreeTile::TreeTile(int id) : Tile(id, Material::wood) +{ + icons = NULL; + iconTop = NULL; +} + +int TreeTile::getRenderShape() +{ + return Tile::SHAPE_TREE; +} + +int TreeTile::getResourceCount(Random *random) +{ + return 1; +} + +int TreeTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::treeTrunk_Id; +} + +void TreeTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + int r = LeafTile::REQUIRED_WOOD_RANGE; + int r2 = r + 1; + + if (level->hasChunksAt(x - r2, y - r2, z - r2, x + r2, y + r2, z + r2)) + { + for (int xo = -r; xo <= r; xo++) + for (int yo = -r; yo <= r; yo++) + for (int zo = -r; zo <= r; zo++) + { + int t = level->getTile(x + xo, y + yo, z + zo); + if (t == Tile::leaves_Id) + { + int currentData = level->getData(x + xo, y + yo, z + zo); + if ((currentData & LeafTile::UPDATE_LEAF_BIT) == 0) + { + level->setDataNoUpdate(x + xo, y + yo, z + zo, currentData | LeafTile::UPDATE_LEAF_BIT); + } + } + } + } +} + +void TreeTile::setPlacedBy(Level *level, int x, int y, int z, shared_ptr by) +{ + int type = level->getData(x, y, z) & MASK_TYPE; + int dir = PistonBaseTile::getNewFacing(level, x, y, z, dynamic_pointer_cast(by)); + int facing = 0; + + switch (dir) + { + case Facing::NORTH: + case Facing::SOUTH: + facing = FACING_Z; + break; + case Facing::EAST: + case Facing::WEST: + facing = FACING_X; + break; + case Facing::UP: + case Facing::DOWN: + facing = FACING_Y; + break; + } + + level->setData(x, y, z, type | facing); +} + +Icon *TreeTile::getTexture(int face, int data) +{ + int dir = data & MASK_FACING; + int type = data & MASK_TYPE; + + if (dir == FACING_Y && (face == Facing::UP || face == Facing::DOWN)) + { + return iconTop; + } + else if (dir == FACING_X && (face == Facing::EAST || face == Facing::WEST)) + { + return iconTop; + } + else if (dir == FACING_Z && (face == Facing::NORTH || face == Facing::SOUTH)) + { + return iconTop; + } + + return icons[type]; +} + +unsigned int TreeTile::getDescriptionId(int iData /*= -1*/) +{ + int type = iData & MASK_TYPE; + if(type < 0 ) type = 0; + return TreeTile::TREE_NAMES[type]; +} + +int TreeTile::getSpawnResourcesAuxValue(int data) +{ + return data & MASK_TYPE; +} + +int TreeTile::getWoodType(int data) +{ + return data & MASK_TYPE; +} + +shared_ptr TreeTile::getSilkTouchItemInstance(int data) +{ + // fix to avoid getting silktouched sideways logs + return shared_ptr(new ItemInstance(id, 1, getWoodType(data))); +} + +void TreeTile::registerIcons(IconRegister *iconRegister) +{ + iconTop = iconRegister->registerIcon(L"tree_top"); + icons = new Icon*[TREE_NAMES_LENGTH]; + + for (int i = 0; i < TREE_NAMES_LENGTH; i++) + { + icons[i] = iconRegister->registerIcon(TREE_TEXTURES[i]); + } +} \ No newline at end of file diff --git a/Minecraft.World/TreeTile.h b/Minecraft.World/TreeTile.h new file mode 100644 index 00000000..b7e0d56d --- /dev/null +++ b/Minecraft.World/TreeTile.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Tile.h" + +class ChunkRebuildData; +class Player; + +class TreeTile : public Tile +{ + friend class Tile; + friend class ChunkRebuildData; +public: + static const int DARK_TRUNK = 1; + static const int BIRCH_TRUNK = 2; + static const int JUNGLE_TRUNK = 3; + + static const int MASK_TYPE = 0x3; + static const int MASK_FACING = 0xC; + static const int FACING_Y = 0 << 2; + static const int FACING_X = 1 << 2; + static const int FACING_Z = 2 << 2; + + static const int TREE_NAMES_LENGTH = 4; + + + static const unsigned int TREE_NAMES[TREE_NAMES_LENGTH]; + + static const wstring TREE_TEXTURES[]; + +private: + Icon **icons; + Icon *iconTop; + +protected: + TreeTile(int id); + +public: + virtual int getRenderShape(); + virtual int getResourceCount(Random *random); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual void onRemove(Level *level, int x, int y, int z, int id, int data); + virtual void setPlacedBy(Level *level, int x, int y, int z, shared_ptr by); + virtual Icon *getTexture(int face, int data); + virtual unsigned int getDescriptionId(int iData = -1); + +protected: + int getSpawnResourcesAuxValue(int data); + +public: + static int getWoodType(int data); + void registerIcons(IconRegister *iconRegister); + +protected: + virtual shared_ptr getSilkTouchItemInstance(int data); +}; \ No newline at end of file diff --git a/Minecraft.World/TreeTileItem.cpp b/Minecraft.World/TreeTileItem.cpp new file mode 100644 index 00000000..30406821 --- /dev/null +++ b/Minecraft.World/TreeTileItem.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.tile.h" +#include "TreeTileItem.h" + +TreeTileItem::TreeTileItem(int id, Tile *parentTile) : TileItem(id) +{ + this->parentTile = parentTile; + + setMaxDamage(0); + setStackedByData(true); +} + +Icon *TreeTileItem::getIcon(int itemAuxValue) +{ + return parentTile->getTexture(2, itemAuxValue); +} + +int TreeTileItem::getLevelDataForAuxValue(int auxValue) +{ + return auxValue; +} + +unsigned int TreeTileItem::getDescriptionId(shared_ptr instance) +{ + int auxValue = instance->getAuxValue(); + if (auxValue < 0 || auxValue >= TreeTile::TREE_NAMES_LENGTH) + { + auxValue = 0; + } + return TreeTile::TREE_NAMES[auxValue]; +} \ No newline at end of file diff --git a/Minecraft.World/TreeTileItem.h b/Minecraft.World/TreeTileItem.h new file mode 100644 index 00000000..7f7b2825 --- /dev/null +++ b/Minecraft.World/TreeTileItem.h @@ -0,0 +1,18 @@ +#pragma once +using namespace std; + +#include "TileItem.h" + +class TreeTileItem : public TileItem +{ +private: + Tile *parentTile; + +public: + TreeTileItem(int id, Tile *parentTile); + + virtual Icon *getIcon(int itemAuxValue); + virtual int getLevelDataForAuxValue(int auxValue); + + virtual unsigned int getDescriptionId(shared_ptr instance); +}; \ No newline at end of file diff --git a/Minecraft.World/TripWireSourceTile.cpp b/Minecraft.World/TripWireSourceTile.cpp new file mode 100644 index 00000000..33f857fa --- /dev/null +++ b/Minecraft.World/TripWireSourceTile.cpp @@ -0,0 +1,359 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "TripWireSourceTile.h" + +TripWireSourceTile::TripWireSourceTile(int id) : Tile(id, Material::decoration, isSolidRender()) +{ + this->setTicking(true); +} + +AABB *TripWireSourceTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +bool TripWireSourceTile::blocksLight() +{ + return false; +} + +bool TripWireSourceTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool TripWireSourceTile::isCubeShaped() +{ + return false; +} + +int TripWireSourceTile::getRenderShape() +{ + return Tile::SHAPE_TRIPWIRE_SOURCE; +} + +int TripWireSourceTile::getTickDelay(Level *level) +{ + return 10; +} + +bool TripWireSourceTile::mayPlace(Level *level, int x, int y, int z, int face) +{ + if (face == Facing::NORTH && level->isSolidBlockingTile(x, y, z + 1)) return true; + if (face == Facing::SOUTH && level->isSolidBlockingTile(x, y, z - 1)) return true; + if (face == Facing::WEST && level->isSolidBlockingTile(x + 1, y, z)) return true; + if (face == Facing::EAST && level->isSolidBlockingTile(x - 1, y, z)) return true; + return false; +} + +bool TripWireSourceTile::mayPlace(Level *level, int x, int y, int z) +{ + if (level->isSolidBlockingTile(x - 1, y, z)) + { + return true; + } + else if (level->isSolidBlockingTile(x + 1, y, z)) + { + return true; + } + else if (level->isSolidBlockingTile(x, y, z - 1)) + { + return true; + } + else if (level->isSolidBlockingTile(x, y, z + 1)) + { + return true; + } + return false; +} + +int TripWireSourceTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) +{ + int dir = 0; + + if (face == Facing::NORTH && level->isSolidBlockingTileInLoadedChunk(x, y, z + 1, true)) dir = Direction::NORTH; + if (face == Facing::SOUTH && level->isSolidBlockingTileInLoadedChunk(x, y, z - 1, true)) dir = Direction::SOUTH; + if (face == Facing::WEST && level->isSolidBlockingTileInLoadedChunk(x + 1, y, z, true)) dir = Direction::WEST; + if (face == Facing::EAST && level->isSolidBlockingTileInLoadedChunk(x - 1, y, z, true)) dir = Direction::EAST; + + return dir; +} + +void TripWireSourceTile::finalizePlacement(Level *level, int x, int y, int z, int data) +{ + calculateState(level, x, y, z, id, data, false, -1, 0); +} + +void TripWireSourceTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (type == this->id) return; + if (checkCanSurvive(level, x, y, z)) + { + int data = level->getData(x, y, z); + int dir = data & MASK_DIR; + bool replace = false; + + if (!level->isSolidBlockingTile(x - 1, y, z) && dir == Direction::EAST) replace = true; + if (!level->isSolidBlockingTile(x + 1, y, z) && dir == Direction::WEST) replace = true; + if (!level->isSolidBlockingTile(x, y, z - 1) && dir == Direction::SOUTH) replace = true; + if (!level->isSolidBlockingTile(x, y, z + 1) && dir == Direction::NORTH) replace = true; + + if (replace) + { + this->spawnResources(level, x, y, z, data, 0); + level->setTile(x, y, z, 0); + } + } +} + +void TripWireSourceTile::calculateState(Level *level, int x, int y, int z, int id, int data, bool canUpdate, + /*4J-Jev, these parameters only used with 'updateSource' -->*/ int wireSource, int wireSourceData) +{ + + + int dir = data & MASK_DIR; + bool wasAttached = (data & MASK_ATTACHED) == MASK_ATTACHED; + bool wasPowered = (data & MASK_POWERED) == MASK_POWERED; + bool attached = id == Tile::tripWireSource_Id; // id is only != TripwireSource_id when 'onRemove' + bool powered = false; + bool suspended = !level->isTopSolidBlocking(x, y - 1, z); + int stepX = Direction::STEP_X[dir]; + int stepZ = Direction::STEP_Z[dir]; + int receiverPos = 0; + int wiresData[WIRE_DIST_MAX]; + + // Loop over each tile down the wire, from this tile, to the expected opposing src tile. + for (int i = 1; i < WIRE_DIST_MAX; i++) + { + int xx = x + stepX * i; + int zz = z + stepZ * i; + int tile = level->getTile(xx, y, zz); + + if (tile == Tile::tripWireSource_Id) + { + int otherData = level->getData(xx, y, zz); + + if ((otherData & MASK_DIR) == Direction::DIRECTION_OPPOSITE[dir]) + { + receiverPos = i; + } + + break; + } + else if (tile == Tile::tripWire_Id || i == wireSource) // wireSource is the wiretile that caused an 'updateSource' + { + int wireData = i == wireSource ? wireSourceData : level->getData(xx, y, zz); + bool wireArmed = (wireData & TripWireTile::MASK_DISARMED) != TripWireTile::MASK_DISARMED; + bool wirePowered = (wireData & TripWireTile::MASK_POWERED) == TripWireTile::MASK_POWERED; + bool wireSuspended = (wireData & TripWireTile::MASK_SUSPENDED) == TripWireTile::MASK_SUSPENDED; + attached &= wireSuspended == suspended; + powered |= wireArmed && wirePowered; + + wiresData[i] = wireData; + + if (i == wireSource) + { + level->addToTickNextTick(x, y, z, id, getTickDelay(level)); + attached &= wireArmed; + } + } + else // Non-wire or src tile encountered. + { + wiresData[i] = -1; + attached = false; + } + } + + attached &= receiverPos > WIRE_DIST_MIN; + powered &= attached; + int state = (attached ? MASK_ATTACHED : 0) | (powered ? MASK_POWERED : 0); + data = dir | state; + + if (receiverPos > 0) // If a receiver is detected update it's state and notify it's neighbours. + { + int xx = x + stepX * receiverPos; + int zz = z + stepZ * receiverPos; + int opposite = Direction::DIRECTION_OPPOSITE[dir]; + level->setData(xx, y, zz, opposite | state); + notifyNeighbors(level, xx, y, zz, opposite); + + playSound(level, xx, y, zz, attached, powered, wasAttached, wasPowered); + } + + playSound(level, x, y, z, attached, powered, wasAttached, wasPowered); + + if (id > 0) // ie. it isn't being removed. + { + level->setData(x, y, z, data); + if (canUpdate) notifyNeighbors(level, x, y, z, dir); + } + + if (wasAttached != attached) + { + for (int i = 1; i < receiverPos; i++) + { + int xx = x + stepX * i; + int zz = z + stepZ * i; + int wireData = wiresData[i]; + if (wireData < 0) continue; + + if (attached) + { + wireData |= TripWireTile::MASK_ATTACHED; + } + else + { + wireData &= ~TripWireTile::MASK_ATTACHED; + } + + + level->setData(xx, y, zz, wireData); + } + } +} + +void TripWireSourceTile::tick(Level *level, int x, int y, int z, Random *random) +{ + calculateState(level, x, y, z, id, level->getData(x, y, z), true, -1, 0); +} + +void TripWireSourceTile::playSound(Level *level, int x, int y, int z, bool attached, bool powered, bool wasAttached, bool wasPowered) +{ + if (powered && !wasPowered) + { + level->playSound(x + 0.5, y + 0.1, z + 0.5, eSoundType_RANDOM_CLICK, 0.4f, 0.6f); + } + else if (!powered && wasPowered) + { + level->playSound(x + 0.5, y + 0.1, z + 0.5, eSoundType_RANDOM_CLICK, 0.4f, 0.5f); + } + else if (attached && !wasAttached) + { + level->playSound(x + 0.5, y + 0.1, z + 0.5, eSoundType_RANDOM_CLICK, 0.4f, 0.7f); + } + else if (!attached && wasAttached) + { + level->playSound(x + 0.5, y + 0.1, z + 0.5, eSoundType_RANDOM_BOW_HIT, 0.4f, 1.2f / (level->random->nextFloat() * 0.2f + 0.9f)); + } +} + +void TripWireSourceTile::notifyNeighbors(Level *level, int x, int y, int z, int dir) +{ + level->updateNeighborsAt(x, y, z, this->id); + + if (dir == Direction::EAST) + { + level->updateNeighborsAt(x - 1, y, z, this->id); + } + else if (dir == Direction::WEST) + { + level->updateNeighborsAt(x + 1, y, z, this->id); + } + else if (dir == Direction::SOUTH) + { + level->updateNeighborsAt(x, y, z - 1, this->id); + } + else if (dir == Direction::NORTH) + { + level->updateNeighborsAt(x, y, z + 1, this->id); + } +} + +bool TripWireSourceTile::checkCanSurvive(Level *level, int x, int y, int z) +{ + if (!mayPlace(level, x, y, z)) + { + this->spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + return false; + } + + return true; +} + +void TripWireSourceTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) +{ + int dir = level->getData(x, y, z) & MASK_DIR; + float r = 3 / 16.0f; + + if (dir == Direction::EAST) + { + setShape(0, 0.2f, 0.5f - r, r * 2, 0.8f, 0.5f + r); + } + else if (dir == Direction::WEST) + { + setShape(1 - r * 2, 0.2f, 0.5f - r, 1, 0.8f, 0.5f + r); + } + else if (dir == Direction::SOUTH) + { + setShape(0.5f - r, 0.2f, 0, 0.5f + r, 0.8f, r * 2); + } + else if (dir == Direction::NORTH) + { + setShape(0.5f - r, 0.2f, 1 - r * 2, 0.5f + r, 0.8f, 1); + } +} + +void TripWireSourceTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + bool attached = (data & MASK_ATTACHED) == MASK_ATTACHED; + bool powered = (data & MASK_POWERED) == MASK_POWERED; + + if (attached || powered) + { + calculateState(level, x, y, z, 0, data, false, -1, 0); // Disconnect + // the other end. + } + + if (powered) + { + level->updateNeighborsAt(x, y, z, this->id); + int dir = data & MASK_DIR; + + if (dir == Direction::EAST) + { + level->updateNeighborsAt(x - 1, y, z, this->id); + } + else if (dir == Direction::WEST) + { + level->updateNeighborsAt(x + 1, y, z, this->id); + } + else if (dir == Direction::SOUTH) + { + level->updateNeighborsAt(x, y, z - 1, this->id); + } + else if (dir == Direction::NORTH) + { + level->updateNeighborsAt(x, y, z + 1, this->id); + } + } + + Tile::onRemove(level, x, y, z, id, data); +} + +bool TripWireSourceTile::getSignal(LevelSource *level, int x, int y, int z, int dir) +{ + return (level->getData(x, y, z) & MASK_POWERED) == MASK_POWERED; +} + +bool TripWireSourceTile::getDirectSignal(Level *level, int x, int y, int z, int dir) +{ + int data = level->getData(x, y, z); + if ((data & MASK_POWERED) != MASK_POWERED) return false; + int myDir = data & MASK_DIR; + + if (myDir == Direction::NORTH && dir == Facing::NORTH) return true; + if (myDir == Direction::SOUTH && dir == Facing::SOUTH) return true; + if (myDir == Direction::WEST && dir == Facing::WEST) return true; + if (myDir == Direction::EAST && dir == Facing::EAST) return true; + + + return false; +} + +bool TripWireSourceTile::isSignalSource() +{ + return true; +} diff --git a/Minecraft.World/TripWireSourceTile.h b/Minecraft.World/TripWireSourceTile.h new file mode 100644 index 00000000..c86e781d --- /dev/null +++ b/Minecraft.World/TripWireSourceTile.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Tile.h" + +class TripWireSourceTile : public Tile +{ +public: + using Tile::getTickDelay; + + static const int MASK_DIR = 0x3; + static const int MASK_ATTACHED = 0x4; + static const int MASK_POWERED = 0x8; + static const int WIRE_DIST_MIN = 1; + static const int WIRE_DIST_MAX = 2 + 40; // 2 hooks + x string + + TripWireSourceTile(int id); + + AABB *getAABB(Level *level, int x, int y, int z); + bool blocksLight(); + bool isSolidRender(bool isServerLevel = false); + bool isCubeShaped(); + int getRenderShape(); + int getTickDelay(Level *level); + bool mayPlace(Level *level, int x, int y, int z, int face); + bool mayPlace(Level *level, int x, int y, int z); + int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + void finalizePlacement(Level *level, int x, int y, int z, int data); + void neighborChanged(Level *level, int x, int y, int z, int type); + void calculateState(Level *level, int x, int y, int z, int id, int data, bool canUpdate, int wireSource, int wireSourceData); + void tick(Level *level, int x, int y, int z, Random *random); + +private: + void playSound(Level *level, int x, int y, int z, bool attached, bool powered, bool wasAttached, bool wasPowered); + void notifyNeighbors(Level *level, int x, int y, int z, int dir); + bool checkCanSurvive(Level *level, int x, int y, int z); + +public: + void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); + void onRemove(Level *level, int x, int y, int z, int id, int data); + virtual bool getSignal(LevelSource *level, int x, int y, int z, int dir); + virtual bool getDirectSignal(Level *level, int x, int y, int z, int dir); + bool isSignalSource(); +}; diff --git a/Minecraft.World/TripWireTile.cpp b/Minecraft.World/TripWireTile.cpp new file mode 100644 index 00000000..40ad93f8 --- /dev/null +++ b/Minecraft.World/TripWireTile.cpp @@ -0,0 +1,221 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "TripWireTile.h" + +TripWireTile::TripWireTile(int id) : Tile(id, Material::decoration, isSolidRender()) +{ + setShape(0, 0, 0, 1, 2.5f / 16.0f, 1); + this->setTicking(true); +} + +int TripWireTile::getTickDelay(Level *level) +{ + // 4J: Increased (x2); quick update caused problems with shared + // data between client and server. + return 20; // 10; +} + +AABB *TripWireTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +bool TripWireTile::blocksLight() +{ + return false; +} + +bool TripWireTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool TripWireTile::isCubeShaped() +{ + return false; +} + +int TripWireTile::getRenderLayer() +{ + return 1; +} + +int TripWireTile::getRenderShape() +{ + return Tile::SHAPE_TRIPWIRE; +} + +int TripWireTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Item::string_Id; +} + +int TripWireTile::cloneTileId(Level *level, int x, int y, int z) +{ + return Item::string_Id; +} + +void TripWireTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + int data = level->getData(x, y, z); + bool wasSuspended = (data & MASK_SUSPENDED) == MASK_SUSPENDED; + bool isSuspended = !level->isTopSolidBlocking(x, y - 1, z); + if (wasSuspended != isSuspended) + { + spawnResources(level, x, y, z, data, 0); + level->setTile(x, y, z, 0); + } +} + +void TripWireTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) +{ + int data = level->getData(x, y, z); + bool attached = (data & MASK_ATTACHED) == MASK_ATTACHED; + bool suspended = (data & MASK_SUSPENDED) == MASK_SUSPENDED; + + if (!suspended) + { + setShape(0, 0, 0, 1, 1.5f / 16.0f, 1); + } + else if (!attached) + { + setShape(0, 0, 0, 1, 8.0f / 16.0f, 1); + } + else + { + setShape(0, 1.0f / 16.0f, 0, 1, 2.5f / 16.0f, 1); + } +} + +void TripWireTile::onPlace(Level *level, int x, int y, int z) +{ + int data = level->isTopSolidBlocking(x, y - 1, z) ? 0 : MASK_SUSPENDED; + level->setData(x, y, z, data); + updateSource(level, x, y, z, data); +} + +void TripWireTile::onRemove(Level *level, int x, int y, int z, int id, int data) +{ + updateSource(level, x, y, z, data | MASK_POWERED); +} + +void TripWireTile::playerWillDestroy(Level *level, int x, int y, int z, int data, shared_ptr player) +{ + if (level->isClientSide) return; + + if (player->getSelectedItem() != NULL && player->getSelectedItem()->id == Item::shears_Id) + { + level->setData(x, y, z, data | MASK_DISARMED); + } +} + +void TripWireTile::updateSource(Level *level, int x, int y, int z, int data) +{ + for (int dir = 0; dir < 2; dir++) + { + for (int i = 1; i < TripWireSourceTile::WIRE_DIST_MAX; i++) + { + int xx = x + Direction::STEP_X[dir] * i; + int zz = z + Direction::STEP_Z[dir] * i; + int tile = level->getTile(xx, y, zz); + + if (tile == Tile::tripWireSource_Id) + { + int sourceDir = level->getData(xx, y, zz) & TripWireSourceTile::MASK_DIR; + + if (sourceDir == Direction::DIRECTION_OPPOSITE[dir]) + { + Tile::tripWireSource->calculateState(level, xx, y, zz, tile, level->getData(xx, y, zz), true, i, data); + } + + break; + } + else if (tile != Tile::tripWire_Id) + { + break; + } + } + } +} + +void TripWireTile::entityInside(Level *level, int x, int y, int z, shared_ptr entity) +{ + if (level->isClientSide) return; + + if ((level->getData(x, y, z) & MASK_POWERED) == MASK_POWERED) return; + + checkPressed(level, x, y, z); +} + +void TripWireTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (level->isClientSide) return; + + if ((level->getData(x, y, z) & MASK_POWERED) != MASK_POWERED) return; + + checkPressed(level, x, y, z); +} + +void TripWireTile::checkPressed(Level *level, int x, int y, int z) +{ + int data = level->getData(x, y, z); + bool wasPressed = (data & MASK_POWERED) == MASK_POWERED; + bool shouldBePressed = false; + + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + vector > *entities = level->getEntities(nullptr, AABB::newTemp(x + tls->xx0, y + tls->yy0, z + tls->zz0, x + tls->xx1, y + tls->yy1, z + tls->zz1)); + if (!entities->empty()) + { + shouldBePressed = true; + } + + if (shouldBePressed && !wasPressed) + { + data |= MASK_POWERED; + } + + if (!shouldBePressed && wasPressed) + { + data &= ~MASK_POWERED; + } + + if (shouldBePressed != wasPressed) + { + level->setData(x, y, z, data); + updateSource(level, x, y, z, data); + } + + if (shouldBePressed) + { + level->addToTickNextTick(x, y, z, id, getTickDelay(level)); + } +} + +bool TripWireTile::shouldConnectTo(LevelSource *level, int x, int y, int z, int data, int dir) +{ + int tx = x + Direction::STEP_X[dir]; + int ty = y; + int tz = z + Direction::STEP_Z[dir]; + int t = level->getTile(tx, ty, tz); + bool suspended = (data & MASK_SUSPENDED) == MASK_SUSPENDED; + + if (t == Tile::tripWireSource_Id) + { + int otherData = level->getData(tx, ty, tz); + int facing = otherData & TripWireSourceTile::MASK_DIR; + + return facing == Direction::DIRECTION_OPPOSITE[dir]; + } + + if (t == Tile::tripWire_Id) + { + int otherData = level->getData(tx, ty, tz); + bool otherSuspended = (otherData & MASK_SUSPENDED) == MASK_SUSPENDED; + return suspended == otherSuspended; + } + + return false; +} diff --git a/Minecraft.World/TripWireTile.h b/Minecraft.World/TripWireTile.h new file mode 100644 index 00000000..7880e664 --- /dev/null +++ b/Minecraft.World/TripWireTile.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Tile.h" + +class TripWireTile : public Tile +{ + using Tile::getTickDelay; +public: + static const int MASK_POWERED = 0x1; + static const int MASK_SUSPENDED = 0x2; + static const int MASK_ATTACHED = 0x4; + static const int MASK_DISARMED = 0x8; + + TripWireTile(int id); + + int getTickDelay(Level *level); + AABB *getAABB(Level *level, int x, int y, int z); + bool blocksLight(); + bool isSolidRender(bool isServerLevel = false); + bool isCubeShaped(); + int getRenderLayer(); + int getRenderShape(); + int getResource(int data, Random *random, int playerBonusLevel); + int cloneTileId(Level *level, int x, int y, int z); + void neighborChanged(Level *level, int x, int y, int z, int type); + void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); + void onPlace(Level *level, int x, int y, int z); + void onRemove(Level *level, int x, int y, int z, int id, int data); + void playerWillDestroy(Level *level, int x, int y, int z, int data, shared_ptr player); + +private: + void updateSource(Level *level, int x, int y, int z, int data); + +public: + void entityInside(Level *level, int x, int y, int z, shared_ptr entity); + void tick(Level *level, int x, int y, int z, Random *random); + +private: + void checkPressed(Level *level, int x, int y, int z); + +public: + static bool shouldConnectTo(LevelSource *level, int x, int y, int z, int data, int dir); +}; diff --git a/Minecraft.World/UntouchingEnchantment.cpp b/Minecraft.World/UntouchingEnchantment.cpp new file mode 100644 index 00000000..1e5a41b7 --- /dev/null +++ b/Minecraft.World/UntouchingEnchantment.cpp @@ -0,0 +1,34 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "UntouchingEnchantment.h" + +UntouchingEnchantment::UntouchingEnchantment(int id, int frequency) : Enchantment(id, frequency, EnchantmentCategory::digger) +{ + setDescriptionId(IDS_ENCHANTMENT_UNTOUCHING); +} + +int UntouchingEnchantment::getMinCost(int level) +{ + return 15; +} + +int UntouchingEnchantment::getMaxCost(int level) +{ + return Enchantment::getMinCost(level) + 50; +} + +int UntouchingEnchantment::getMaxLevel() +{ + return 1; +} + +bool UntouchingEnchantment::isCompatibleWith(Enchantment *other) const +{ + return Enchantment::isCompatibleWith(other) && other->id != resourceBonus->id; +} + +bool UntouchingEnchantment::canEnchant(shared_ptr item) +{ + if (item->getItem()->id == Item::shears_Id) return true; + return Enchantment::canEnchant(item); +} \ No newline at end of file diff --git a/Minecraft.World/UntouchingEnchantment.h b/Minecraft.World/UntouchingEnchantment.h new file mode 100644 index 00000000..db827c27 --- /dev/null +++ b/Minecraft.World/UntouchingEnchantment.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Enchantment.h" + +class UntouchingEnchantment : public Enchantment +{ +public: + UntouchingEnchantment(int id, int frequency); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); + virtual bool isCompatibleWith(Enchantment *other) const; + virtual bool canEnchant(shared_ptr item); +}; \ No newline at end of file diff --git a/Minecraft.World/UpdateGameRuleProgressPacket.cpp b/Minecraft.World/UpdateGameRuleProgressPacket.cpp new file mode 100644 index 00000000..9459ed2b --- /dev/null +++ b/Minecraft.World/UpdateGameRuleProgressPacket.cpp @@ -0,0 +1,75 @@ +#include "stdafx.h" +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "UpdateGameRuleProgressPacket.h" + + + +UpdateGameRuleProgressPacket::UpdateGameRuleProgressPacket() +{ + m_messageId = L""; + m_icon = -1; + m_auxValue = 0; + m_definitionType = ConsoleGameRules::eGameRuleType_LevelRules; + m_dataTag = 0; +} + +UpdateGameRuleProgressPacket::UpdateGameRuleProgressPacket(ConsoleGameRules::EGameRuleType definitionType, const wstring &messageId, int icon, int auxValue, int dataTag, void *data, int dataLength) +{ + m_definitionType = definitionType; + m_messageId = messageId; + m_icon = icon; + m_auxValue = auxValue; + m_dataTag = dataTag; + + if(dataLength > 0) + { + m_data = byteArray(dataLength); + memcpy(m_data.data,data,dataLength); + } + else + { + m_data = byteArray(); + } +} + +void UpdateGameRuleProgressPacket::read(DataInputStream *dis) //throws IOException +{ + m_definitionType = (ConsoleGameRules::EGameRuleType)dis->readInt(); + m_messageId = readUtf(dis,64); + m_icon = dis->readInt(); + m_auxValue = dis->readByte(); + m_dataTag = dis->readInt(); + int dataLength = dis->readInt(); + + if(dataLength > 0) + { + m_data = byteArray(dataLength); + dis->readFully(m_data); + } + else + { + m_data = byteArray(); + } +} + +void UpdateGameRuleProgressPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(m_definitionType); + writeUtf(m_messageId,dos); + dos->writeInt(m_icon); + dos->writeByte(m_auxValue); + dos->writeInt(m_dataTag); + dos->writeInt(m_data.length); + dos->write(m_data); +} + +void UpdateGameRuleProgressPacket::handle(PacketListener *listener) +{ + listener->handleUpdateGameRuleProgressPacket(shared_from_this()); +} + +int UpdateGameRuleProgressPacket::getEstimatedSize() +{ + return (int)m_messageId.length() + 4 + m_data.length; +} \ No newline at end of file diff --git a/Minecraft.World/UpdateGameRuleProgressPacket.h b/Minecraft.World/UpdateGameRuleProgressPacket.h new file mode 100644 index 00000000..b3384fa1 --- /dev/null +++ b/Minecraft.World/UpdateGameRuleProgressPacket.h @@ -0,0 +1,26 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class UpdateGameRuleProgressPacket : public Packet, public enable_shared_from_this +{ +public: + ConsoleGameRules::EGameRuleType m_definitionType; + wstring m_messageId; + int m_icon, m_auxValue; + int m_dataTag; + byteArray m_data; + + UpdateGameRuleProgressPacket(); + UpdateGameRuleProgressPacket(ConsoleGameRules::EGameRuleType definitionType, const wstring &messageId, int icon, int auxValue, int dataTag, void *data, int dataLength); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new UpdateGameRuleProgressPacket()); } + virtual int getId() { return 158; } +}; \ No newline at end of file diff --git a/Minecraft.World/UpdateMobEffectPacket.cpp b/Minecraft.World/UpdateMobEffectPacket.cpp new file mode 100644 index 00000000..dec8d79f --- /dev/null +++ b/Minecraft.World/UpdateMobEffectPacket.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include "net.minecraft.world.effect.h" +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "UpdateMobEffectPacket.h" + + + +UpdateMobEffectPacket::UpdateMobEffectPacket() +{ + this->entityId = 0; + this->effectId = 0; + this->effectAmplifier = 0; + this->effectDurationTicks = 0; +} + +UpdateMobEffectPacket::UpdateMobEffectPacket(int entityId, MobEffectInstance *effect) +{ + this->entityId = entityId; + this->effectId = (BYTE) (effect->getId() & 0xff); + this->effectAmplifier = (char) (effect->getAmplifier() & 0xff); + this->effectDurationTicks = (short) effect->getDuration(); +} + +void UpdateMobEffectPacket::read(DataInputStream *dis) +{ + entityId = dis->readInt(); + effectId = dis->readByte(); + effectAmplifier = dis->readByte(); + effectDurationTicks = dis->readShort(); +} + +void UpdateMobEffectPacket::write(DataOutputStream *dos) +{ + dos->writeInt(entityId); + dos->writeByte(effectId); + dos->writeByte(effectAmplifier); + dos->writeShort(effectDurationTicks); +} + +void UpdateMobEffectPacket::handle(PacketListener *listener) +{ + listener->handleUpdateMobEffect(shared_from_this()); +} + +int UpdateMobEffectPacket::getEstimatedSize() +{ + return 8; +} + +bool UpdateMobEffectPacket::canBeInvalidated() +{ + return true; +} + +bool UpdateMobEffectPacket::isInvalidatedBy(shared_ptr packet) +{ + shared_ptr target = dynamic_pointer_cast(packet); + return target->entityId == entityId && target->effectId == effectId; +} \ No newline at end of file diff --git a/Minecraft.World/UpdateMobEffectPacket.h b/Minecraft.World/UpdateMobEffectPacket.h new file mode 100644 index 00000000..d17d1be4 --- /dev/null +++ b/Minecraft.World/UpdateMobEffectPacket.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Packet.h" + +class MobEffectInstance; + +class UpdateMobEffectPacket : public Packet, public enable_shared_from_this +{ +public: + int entityId; + BYTE effectId; + char effectAmplifier; + short effectDurationTicks; + + UpdateMobEffectPacket(); + UpdateMobEffectPacket(int entityId, MobEffectInstance *effect); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + virtual bool canBeInvalidated(); + virtual bool isInvalidatedBy(shared_ptr packet); + +public: + static shared_ptr create() { return shared_ptr(new UpdateMobEffectPacket()); } + virtual int getId() { return 41; } +}; \ No newline at end of file diff --git a/Minecraft.World/UpdateProgressPacket.cpp b/Minecraft.World/UpdateProgressPacket.cpp new file mode 100644 index 00000000..e97ed23e --- /dev/null +++ b/Minecraft.World/UpdateProgressPacket.cpp @@ -0,0 +1,37 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "PacketListener.h" +#include "UpdateProgressPacket.h" + + + +UpdateProgressPacket::UpdateProgressPacket() +{ + this->m_percentage = 0; +} + +UpdateProgressPacket::UpdateProgressPacket(int percentage) +{ + this->m_percentage = percentage; +} + +void UpdateProgressPacket::read(DataInputStream *dis) //throws IOException +{ + m_percentage = dis->readByte(); +} + +void UpdateProgressPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeByte(m_percentage); +} + +void UpdateProgressPacket::handle(PacketListener *listener) +{ + listener->handleUpdateProgress(shared_from_this()); +} + +int UpdateProgressPacket::getEstimatedSize() +{ + return 1; +} diff --git a/Minecraft.World/UpdateProgressPacket.h b/Minecraft.World/UpdateProgressPacket.h new file mode 100644 index 00000000..beca6509 --- /dev/null +++ b/Minecraft.World/UpdateProgressPacket.h @@ -0,0 +1,25 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +// 4J Added packet to update clients on the time for the host to finish doing something + +class UpdateProgressPacket : public Packet, public enable_shared_from_this +{ + +public: + int m_percentage; + + UpdateProgressPacket(); + UpdateProgressPacket(int percentage); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new UpdateProgressPacket()); } + virtual int getId() { return 156; } +}; \ No newline at end of file diff --git a/Minecraft.World/UseAnim.h b/Minecraft.World/UseAnim.h new file mode 100644 index 00000000..cb045263 --- /dev/null +++ b/Minecraft.World/UseAnim.h @@ -0,0 +1,10 @@ +#pragma once + +enum UseAnim +{ + UseAnim_none, + UseAnim_eat, + UseAnim_drink, + UseAnim_block, + UseAnim_bow +}; diff --git a/Minecraft.World/UseItemPacket.cpp b/Minecraft.World/UseItemPacket.cpp new file mode 100644 index 00000000..d9699130 --- /dev/null +++ b/Minecraft.World/UseItemPacket.cpp @@ -0,0 +1,112 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.item.h" +#include "PacketListener.h" +#include "UseItemPacket.h" + +const float UseItemPacket::CLICK_ACCURACY = 16.0f; + +UseItemPacket::~UseItemPacket() +{ +} + +UseItemPacket::UseItemPacket() +{ + x = 0; + y = 0; + z = 0; + face = 0; + item = nullptr; + clickX = 0.0f; + clickY = 0.0f; + clickZ = 0.0f; +} + +UseItemPacket::UseItemPacket(int x, int y, int z, int face, shared_ptr item, float clickX, float clickY, float clickZ) +{ + this->x = x; + this->y = y; + this->z = z; + this->face = face; + // 4J - take copy of item as we want our packets to have full ownership of any referenced data + this->item = item ? item->copy() : shared_ptr(); + this->clickX = clickX; + this->clickY = clickY; + this->clickZ = clickZ; +} + +void UseItemPacket::read(DataInputStream *dis) //throws IOException +{ + x = dis->readInt(); + y = dis->read(); + z = dis->readInt(); + face = dis->read(); + item = readItem(dis); + clickX = dis->read() / CLICK_ACCURACY; + clickY = dis->read() / CLICK_ACCURACY; + clickZ = dis->read() / CLICK_ACCURACY; +} + +void UseItemPacket::write(DataOutputStream *dos) //throws IOException +{ + dos->writeInt(x); + dos->write(y); + dos->writeInt(z); + dos->write(face); + + writeItem(item, dos); + dos->write((int) (clickX * CLICK_ACCURACY)); + dos->write((int) (clickY * CLICK_ACCURACY)); + dos->write((int)(clickZ * CLICK_ACCURACY)); +} + +void UseItemPacket::handle(PacketListener *listener) +{ + listener->handleUseItem(shared_from_this()); +} + +int UseItemPacket::getEstimatedSize() +{ + return 15; +} + +int UseItemPacket::getX() +{ + return x; +} + +int UseItemPacket::getY() +{ + return y; +} + +int UseItemPacket::getZ() +{ + return z; +} + +int UseItemPacket::getFace() +{ + return face; +} + +shared_ptr UseItemPacket::getItem() +{ + return item; +} + +float UseItemPacket::getClickX() +{ + return clickX; +} + +float UseItemPacket::getClickY() +{ + return clickY; +} + +float UseItemPacket::getClickZ() +{ + return clickZ; +} diff --git a/Minecraft.World/UseItemPacket.h b/Minecraft.World/UseItemPacket.h new file mode 100644 index 00000000..44e20457 --- /dev/null +++ b/Minecraft.World/UseItemPacket.h @@ -0,0 +1,36 @@ +#pragma once +using namespace std; + +#include "Packet.h" + +class UseItemPacket : public Packet, public enable_shared_from_this +{ +private: + static const float CLICK_ACCURACY; + int x, y, z, face; + shared_ptr item; + float clickX, clickY, clickZ; + +public: + UseItemPacket(); + UseItemPacket(int x, int y, int z, int face, shared_ptr item, float clickX, float clickY, float clickZ); + ~UseItemPacket(); + + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual void handle(PacketListener *listener); + virtual int getEstimatedSize(); + + int getX(); + int getY(); + int getZ(); + int getFace(); + shared_ptr getItem(); + float getClickX(); + float getClickY(); + float getClickZ(); + +public: + static shared_ptr create() { return shared_ptr(new UseItemPacket()); } + virtual int getId() { return 15; } +}; diff --git a/Minecraft.World/Vec3.cpp b/Minecraft.World/Vec3.cpp new file mode 100644 index 00000000..b1f79043 --- /dev/null +++ b/Minecraft.World/Vec3.cpp @@ -0,0 +1,265 @@ +#include "stdafx.h" +#include "Vec3.h" +#include "AABB.h" + +DWORD Vec3::tlsIdx = 0; +Vec3::ThreadStorage *Vec3::tlsDefault = NULL; + +Vec3::ThreadStorage::ThreadStorage() +{ + pool = new Vec3[POOL_SIZE]; + poolPointer = 0; +} + +Vec3::ThreadStorage::~ThreadStorage() +{ + delete [] pool; +} + +void Vec3::CreateNewThreadStorage() +{ + ThreadStorage *tls = new ThreadStorage(); + if(tlsDefault == NULL ) + { + tlsIdx = TlsAlloc(); + tlsDefault = tls; + } + TlsSetValue(tlsIdx, tls); +} + +void Vec3::UseDefaultThreadStorage() +{ + TlsSetValue(tlsIdx, tlsDefault); +} + +void Vec3::ReleaseThreadStorage() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); + if( tls == tlsDefault ) return; + + delete tls; +} + +Vec3 *Vec3::newPermanent(double x, double y, double z) +{ + return new Vec3(x,y,z); +}; + +void Vec3::clearPool() +{ +} + +void Vec3::resetPool() +{ +} + +Vec3 *Vec3::newTemp(double x, double y, double z) +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); + Vec3 *thisVec = &tls->pool[tls->poolPointer]; + thisVec->set(x, y, z); + tls->poolPointer = ( tls->poolPointer + 1 ) % ThreadStorage::POOL_SIZE; + return thisVec; +} + +Vec3::Vec3(double x, double y, double z) +{ + if (x == -0.0) x = 0.0; + if (y == -0.0) y = 0.0; + if (z == -0.0) z = 0.0; + this->x = x; + this->y = y; + this->z = z; +} + +Vec3 *Vec3::set(double x, double y, double z) +{ + this->x = x; + this->y = y; + this->z = z; + return this; +} + + +Vec3 *Vec3::interpolateTo(Vec3 *t, double p) +{ + double xt = x + (t->x - x) * p; + double yt = y + (t->y - y) * p; + double zt = z + (t->z - z) * p; + + return Vec3::newTemp(xt, yt, zt); +} + +Vec3 *Vec3::vectorTo(Vec3 *p) +{ + return Vec3::newTemp(p->x - x, p->y - y, p->z - z); +} + +Vec3 *Vec3::normalize() +{ + double dist = (double) (sqrt(x * x + y * y + z * z)); + if (dist < 0.0001) return Vec3::newTemp(0, 0, 0); + return Vec3::newTemp(x / dist, y / dist, z / dist); +} + +double Vec3::dot(Vec3 *p) +{ + return x * p->x + y * p->y + z * p->z; +} + +Vec3 *Vec3::cross(Vec3 *p) +{ + return Vec3::newTemp(y * p->z - z * p->y, z * p->x - x * p->z, x * p->y - y * p->x); +} + +Vec3 *Vec3::add(double x, double y, double z) +{ + return Vec3::newTemp(this->x + x, this->y + y, this->z + z); +} + +double Vec3::distanceTo(Vec3 *p) +{ + double xd = p->x - x; + double yd = p->y - y; + double zd = p->z - z; + return (double) sqrt(xd * xd + yd * yd + zd * zd); +} + +double Vec3::distanceToSqr(Vec3 *p) +{ + double xd = p->x - x; + double yd = p->y - y; + double zd = p->z - z; + return xd * xd + yd * yd + zd * zd; +} + +double Vec3::distanceToSqr(double x2, double y2, double z2) +{ + double xd = x2 - x; + double yd = y2 - y; + double zd = z2 - z; + return xd * xd + yd * yd + zd * zd; +} + +Vec3 *Vec3::scale(double l) +{ + return Vec3::newTemp(x * l, y * l, z * l); +} + +double Vec3::length() +{ + return sqrt(x * x + y * y + z * z); +} + +Vec3 *Vec3::clipX(Vec3 *b, double xt) +{ + double xd = b->x - x; + double yd = b->y - y; + double zd = b->z - z; + + if (xd * xd < 0.0000001f) return NULL; + + double d = (xt - x) / xd; + if (d < 0 || d > 1) return NULL; + return Vec3::newTemp(x + xd * d, y + yd * d, z + zd * d); +} + +Vec3 *Vec3::clipY(Vec3 *b, double yt) +{ + double xd = b->x - x; + double yd = b->y - y; + double zd = b->z - z; + + if (yd * yd < 0.0000001f) return NULL; + + double d = (yt - y) / yd; + if (d < 0 || d > 1) return NULL; + return Vec3::newTemp(x + xd * d, y + yd * d, z + zd * d); +} + +Vec3 *Vec3::clipZ(Vec3 *b, double zt) +{ + double xd = b->x - x; + double yd = b->y - y; + double zd = b->z - z; + + if (zd * zd < 0.0000001f) return NULL; + + double d = (zt - z) / zd; + if (d < 0 || d > 1) return NULL; + return Vec3::newTemp(x + xd * d, y + yd * d, z + zd * d); +} + +wstring Vec3::toString() +{ + static wchar_t buf[128]; + swprintf(buf, 128, L"(%f,%f,%f)",x,y,z); + return wstring(buf); +} + +Vec3 *Vec3::lerp(Vec3 *v, double a) +{ + return Vec3::newTemp(x + (v->x - x) * a, y + (v->y - y) * a, z + (v->z - z) * a); +} + +void Vec3::xRot(float degs) +{ + double _cos = cos(degs); // 4J - cos/sin were floats but seems pointless wasting precision here + double _sin = sin(degs); + + double xx = x; + double yy = y * _cos + z * _sin; + double zz = z * _cos - y * _sin; + + this->x = xx; + this->y = yy; + this->z = zz; +} + +void Vec3::yRot(float degs) +{ + double _cos = cos(degs); // 4J - cos/sin were floats but seems pointless wasting precision here + double _sin = sin(degs); + + double xx = x * _cos + z * _sin; + double yy = y; + double zz = z * _cos - x * _sin; + + this->x = xx; + this->y = yy; + this->z = zz; +} + +void Vec3::zRot(float degs) +{ + double _cos = cos(degs); // 4J - cos/sin were floats but seems pointless wasting precision here + double _sin = sin(degs); + + double xx = x * _cos + y * _sin; + double yy = y * _cos - x * _sin; + double zz = z; + + this->x = xx; + this->y = yy; + this->z = zz; +} + +// Returns 0 if this point is within the box +// Otherwise returns the distance to the box +double Vec3::distanceTo(AABB *box) +{ + if(box->contains(this)) return 0; + + double xd = 0, yd = 0, zd = 0; + + if(x < box->x0) xd = box->x0 - x; + else if( x > box->x1) xd = x - box->x1; + + if(y < box->y0) yd = box->y0 - y; + else if( y > box->y1) yd = y - box->y1; + + if(z < box->z0) zd = box->z0 - z; + else if( z > box->z1) zd = z - box->z1; + + return sqrt(xd * xd + yd * yd + zd * zd); +} \ No newline at end of file diff --git a/Minecraft.World/Vec3.h b/Minecraft.World/Vec3.h new file mode 100644 index 00000000..00a74103 --- /dev/null +++ b/Minecraft.World/Vec3.h @@ -0,0 +1,59 @@ +#pragma once +using namespace std; + +class AABB; + +class Vec3 +{ + // 4J added so we can have separate pools for different threads + class ThreadStorage + { + public: + static const int POOL_SIZE = 1024; + Vec3 *pool; + unsigned int poolPointer; + ThreadStorage(); + ~ThreadStorage(); + }; + static DWORD tlsIdx; + static ThreadStorage *tlsDefault; +public: + // Each new thread that needs to use Vec3 pools will need to call one of the following 2 functions, to either create its own + // local storage, or share the default storage already allocated by the main thread + static void CreateNewThreadStorage(); + static void UseDefaultThreadStorage(); + static void ReleaseThreadStorage(); + + static Vec3 *newPermanent(double x, double y, double z); + static void clearPool(); + static void resetPool(); + static Vec3 *newTemp(double x, double y, double z); + double x, y, z; +private: + Vec3() {} + Vec3(double x, double y, double z); + Vec3 *set(double x, double y, double z); +public: + Vec3 *interpolateTo(Vec3 *t, double p); + Vec3 *vectorTo(Vec3 *p); + Vec3 *normalize(); + double dot(Vec3 *p); + Vec3 *cross(Vec3 *p); + Vec3 *add(double x, double y, double z); + double distanceTo(Vec3 *p); + double distanceToSqr(Vec3 *p); + double distanceToSqr(double x2, double y2, double z2); + Vec3 *scale(double l); + double length(); + Vec3 *clipX(Vec3 *b, double xt); + Vec3 *clipY(Vec3 *b, double yt); + Vec3 *clipZ(Vec3 *b, double zt); + wstring toString(); + Vec3 *lerp(Vec3 *v, double a); + void xRot(float degs); + void yRot(float degs); + void zRot(float degs); + + // 4J Added + double distanceTo(AABB *box); +}; \ No newline at end of file diff --git a/Minecraft.World/Village.cpp b/Minecraft.World/Village.cpp new file mode 100644 index 00000000..aef38066 --- /dev/null +++ b/Minecraft.World/Village.cpp @@ -0,0 +1,512 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.village.h" +#include "net.minecraft.world.entity.npc.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "BasicTypeContainers.h" +#include "Village.h" + +Village::Aggressor::Aggressor(shared_ptr mob, int timeStamp) +{ + this->mob = mob; + this->timeStamp = timeStamp; +} + +Village::Village() +{ + accCenter = new Pos(0, 0, 0); + center = new Pos(0, 0, 0); + radius = 0; + stableSince = 0; + _tick = 0; + populationSize = 0; + golemCount = 0; + noBreedTimer = 0; + + level = NULL; +} + +Village::Village(Level *level) +{ + accCenter = new Pos(0, 0, 0); + center = new Pos(0, 0, 0); + radius = 0; + stableSince = 0; + _tick = 0; + populationSize = 0; + golemCount = 0; + noBreedTimer = 0; + + this->level = level; +} + +Village::~Village() +{ + delete accCenter; + delete center; + for(AUTO_VAR(it, aggressors.begin()); it != aggressors.end(); ++it) + { + delete *it; + } +} + +void Village::setLevel(Level *level) +{ + this->level = level; +} + +void Village::tick(int tick) +{ + this->_tick = tick; + updateDoors(); + updateAggressors(); + if (tick % 20 == 0) countPopulation(); + if (tick % 30 == 0) countGolem(); + + int idealGolemCount = populationSize / 10; + if (golemCount < idealGolemCount && doorInfos.size() > 20 && level->random->nextInt(7000) == 0) + { + Vec3 *spawnPos = findRandomSpawnPos(center->x, center->y, center->z, 2, 4, 2); + if (spawnPos != NULL) + { + shared_ptr vg = shared_ptr( new VillagerGolem(level) ); + vg->setPos(spawnPos->x, spawnPos->y, spawnPos->z); + level->addEntity(vg); + ++golemCount; + } + } + + // 4J - All commented out in java + // for (DoorInfo di : doorInfos) { + // level.addParticle("heart", di.getIndoorX() + 0.5, di.getIndoorY() + .5f, di.getIndoorZ() + 0.5, 0, 1, 0); + // } + // + // for (int i = 0; i < 8; ++i) + // for (int j = 0; j < 8; ++j) + // level.addParticle("heart", center.x + 0.5 + i, center.y + .5f, center.z + 0.5 + j, 0, 1, 0); + // for (float i = 0; i < Math.PI * 2; i += 0.1) { + // int x = center.x + (int) (Math.cos(i) * radius); + // int z = center.z + (int) (Math.sin(i) * radius); + // level.addParticle("heart", x, center.y + .5f, z, 0, 1, 0); + // } +} + +Vec3 *Village::findRandomSpawnPos(int x, int y, int z, int sx, int sy, int sz) +{ + for (int i = 0; i < 10; ++i) + { + int xx = x + level->random->nextInt(16) - 8; + int yy = y + level->random->nextInt(6) - 3; + int zz = z + level->random->nextInt(16) - 8; + if (!isInside(xx, yy, zz)) continue; + if (canSpawnAt(xx, yy, zz, sx, sy, sz)) return Vec3::newTemp(xx, yy, zz); + } + return NULL; +} + +bool Village::canSpawnAt(int x, int y, int z, int sx, int sy, int sz) +{ + if (!level->isTopSolidBlocking(x, y - 1, z)) return false; + + int startX = x - sx / 2; + int startZ = z - sz / 2; + for (int xx = startX; xx < startX + sx; xx++) + for (int yy = y; yy < y + sy; yy++) + for (int zz = startZ; zz < startZ + sz; zz++) + if (level->isSolidBlockingTile(xx, yy, zz)) return false; + + return true; +} + +void Village::countGolem() +{ + // Fix - let bots report themselves? + vector > *golems = level->getEntitiesOfClass(typeid(VillagerGolem), AABB::newTemp(center->x - radius, center->y - 4, center->z - radius, center->x + radius, center->y + 4, center->z + radius)); + golemCount = golems->size(); + delete golems; +} + +void Village::countPopulation() +{ + vector > *villagers = level->getEntitiesOfClass(typeid(Villager), AABB::newTemp(center->x - radius, center->y - 4, center->z - radius, center->x + radius, center->y + 4, center->z + radius)); + populationSize = villagers->size(); + delete villagers; + + if (populationSize == 0) + { + // forget standing + playerStanding.clear(); + } +} + +Pos *Village::getCenter() +{ + return center; +} + +int Village::getRadius() +{ + return radius; +} + +int Village::getDoorCount() +{ + return doorInfos.size(); +} + +int Village::getStableAge() +{ + return _tick - stableSince; +} + +int Village::getPopulationSize() +{ + return populationSize; +} + +bool Village::isInside(int xx, int yy, int zz) +{ + return center->distSqr(xx, yy, zz) < radius * radius; +} + +vector > *Village::getDoorInfos() +{ + return &doorInfos; +} + +shared_ptr Village::getClosestDoorInfo(int x, int y, int z) +{ + shared_ptr closest = nullptr; + int closestDistSqr = Integer::MAX_VALUE; + //for (DoorInfo dm : doorInfos) + for(AUTO_VAR(it, doorInfos.begin()); it != doorInfos.end(); ++it) + { + shared_ptr dm = *it; + int distSqr = dm->distanceToSqr(x, y, z); + if (distSqr < closestDistSqr) + { + closest = dm; + closestDistSqr = distSqr; + } + } + return closest; +} + +shared_ptrVillage::getBestDoorInfo(int x, int y, int z) +{ + shared_ptr closest = nullptr; + int closestDist = Integer::MAX_VALUE; + //for (DoorInfo dm : doorInfos) + for(AUTO_VAR(it, doorInfos.begin()); it != doorInfos.end(); ++it) + { + shared_ptrdm = *it; + + int distSqr = dm->distanceToSqr(x, y, z); + if (distSqr > 16 * 16) distSqr *= 1000; + else distSqr = dm->getBookingsCount(); + + if (distSqr < closestDist) + { + closest = dm; + closestDist = distSqr; + } + } + return closest; +} + +bool Village::hasDoorInfo(int x, int y, int z) +{ + return getDoorInfo(x, y, z) != NULL; +} + +shared_ptrVillage::getDoorInfo(int x, int y, int z) +{ + if (center->distSqr(x, y, z) > radius * radius) return nullptr; + //for (DoorInfo di : doorInfos) + for(AUTO_VAR(it, doorInfos.begin()); it != doorInfos.end(); ++it) + { + shared_ptr di = *it; + if (di->x == x && di->z == z && abs(di->y - y) <= 1) return di; + } + return nullptr; +} + +void Village::addDoorInfo(shared_ptr di) +{ + doorInfos.push_back(di); + accCenter->x += di->x; + accCenter->y += di->y; + accCenter->z += di->z; + calcInfo(); + stableSince = di->timeStamp; +} + +bool Village::canRemove() +{ + return doorInfos.empty(); +} + +void Village::addAggressor(shared_ptr mob) +{ + //for (Aggressor a : aggressors) + for(AUTO_VAR(it, aggressors.begin()); it != aggressors.end(); ++it) + { + Aggressor *a = *it; + if (a->mob == mob) + { + a->timeStamp = _tick; + return; + } + } + aggressors.push_back(new Aggressor(mob, _tick)); +} + +shared_ptr Village::getClosestAggressor(shared_ptr from) +{ + double closestSqr = Double::MAX_VALUE; + Aggressor *closest = NULL; + //for (int i = 0; i < aggressors.size(); ++i) + for(AUTO_VAR(it, aggressors.begin()); it != aggressors.end(); ++it) + { + Aggressor *a = *it; //aggressors.get(i); + double distSqr = a->mob->distanceToSqr(from); + if (distSqr > closestSqr) continue; + closest = a; + closestSqr = distSqr; + } + return closest != NULL ? closest->mob : nullptr; +} + +shared_ptr Village::getClosestBadStandingPlayer(shared_ptr from) // 4J Stu - Should be LivingEntity when we add that +{ + double closestSqr = Double::MAX_VALUE; + shared_ptr closest = nullptr; + + //for (String player : playerStanding.keySet()) + for(AUTO_VAR(it,playerStanding.begin()); it != playerStanding.end(); ++it) + { + wstring player = it->first; + if (isVeryBadStanding(player)) + { + shared_ptr mob = level->getPlayerByName(player); + if (mob != NULL) + { + double distSqr = mob->distanceToSqr(from); + if (distSqr > closestSqr) continue; + closest = mob; + closestSqr = distSqr; + } + } + } + + return closest; +} + +void Village::updateAggressors() +{ + //for (Iterator it = aggressors.iterator(); it.hasNext();) + for(AUTO_VAR(it, aggressors.begin()); it != aggressors.end();) + { + Aggressor *a = *it; //it.next(); + if (!a->mob->isAlive() || abs(_tick - a->timeStamp) > 300) + { + delete *it; + it = aggressors.erase(it); + //it.remove(); + } + else + { + ++it; + } + } +} + +void Village::updateDoors() +{ + bool removed = false; + bool resetBookings = level->random->nextInt(50) == 0; + //for (Iterator it = doorInfos.iterator(); it.hasNext();) + for(AUTO_VAR(it, doorInfos.begin()); it != doorInfos.end();) + { + shared_ptr dm = *it; //it.next(); + if (resetBookings) dm->resetBookingCount(); + if (!isDoor(dm->x, dm->y, dm->z) || abs(_tick - dm->timeStamp) > 1200) + { + accCenter->x -= dm->x; + accCenter->y -= dm->y; + accCenter->z -= dm->z; + removed = true; + dm->removed = true; + + it = doorInfos.erase(it); + //it.remove(); + } + else + { + ++it; + } + } + + if (removed) calcInfo(); +} + +bool Village::isDoor(int x, int y, int z) +{ + int tileId = level->getTile(x, y, z); + if (tileId <= 0) return false; + return tileId == Tile::door_wood_Id; +} + +void Village::calcInfo() +{ + int s = doorInfos.size(); + if (s == 0) + { + center->set(0, 0, 0); + radius = 0; + return; + } + center->set(accCenter->x / s, accCenter->y / s, accCenter->z / s); + int maxRadiusSqr = 0; + //for (DoorInfo dm : doorInfos) + for(AUTO_VAR(it, doorInfos.begin()); it != doorInfos.end(); ++it) + { + shared_ptr dm = *it; + maxRadiusSqr = max(dm->distanceToSqr(center->x, center->y, center->z), maxRadiusSqr); + } + int doorDist= Villages::MaxDoorDist; // Take into local int for PS4 as max takes a reference to the const int there and then needs the value to exist for the linker + radius = max(doorDist, (int) sqrt((float)maxRadiusSqr) + 1); +} + +int Village::getStanding(const wstring &playerName) +{ + AUTO_VAR(it,playerStanding.find(playerName)); + if (it != playerStanding.end()) + { + return it->second; + } + return 0; +} + +int Village::modifyStanding(const wstring &playerName, int delta) +{ + int current = getStanding(playerName); + int newValue = Mth::clamp(current + delta, -30, 10); + playerStanding.insert(pair(playerName, newValue)); + return newValue; +} + +bool Village::isGoodStanding(const wstring &playerName) +{ + return getStanding(playerName) >= 0; +} + +bool Village::isBadStanding(const wstring &playerName) +{ + return getStanding(playerName) <= -5; +} + +bool Village::isVeryBadStanding(const wstring playerName) +{ + return getStanding(playerName) <= -15; +} + +void Village::readAdditionalSaveData(CompoundTag *tag) +{ + populationSize = tag->getInt(L"PopSize"); + radius = tag->getInt(L"Radius"); + golemCount = tag->getInt(L"Golems"); + stableSince = tag->getInt(L"Stable"); + _tick = tag->getInt(L"Tick"); + noBreedTimer = tag->getInt(L"MTick"); + center->x = tag->getInt(L"CX"); + center->y = tag->getInt(L"CY"); + center->z = tag->getInt(L"CZ"); + accCenter->x = tag->getInt(L"ACX"); + accCenter->y = tag->getInt(L"ACY"); + accCenter->z = tag->getInt(L"ACZ"); + + ListTag *doorTags = (ListTag *) tag->getList(L"Doors"); + for (int i = 0; i < doorTags->size(); i++) + { + CompoundTag *dTag = doorTags->get(i); + + shared_ptr door = shared_ptr(new DoorInfo(dTag->getInt(L"X"), dTag->getInt(L"Y"), dTag->getInt(L"Z"), dTag->getInt(L"IDX"), dTag->getInt(L"IDZ"), dTag->getInt(L"TS"))); + doorInfos.push_back(door); + } + + ListTag *playerTags = (ListTag *) tag->getList(L"Players"); + for (int i = 0; i < playerTags->size(); i++) + { + CompoundTag *pTag = playerTags->get(i); + playerStanding.insert(pair(pTag->getString(L"Name"), pTag->getInt(L"S"))); + } +} + +void Village::addAdditonalSaveData(CompoundTag *tag) +{ + tag->putInt(L"PopSize", populationSize); + tag->putInt(L"Radius", radius); + tag->putInt(L"Golems", golemCount); + tag->putInt(L"Stable", stableSince); + tag->putInt(L"Tick", _tick); + tag->putInt(L"MTick", noBreedTimer); + tag->putInt(L"CX", center->x); + tag->putInt(L"CY", center->y); + tag->putInt(L"CZ", center->z); + tag->putInt(L"ACX", accCenter->x); + tag->putInt(L"ACY", accCenter->y); + tag->putInt(L"ACZ", accCenter->z); + + ListTag *doorTags = new ListTag(L"Doors"); + //for (DoorInfo dm : doorInfos) + for(AUTO_VAR(it,doorInfos.begin()); it != doorInfos.end(); ++it) + { + shared_ptr dm = *it; + CompoundTag *doorTag = new CompoundTag(L"Door"); + doorTag->putInt(L"X", dm->x); + doorTag->putInt(L"Y", dm->y); + doorTag->putInt(L"Z", dm->z); + doorTag->putInt(L"IDX", dm->insideDx); + doorTag->putInt(L"IDZ", dm->insideDz); + doorTag->putInt(L"TS", dm->timeStamp); + doorTags->add(doorTag); + } + tag->put(L"Doors", doorTags); + + ListTag *playerTags = new ListTag(L"Players"); + //for (String player : playerStanding.keySet()) + for(AUTO_VAR(it, playerStanding.begin()); it != playerStanding.end(); ++it) + { + wstring player = it->first; + CompoundTag *playerTag = new CompoundTag(player); + playerTag->putString(L"Name", player); + playerTag->putInt(L"S", it->second); + playerTags->add(playerTag); + } + tag->put(L"Players", playerTags); + +} + +void Village::resetNoBreedTimer() +{ + noBreedTimer = _tick; +} + +bool Village::isBreedTimerOk() +{ + // prevent new villagers if a villager was killed by a mob within 3 + // minutes + return noBreedTimer == 0 || (_tick - noBreedTimer) >= (SharedConstants::TICKS_PER_SECOND * 60 * 3); +} + +void Village::rewardAllPlayers(int amount) +{ + //for (String player : playerStanding.keySet()) + for(AUTO_VAR(it, playerStanding.begin()); it != playerStanding.end(); ++it) + { + modifyStanding(it->first, amount); + } +} \ No newline at end of file diff --git a/Minecraft.World/Village.h b/Minecraft.World/Village.h new file mode 100644 index 00000000..07858ef9 --- /dev/null +++ b/Minecraft.World/Village.h @@ -0,0 +1,81 @@ +#pragma once + +class Village +{ +private: + Level *level; + vector > doorInfos; + + Pos *accCenter; + Pos *center; + int radius; + int stableSince; + int _tick; + int populationSize; + int noBreedTimer; + + unordered_map playerStanding; + + class Aggressor + { + public: + shared_ptr mob; + int timeStamp; + + Aggressor(shared_ptr mob, int timeStamp); + }; + + vector aggressors; + int golemCount; + +public: + Village(); + Village(Level *level); + ~Village(); + + void setLevel(Level *level); + + void tick(int tick); + +private: + Vec3 *findRandomSpawnPos(int x, int y, int z, int sx, int sy, int sz); + bool canSpawnAt(int x, int y, int z, int sx, int sy, int sz); + void countGolem(); + void countPopulation(); + +public: + Pos *getCenter(); + int getRadius(); + int getDoorCount(); + int getStableAge(); + int getPopulationSize(); + bool isInside(int xx, int yy, int zz); + vector > *getDoorInfos(); + shared_ptr getClosestDoorInfo(int x, int y, int z); + shared_ptr getBestDoorInfo(int x, int y, int z); + bool hasDoorInfo(int x, int y, int z); + shared_ptr getDoorInfo(int x, int y, int z); + void addDoorInfo(shared_ptr di); + bool canRemove(); + void addAggressor(shared_ptr mob); + shared_ptr getClosestAggressor(shared_ptr from); + shared_ptr getClosestBadStandingPlayer(shared_ptr from); // 4J Stu - Should be LivingEntity when we add that + +private: + void updateAggressors(); + void updateDoors(); + bool isDoor(int x, int y, int z); + void calcInfo(); + +public: + int getStanding(const wstring &playerName); + int modifyStanding(const wstring &playerName, int delta); + bool isGoodStanding(const wstring &playerName); + bool isBadStanding(const wstring &playerName); + bool isVeryBadStanding(const wstring playerName); + void readAdditionalSaveData(CompoundTag *tag); + void addAdditonalSaveData(CompoundTag *tag); + void resetNoBreedTimer(); + bool isBreedTimerOk(); + void rewardAllPlayers(int amount); +}; \ No newline at end of file diff --git a/Minecraft.World/VillageFeature.cpp b/Minecraft.World/VillageFeature.cpp new file mode 100644 index 00000000..be724be6 --- /dev/null +++ b/Minecraft.World/VillageFeature.cpp @@ -0,0 +1,144 @@ +#include "stdafx.h" +#include "VillageFeature.h" +#include "VillagePieces.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.level.dimension.h" + +vector VillageFeature::allowedBiomes; + +void VillageFeature::staticCtor() +{ + allowedBiomes.push_back( Biome::plains ); + allowedBiomes.push_back( Biome::desert ); +} + + +VillageFeature::VillageFeature(int villageSizeModifier, int iXZSize) : StructureFeature(), villageSizeModifier(villageSizeModifier) +{ + m_iXZSize=iXZSize; +} + +bool VillageFeature::isFeatureChunk(int x, int z,bool bIsSuperflat) +{ + int townSpacing; + +#ifdef _LARGE_WORLDS + if(level->dimension->getXZSize() > 128) + { + townSpacing = 32; + } + else +#endif + if(bIsSuperflat) + { + townSpacing= 32; + } + else + { + townSpacing= 16;// 4J change 32; + } + + int minTownSeparation = 8; + + int xx = x; + int zz = z; + if (x < 0) x -= townSpacing - 1; + if (z < 0) z -= townSpacing - 1; + + int xCenterTownChunk = x / townSpacing; + int zCenterTownChunk = z / townSpacing; + Random *r = level->getRandomFor(xCenterTownChunk, zCenterTownChunk, 10387312); + xCenterTownChunk *= townSpacing; + zCenterTownChunk *= townSpacing; + xCenterTownChunk += r->nextInt(townSpacing - minTownSeparation); + zCenterTownChunk += r->nextInt(townSpacing - minTownSeparation); + x = xx; + z = zz; + + bool forcePlacement = false; + LevelGenerationOptions *levelGenOptions = app.getLevelGenerationOptions(); + if( levelGenOptions != NULL ) + { + forcePlacement = levelGenOptions->isFeatureChunk(x,z,eFeature_Village); + } + + if (forcePlacement || (x == xCenterTownChunk && z == zCenterTownChunk) ) + { + bool biomeOk = level->getBiomeSource()->containsOnly(x * 16 + 8, z * 16 + 8, 0, allowedBiomes); + if (biomeOk) + { + //app.DebugPrintf("Biome ok for Village at %d, %d\n",(x * 16 + 8),(z * 16 + 8)); + return true; + } + } + + return false; +} + +StructureStart *VillageFeature::createStructureStart(int x, int z) +{ + // 4J added + app.AddTerrainFeaturePosition(eTerrainFeature_Village,x,z); + + return new VillageStart(level, random, x, z, villageSizeModifier, m_iXZSize); +} + +VillageFeature::VillageStart::VillageStart(Level *level, Random *random, int chunkX, int chunkZ, int villageSizeModifier, int iXZSize) +{ + valid = false; // 4J added initialiser + m_iXZSize=iXZSize; + + list *pieceSet = VillagePieces::createPieceSet(random, villageSizeModifier); + + VillagePieces::StartPiece *startRoom = new VillagePieces::StartPiece(level->getBiomeSource(), 0, random, (chunkX << 4) + 2, (chunkZ << 4) + 2, pieceSet, villageSizeModifier, level); + pieces.push_back(startRoom); + startRoom->addChildren(startRoom, &pieces, random); + + vector *pendingRoads = &startRoom->pendingRoads; + vector *pendingHouses = &startRoom->pendingHouses; + while (!pendingRoads->empty() || !pendingHouses->empty()) + { + + // prioritize roads + if (pendingRoads->empty()) + { + int pos = random->nextInt((int)pendingHouses->size()); + AUTO_VAR(it, pendingHouses->begin() + pos); + StructurePiece *structurePiece = *it; + pendingHouses->erase(it); + structurePiece->addChildren(startRoom, &pieces, random); + } + else + { + int pos = random->nextInt((int)pendingRoads->size()); + AUTO_VAR(it, pendingRoads->begin() + pos); + StructurePiece *structurePiece = *it; + pendingRoads->erase(it); + structurePiece->addChildren(startRoom, &pieces, random); + } + } + + calculateBoundingBox(); + + int count = 0; + for( AUTO_VAR(it, pieces.begin()); it != pieces.end(); it++ ) + { + StructurePiece *piece = *it; + if (dynamic_cast(piece) == NULL) + { + count++; + } + } + valid = count > 2; +} + +bool VillageFeature::VillageStart::isValid() +{ + // 4J-PB - Adding a bounds check to ensure a village isn't over the edge of our world - we end up with half houses in that case + if((boundingBox->x0<(-m_iXZSize/2)) || (boundingBox->x1>(m_iXZSize/2)) || (boundingBox->z0<(-m_iXZSize/2)) || (boundingBox->z1>(m_iXZSize/2))) + { + valid=false; + } + return valid; +} diff --git a/Minecraft.World/VillageFeature.h b/Minecraft.World/VillageFeature.h new file mode 100644 index 00000000..e998918a --- /dev/null +++ b/Minecraft.World/VillageFeature.h @@ -0,0 +1,31 @@ +#pragma once +#include "StructureFeature.h" +#include "StructureStart.h" +class Biome; + +class VillageFeature : public StructureFeature +{ +private: + const int villageSizeModifier; +public: + static void staticCtor(); + static vector allowedBiomes; + VillageFeature(int villageSizeModifier, int iXZSize); + +protected: + virtual bool isFeatureChunk(int x, int z, bool bIsSuperflat=false); + virtual StructureStart *createStructureStart(int x, int z); + +private: + class VillageStart : public StructureStart + { + private: + bool valid; + int m_iXZSize; + public: + VillageStart(Level *level, Random *random, int chunkX, int chunkZ, int villageSizeModifier,int iXZSize); + bool isValid(); + }; + + int m_iXZSize; +}; diff --git a/Minecraft.World/VillagePieces.cpp b/Minecraft.World/VillagePieces.cpp new file mode 100644 index 00000000..27b21aa5 --- /dev/null +++ b/Minecraft.World/VillagePieces.cpp @@ -0,0 +1,1847 @@ +#include "stdafx.h" +#include "net.minecraft.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.storage.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.levelgen.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.dimension.h" +#include "net.minecraft.world.entity.npc.h" +#include "WeighedTreasure.h" +#include "VillagePieces.h" +#include "VillageFeature.h" +#include "Direction.h" +#include "JavaMath.h" +#include "BiomeSource.h" + +WeighedTreasureArray VillagePieces::Smithy::treasureItems; + +VillagePieces::PieceWeight::PieceWeight(VillagePieces::EPieceClass pieceClass, int weight, int maxPlaceCount) : weight(weight) +{ + this->placeCount = 0; // 4J added initialiser + this->pieceClass = pieceClass; + this->maxPlaceCount = maxPlaceCount; +} + +bool VillagePieces::PieceWeight::doPlace(int depth) +{ + return maxPlaceCount == 0 || placeCount < maxPlaceCount; +} + +bool VillagePieces::PieceWeight::isValid() +{ + return maxPlaceCount == 0 || placeCount < maxPlaceCount; +} + +list *VillagePieces::createPieceSet(Random *random, int villageSize) +{ + list *newPieces = new list; + + newPieces->push_back(new PieceWeight(VillagePieces::EPieceClass_SimpleHouse, 4, Mth::nextInt(random, 2 + villageSize, 4 + villageSize * 2))); + newPieces->push_back(new PieceWeight(VillagePieces::EPieceClass_SmallTemple, 20, Mth::nextInt(random, 0 + villageSize, 1 + villageSize))); + newPieces->push_back(new PieceWeight(VillagePieces::EPieceClass_BookHouse, 20, Mth::nextInt(random, 0 + villageSize, 2 + villageSize))); + newPieces->push_back(new PieceWeight(VillagePieces::EPieceClass_SmallHut, 3, Mth::nextInt(random, 2 + villageSize, 5 + villageSize * 3))); + newPieces->push_back(new PieceWeight(VillagePieces::EPieceClass_PigHouse, 15, Mth::nextInt(random, 0 + villageSize, 2 + villageSize))); + newPieces->push_back(new PieceWeight(VillagePieces::EPieceClass_DoubleFarmland, 3, Mth::nextInt(random, 1 + villageSize, 4 + villageSize))); + newPieces->push_back(new PieceWeight(VillagePieces::EPieceClass_Farmland, 3, Mth::nextInt(random, 2 + villageSize, 4 + villageSize * 2))); + newPieces->push_back(new PieceWeight(VillagePieces::EPieceClass_Smithy, 15, Mth::nextInt(random, 0, 1 + villageSize))); + newPieces->push_back(new PieceWeight(VillagePieces::EPieceClass_TwoRoomHouse, 8, Mth::nextInt(random, 0 + villageSize, 3 + villageSize * 2))); + + // silly way of filtering "infinite" buildings + AUTO_VAR(it, newPieces->begin()); + while( it != newPieces->end() ) + { + if( (*it)->maxPlaceCount == 0 ) + { + delete (*it); + it = newPieces->erase(it); + } + else + { + it++; + } + } + + return newPieces; +} + +int VillagePieces::updatePieceWeight(list *currentPieces) +{ + bool hasAnyPieces = false; + int totalWeight = 0; + for( AUTO_VAR(it, currentPieces->begin()); it != currentPieces->end(); it++ ) + { + PieceWeight *piece = *it; + if (piece->maxPlaceCount > 0 && piece->placeCount < piece->maxPlaceCount) + { + hasAnyPieces = true; + } + totalWeight += piece->weight; + } + return (hasAnyPieces ? totalWeight : -1); +} + +VillagePieces::VillagePiece *VillagePieces::findAndCreatePieceFactory(StartPiece *startPiece, VillagePieces::PieceWeight *piece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth) +{ + VillagePieces::EPieceClass pieceClass = piece->pieceClass; + VillagePiece *villagePiece = NULL; + + if (pieceClass == VillagePieces::EPieceClass_SimpleHouse) + { + villagePiece = SimpleHouse::createPiece(startPiece, pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == VillagePieces::EPieceClass_SmallTemple) + { + villagePiece = SmallTemple::createPiece(startPiece, pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == VillagePieces::EPieceClass_BookHouse) + { + villagePiece = BookHouse::createPiece(startPiece, pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == VillagePieces::EPieceClass_SmallHut) + { + villagePiece = SmallHut::createPiece(startPiece, pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == VillagePieces::EPieceClass_PigHouse) + { + villagePiece = PigHouse::createPiece(startPiece, pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == VillagePieces::EPieceClass_DoubleFarmland) + { + villagePiece = DoubleFarmland::createPiece(startPiece, pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == VillagePieces::EPieceClass_Farmland) + { + villagePiece = Farmland::createPiece(startPiece, pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == VillagePieces::EPieceClass_Smithy) + { + villagePiece = Smithy::createPiece(startPiece, pieces, random, footX, footY, footZ, direction, depth); + } + else if (pieceClass == VillagePieces::EPieceClass_TwoRoomHouse) + { + villagePiece = TwoRoomHouse::createPiece(startPiece, pieces, random, footX, footY, footZ, direction, depth); + } + + return villagePiece; +} + +VillagePieces::VillagePiece *VillagePieces::generatePieceFromSmallDoor(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth) +{ + int totalWeight = updatePieceWeight(startPiece->pieceSet); + if (totalWeight <= 0) + { + return NULL; + } + + int numAttempts = 0; + while (numAttempts < 5) + { + numAttempts++; + + int weightSelection = random->nextInt(totalWeight); + for( AUTO_VAR(it, startPiece->pieceSet->begin()); it != startPiece->pieceSet->end(); it++ ) + { + PieceWeight *piece = *it; + weightSelection -= piece->weight; + if (weightSelection < 0) + { + + if (!piece->doPlace(depth) || (piece == startPiece->previousPiece && startPiece->pieceSet->size() > 1)) + { + break; + } + + VillagePiece *villagePiece = findAndCreatePieceFactory(startPiece, piece, pieces, random, footX, footY, footZ, direction, depth); + if (villagePiece != NULL) + { + piece->placeCount++; + startPiece->previousPiece = piece; + + if (!piece->isValid()) + { + startPiece->pieceSet->remove(piece); + } + return villagePiece; + } + } + } + } + + // attempt to place a light post instead + { + BoundingBox *box = LightPost::findPieceBox(startPiece, pieces, random, footX, footY, footZ, direction); + if (box != NULL) + { + return new LightPost(startPiece, depth, random, box, direction); + } + delete box; + } + + return NULL; +} + +StructurePiece *VillagePieces::generateAndAddPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth) +{ + if (depth > MAX_DEPTH) + { + return NULL; + } + if (abs(footX - startPiece->getBoundingBox()->x0) > 7 * 16 || abs(footZ - startPiece->getBoundingBox()->z0) > 7 * 16) + { + return NULL; + } + + StructurePiece *newPiece = generatePieceFromSmallDoor(startPiece, pieces, random, footX, footY, footZ, direction, depth + 1); + if (newPiece != NULL) + { + int x = (newPiece->boundingBox->x0 + newPiece->boundingBox->x1) / 2; + int z = (newPiece->boundingBox->z0 + newPiece->boundingBox->z1) / 2; + int xs = newPiece->boundingBox->x1 - newPiece->boundingBox->x0; + int zs = newPiece->boundingBox->z1 - newPiece->boundingBox->z0; + int r = xs > zs ? xs : zs; + if (startPiece->getBiomeSource()->containsOnly(x, z, r / 2 + 4, VillageFeature::allowedBiomes)) + { + pieces->push_back(newPiece); + startPiece->pendingHouses.push_back(newPiece); + return newPiece; + } + delete newPiece; + } + return NULL; +} + +StructurePiece *VillagePieces::generateAndAddRoadPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth) +{ + if (depth > BASE_ROAD_DEPTH + startPiece->villageSize) + { + return NULL; + } + if (abs(footX - startPiece->getBoundingBox()->x0) > 7 * 16 || abs(footZ - startPiece->getBoundingBox()->z0) > 7 * 16) + { + return NULL; + } + + BoundingBox *box = StraightRoad::findPieceBox(startPiece, pieces, random, footX, footY, footZ, direction); + if (box != NULL && box->y0 > LOWEST_Y_POSITION) + { + StructurePiece *newPiece = new StraightRoad(startPiece, depth, random, box, direction); + int x = (newPiece->boundingBox->x0 + newPiece->boundingBox->x1) / 2; + int z = (newPiece->boundingBox->z0 + newPiece->boundingBox->z1) / 2; + int xs = newPiece->boundingBox->x1 - newPiece->boundingBox->x0; + int zs = newPiece->boundingBox->z1 - newPiece->boundingBox->z0; + int r = xs > zs ? xs : zs; + if (startPiece->getBiomeSource()->containsOnly(x, z, r / 2 + 4, VillageFeature::allowedBiomes)) + { + pieces->push_back(newPiece); + startPiece->pendingRoads.push_back(newPiece); + return newPiece; + } + // 4J Stu - The dtor for newPiece will destroy box + delete newPiece; + } + else if(box != NULL) + { + delete box; + } + + return NULL; +} + +VillagePieces::VillagePiece::VillagePiece(StartPiece *startPiece, int genDepth) : StructurePiece(genDepth) +{ + spawnedVillagerCount = 0; + this->startPiece = startPiece; +} + +StructurePiece *VillagePieces::VillagePiece::generateHouseNorthernLeft(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff) +{ + switch (orientation) + { + case Direction::NORTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::WEST, getGenDepth()); + case Direction::SOUTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::WEST, getGenDepth()); + case Direction::WEST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z0 - 1, Direction::NORTH, getGenDepth()); + case Direction::EAST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z0 - 1, Direction::NORTH, getGenDepth()); + } + return NULL; +} + +StructurePiece *VillagePieces::VillagePiece::generateHouseNorthernRight(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff) +{ + switch (orientation) + { + case Direction::NORTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::EAST, getGenDepth()); + case Direction::SOUTH: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0 + yOff, boundingBox->z0 + zOff, Direction::EAST, getGenDepth()); + case Direction::WEST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z1 + 1, Direction::SOUTH, getGenDepth()); + case Direction::EAST: + return generateAndAddPiece(startPiece, pieces, random, boundingBox->x0 + zOff, boundingBox->y0 + yOff, boundingBox->z1 + 1, Direction::SOUTH, getGenDepth()); + } + return NULL; +} + +int VillagePieces::VillagePiece::getAverageGroundHeight(Level *level, BoundingBox *chunkBB) +{ + int total = 0; + int count = 0; + for (int z = boundingBox->z0; z <= boundingBox->z1; z++) + { + for (int x = boundingBox->x0; x <= boundingBox->x1; x++) + { + if (chunkBB->isInside(x, 64, z)) + { + total += Math::_max(level->getTopSolidBlock(x, z), level->dimension->getSpawnYPosition()); + count++; + } + } + } + + if (count == 0) + { + return -1; + } + return total / count; +} + +bool VillagePieces::VillagePiece::isOkBox(BoundingBox *box, StartPiece *startRoom) +{ + bool bIsOk = false; + + if(box != NULL) + { + if( box->y0 > LOWEST_Y_POSITION ) bIsOk = true; + + int xzSize = startRoom->m_level->getLevelData()->getXZSize(); + int blockMin = -( (xzSize << 4) / 2) + 1; + int blockMax = ( (xzSize << 4) / 2 ) - 1; + + if(box->x0 <= blockMin) bIsOk = false; + if(box->z0 <= blockMin) bIsOk = false; + if(box->x1 >= blockMax) bIsOk = false; + if(box->z1 >= blockMax) bIsOk = false; + } + + return bIsOk; +} + +void VillagePieces::VillagePiece::spawnVillagers(Level *level, BoundingBox *chunkBB, int x, int y, int z, int count) +{ + if (spawnedVillagerCount >= count) + { + return; + } + + for (int i = spawnedVillagerCount; i < count; i++) + { + int worldX = getWorldX(x + i, z); + int worldY = getWorldY(y); + int worldZ = getWorldZ(x + i, z); + + if (chunkBB->isInside(worldX, worldY, worldZ)) + { + spawnedVillagerCount++; + + shared_ptr villager = shared_ptr(new Villager(level, getVillagerProfession(i))); + villager->moveTo(worldX + 0.5, worldY, worldZ + 0.5, 0, 0); + level->addEntity(villager); + } + else + { + // try again later + break; + } + } +} + +int VillagePieces::VillagePiece::getVillagerProfession(int villagerNumber) +{ + return Villager::PROFESSION_FARMER; +} + +int VillagePieces::VillagePiece::biomeBlock(int tile, int data) +{ + if (startPiece->isDesertVillage) + { + if (tile == Tile::treeTrunk_Id) + { + return Tile::sandStone_Id; + } + else if (tile == Tile::stoneBrick_Id) + { + return Tile::sandStone_Id; + } + else if (tile == Tile::wood_Id) + { + return Tile::sandStone_Id; + } + else if (tile == Tile::stairs_wood_Id) + { + return Tile::stairs_sandstone_Id; + } + else if (tile == Tile::stairs_stone_Id) + { + return Tile::stairs_sandstone_Id; + } + else if (tile == Tile::gravel_Id) + { + return Tile::sandStone_Id; + } + } + return tile; +} + +int VillagePieces::VillagePiece::biomeData(int tile, int data) +{ + if (startPiece->isDesertVillage) + { + if (tile == Tile::treeTrunk_Id) + { + return 0; + } + else if (tile == Tile::stoneBrick_Id) + { + return SandStoneTile::TYPE_DEFAULT; + } + else if (tile == Tile::wood_Id) + { + return SandStoneTile::TYPE_SMOOTHSIDE; + } + } + return data; +} + +void VillagePieces::VillagePiece::placeBlock(Level *level, int block, int data, int x, int y, int z, BoundingBox *chunkBB) +{ + int bblock = biomeBlock(block, data); + int bdata = biomeData(block, data); + StructurePiece::placeBlock(level, bblock, bdata, x, y, z, chunkBB); +} + +void VillagePieces::VillagePiece::generateBox(Level *level, BoundingBox *chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, int edgeTile, int fillTile, bool skipAir) +{ + int bEdge = biomeBlock(edgeTile, 0); + int bEdgeData = biomeData(edgeTile, 0); + int bFill = biomeBlock(fillTile, 0); + int bFillData = biomeData(fillTile, 0); + StructurePiece::generateBox(level, chunkBB, x0, y0, z0, x1, y1, z1, bEdge, bEdgeData, bFill, bFillData, skipAir); +} + +void VillagePieces::VillagePiece::fillColumnDown(Level *level, int block, int data, int x, int startY, int z, BoundingBox *chunkBB) +{ + int bblock = biomeBlock(block, data); + int bdata = biomeData(block, data); + StructurePiece::fillColumnDown(level, bblock, bdata, x, startY, z, chunkBB); +} + +VillagePieces::Well::Well(StartPiece *startPiece, int genDepth, Random *random, int west, int north) : VillagePiece(startPiece, genDepth), isSource(true) +{ + heightPosition = -1; // 4J added initialiser + orientation = random->nextInt(4); + + switch (orientation) + { + case Direction::NORTH: + case Direction::SOUTH: + boundingBox = new BoundingBox(west, 64, north, west + width - 1, 64 + height - 1, north + depth - 1); + break; + default: + boundingBox = new BoundingBox(west, 64, north, west + depth - 1, 64 + height - 1, north + width - 1); + break; + } +} + +VillagePieces::Well::Well(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction) : VillagePiece(startPiece, genDepth), isSource(false) +{ + heightPosition = -1; // 4J added initialiser + + orientation = direction; + boundingBox = stairsBox; +} + +void VillagePieces::Well::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + generateAndAddRoadPiece((StartPiece *) startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y1 - 4, boundingBox->z0 + 1, Direction::WEST, getGenDepth()); + generateAndAddRoadPiece((StartPiece *) startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y1 - 4, boundingBox->z0 + 1, Direction::EAST, getGenDepth()); + generateAndAddRoadPiece((StartPiece *) startPiece, pieces, random, boundingBox->x0 + 1, boundingBox->y1 - 4, boundingBox->z0 - 1, Direction::NORTH, getGenDepth()); + generateAndAddRoadPiece((StartPiece *) startPiece, pieces, random, boundingBox->x0 + 1, boundingBox->y1 - 4, boundingBox->z1 + 1, Direction::SOUTH, getGenDepth()); +} + +//VillagePieces::Well *VillagePieces::Well::createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +//{ +// BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, -1, 4 - height, 0, width, height, depth, direction); +// +// if (!isOkBox(box) || StructurePiece::findCollisionPiece(pieces, box) != NULL) +// { +// delete box; +// return NULL; +// } +// +// return new Well(genDepth, random, box, direction); +//} + +bool VillagePieces::Well::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (heightPosition < 0) + { + heightPosition = getAverageGroundHeight(level, chunkBB); + if (heightPosition < 0) + { + return true; + } + boundingBox->move(0, heightPosition - boundingBox->y1 + 3, 0); + } + + generateBox(level, chunkBB, 1, 0, 1, 4, height - 3, 4, Tile::stoneBrick_Id, Tile::water_Id, false); + placeBlock(level, 0, 0, 2, height - 3, 2, chunkBB); + placeBlock(level, 0, 0, 3, height - 3, 2, chunkBB); + placeBlock(level, 0, 0, 2, height - 3, 3, chunkBB); + placeBlock(level, 0, 0, 3, height - 3, 3, chunkBB); + + placeBlock(level, Tile::fence_Id, 0, 1, height - 2, 1, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 1, height - 1, 1, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 4, height - 2, 1, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 4, height - 1, 1, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 1, height - 2, 4, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 1, height - 1, 4, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 4, height - 2, 4, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 4, height - 1, 4, chunkBB); + generateBox(level, chunkBB, 1, height, 1, 4, height, 4, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + + for (int z = 0; z <= 5; z++) + { + for (int x = 0; x <= 5; x++) + { + // only do the frame + if (x != 0 && x != 5 && z != 0 && z != 5) + { + continue; + } + placeBlock(level, Tile::gravel_Id, 0, x, height - 4, z, chunkBB); + generateAirColumnUp(level, x, height - 3, z, chunkBB); + } + } + + return true; + +} + +VillagePieces::StartPiece::StartPiece(BiomeSource *biomeSource, int genDepth, Random *random, int west, int north, list *pieceSet, int villageSize, Level *level) : Well(NULL, 0, random, west, north) +{ + isLibraryAdded = false; // 4J - added initialiser + previousPiece = NULL; // 4J - added initialiser + this->biomeSource = biomeSource; + this->pieceSet = pieceSet; + this->villageSize = villageSize; + m_level = level; + + Biome *biome = biomeSource->getBiome(west, north); + this->isDesertVillage = biome == Biome::desert || biome == Biome::desertHills; + this->startPiece = this; +} + +VillagePieces::StartPiece::~StartPiece() +{ + for(AUTO_VAR(it, pieceSet->begin()); it != pieceSet->end(); it++ ) + { + delete (*it); + } + delete pieceSet; +} + +BiomeSource *VillagePieces::StartPiece::getBiomeSource() +{ + return biomeSource; +} + +VillagePieces::StraightRoad::StraightRoad(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction) : VillageRoadPiece(startPiece, genDepth) +{ + orientation = direction; + boundingBox = stairsBox; + length = Math::_max(stairsBox->getXSpan(), stairsBox->getZSpan()); +} + +void VillagePieces::StraightRoad::addChildren(StructurePiece *startPiece, list *pieces, Random *random) +{ + bool hasHouses = false; + + // place left houses + int depth = random->nextInt(5); + while (depth < length - 8) + { + StructurePiece *piece = generateHouseNorthernLeft((StartPiece *) startPiece, pieces, random, 0, depth); + if (piece != NULL) + { + depth += Math::_max(piece->boundingBox->getXSpan(), piece->boundingBox->getZSpan()); + hasHouses = true; + } + depth += 2 + random->nextInt(5); + } + + // place right houses + depth = random->nextInt(5); + while (depth < length - 8) + { + StructurePiece *piece = generateHouseNorthernRight((StartPiece *) startPiece, pieces, random, 0, depth); + if (piece != NULL) + { + depth += Math::_max(piece->boundingBox->getXSpan(), piece->boundingBox->getZSpan()); + hasHouses = true; + } + depth += 2 + random->nextInt(5); + } + + if (hasHouses && random->nextInt(3) > 0) + { + switch (orientation) + { + case Direction::NORTH: + generateAndAddRoadPiece((StartPiece *) startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0, boundingBox->z0, Direction::WEST, getGenDepth()); + break; + case Direction::SOUTH: + generateAndAddRoadPiece((StartPiece *) startPiece, pieces, random, boundingBox->x0 - 1, boundingBox->y0, boundingBox->z1 - 2, Direction::WEST, getGenDepth()); + break; + case Direction::EAST: + generateAndAddRoadPiece((StartPiece *) startPiece, pieces, random, boundingBox->x1 - 2, boundingBox->y0, boundingBox->z0 - 1, Direction::NORTH, getGenDepth()); + break; + case Direction::WEST: + generateAndAddRoadPiece((StartPiece *) startPiece, pieces, random, boundingBox->x0, boundingBox->y0, boundingBox->z0 - 1, Direction::NORTH, getGenDepth()); + break; + } + } + if (hasHouses && random->nextInt(3) > 0) + { + switch (orientation) + { + case Direction::NORTH: + generateAndAddRoadPiece((StartPiece *) startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0, boundingBox->z0, Direction::EAST, getGenDepth()); + break; + case Direction::SOUTH: + generateAndAddRoadPiece((StartPiece *) startPiece, pieces, random, boundingBox->x1 + 1, boundingBox->y0, boundingBox->z1 - 2, Direction::EAST, getGenDepth()); + break; + case Direction::EAST: + generateAndAddRoadPiece((StartPiece *) startPiece, pieces, random, boundingBox->x1 - 2, boundingBox->y0, boundingBox->z1 + 1, Direction::SOUTH, getGenDepth()); + break; + case Direction::WEST: + generateAndAddRoadPiece((StartPiece *) startPiece, pieces, random, boundingBox->x0, boundingBox->y0, boundingBox->z1 + 1, Direction::SOUTH, getGenDepth()); + break; + } + } +} + +BoundingBox *VillagePieces::StraightRoad::findPieceBox(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction) +{ + int length = 7 * (Mth::nextInt(random, 3, 5)); + + while (length >= 7) + { + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, 0, 0, 0, width, 3, length, direction); + + if (isOkBox(box, startPiece) && StructurePiece::findCollisionPiece(pieces, box) == NULL) + { + return box; + } + delete box; + length -= 7; + } + + return NULL; +} + +bool VillagePieces::StraightRoad::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + int tile = biomeBlock(Tile::gravel_Id, 0); + for (int x = boundingBox->x0; x <= boundingBox->x1; x++) + { + for (int z = boundingBox->z0; z <= boundingBox->z1; z++) + { + if (chunkBB->isInside(x, 64, z)) + { + int y = level->getTopSolidBlock(x, z) - 1; + level->setTileNoUpdate(x, y, z,tile); + } + } + } + + return true; +} + +/* +int heightPosition; +const bool hasTerrace;*/ + +VillagePieces::SimpleHouse::SimpleHouse(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction) : VillagePiece(startPiece, genDepth), hasTerrace(random->nextBoolean()) +{ + heightPosition = -1; // 4J added initialiser + orientation = direction; + boundingBox = stairsBox; +} + +VillagePieces::SimpleHouse *VillagePieces::SimpleHouse::createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, 0, 0, 0, width, height, depth, direction); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new SimpleHouse(startPiece, genDepth, random, box, direction); +} + +bool VillagePieces::SimpleHouse::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (heightPosition < 0) + { + heightPosition = getAverageGroundHeight(level, chunkBB); + if (heightPosition < 0) + { + return true; + } + boundingBox->move(0, heightPosition - boundingBox->y1 + height - 1, 0); + } + + // floor + generateBox(level, chunkBB, 0, 0, 0, 4, 0, 4, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + // roof + generateBox(level, chunkBB, 0, 4, 0, 4, 4, 4, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 1, 4, 1, 3, 4, 3, Tile::wood_Id, Tile::wood_Id, false); + + // window walls + placeBlock(level, Tile::stoneBrick_Id, 0, 0, 1, 0, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 0, 2, 0, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 0, 3, 0, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 4, 1, 0, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 4, 2, 0, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 4, 3, 0, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 0, 1, 4, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 0, 2, 4, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 0, 3, 4, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 4, 1, 4, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 4, 2, 4, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 4, 3, 4, chunkBB); + generateBox(level, chunkBB, 0, 1, 1, 0, 3, 3, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 4, 1, 1, 4, 3, 3, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 1, 1, 4, 3, 3, 4, Tile::wood_Id, Tile::wood_Id, false); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 2, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 2, 2, 4, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 4, 2, 2, chunkBB); + + // door wall + placeBlock(level, Tile::wood_Id, 0, 1, 1, 0, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 1, 2, 0, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 1, 3, 0, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 2, 3, 0, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 3, 3, 0, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 3, 2, 0, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 3, 1, 0, chunkBB); + if (getBlock(level, 2, 0, -1, chunkBB) == 0 && getBlock(level, 2, -1, -1, chunkBB) != 0) + { + placeBlock(level, Tile::stairs_stone_Id, getOrientationData(Tile::stairs_stone_Id, 3), 2, 0, -1, chunkBB); + } + + // fill room with air + generateBox(level, chunkBB, 1, 1, 1, 3, 3, 3, 0, 0, false); + + // roof fence + if (hasTerrace) { + placeBlock(level, Tile::fence_Id, 0, 0, 5, 0, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 1, 5, 0, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 2, 5, 0, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 3, 5, 0, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 4, 5, 0, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 0, 5, 4, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 1, 5, 4, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 2, 5, 4, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 3, 5, 4, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 4, 5, 4, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 4, 5, 1, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 4, 5, 2, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 4, 5, 3, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 0, 5, 1, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 0, 5, 2, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 0, 5, 3, chunkBB); + } + + // ladder + if (hasTerrace) + { + int orientationData = getOrientationData(Tile::ladder_Id, 3); + placeBlock(level, Tile::ladder_Id, orientationData, 3, 1, 3, chunkBB); + placeBlock(level, Tile::ladder_Id, orientationData, 3, 2, 3, chunkBB); + placeBlock(level, Tile::ladder_Id, orientationData, 3, 3, 3, chunkBB); + placeBlock(level, Tile::ladder_Id, orientationData, 3, 4, 3, chunkBB); + } + + // torch + placeBlock(level, Tile::torch_Id, 0, 2, 3, 1, chunkBB); + + for (int z = 0; z < depth; z++) + { + for (int x = 0; x < width; x++) + { + generateAirColumnUp(level, x, height, z, chunkBB); + fillColumnDown(level, Tile::stoneBrick_Id, 0, x, -1, z, chunkBB); + } + } + + spawnVillagers(level, chunkBB, 1, 1, 2, 1); + + return true; + +} + +VillagePieces::SmallTemple::SmallTemple(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction) : VillagePiece(startPiece, genDepth) +{ + heightPosition = -1; // 4J added initialiser + orientation = direction; + boundingBox = stairsBox; +} + +VillagePieces::SmallTemple *VillagePieces::SmallTemple::createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, 0, 0, 0, width, height, depth, direction); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new SmallTemple(startPiece, genDepth, random, box, direction); +} + +bool VillagePieces::SmallTemple::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (heightPosition < 0) + { + heightPosition = getAverageGroundHeight(level, chunkBB); + if (heightPosition < 0) + { + return true; + } + boundingBox->move(0, heightPosition - boundingBox->y1 + height - 1, 0); + } + + // fill inside with air + generateBox(level, chunkBB, 1, 1, 1, 3, 3, 7, 0, 0, false); + generateBox(level, chunkBB, 1, 5, 1, 3, 9, 3, 0, 0, false); + + // floor + generateBox(level, chunkBB, 1, 0, 0, 3, 0, 8, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + + // front wall + generateBox(level, chunkBB, 1, 1, 0, 3, 10, 0, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + // left tall wall + generateBox(level, chunkBB, 0, 1, 1, 0, 10, 3, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + // right tall wall + generateBox(level, chunkBB, 4, 1, 1, 4, 10, 3, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + // left low wall + generateBox(level, chunkBB, 0, 0, 4, 0, 4, 7, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + // right low wall + generateBox(level, chunkBB, 4, 0, 4, 4, 4, 7, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + // far low wall + generateBox(level, chunkBB, 1, 1, 8, 3, 4, 8, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + // far upper wall + generateBox(level, chunkBB, 1, 5, 4, 3, 10, 4, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + + // low roof + generateBox(level, chunkBB, 1, 5, 5, 3, 5, 7, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + // high roof + generateBox(level, chunkBB, 0, 9, 0, 4, 9, 4, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + // middle floor / roof + generateBox(level, chunkBB, 0, 4, 0, 4, 4, 4, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + placeBlock(level, Tile::stoneBrick_Id, 0, 0, 11, 2, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 4, 11, 2, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 2, 11, 0, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 2, 11, 4, chunkBB); + + // altar pieces + placeBlock(level, Tile::stoneBrick_Id, 0, 1, 1, 6, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 1, 1, 7, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 2, 1, 7, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 3, 1, 6, chunkBB); + placeBlock(level, Tile::stoneBrick_Id, 0, 3, 1, 7, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, getOrientationData(Tile::stairs_stone_Id, 3), 1, 1, 5, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, getOrientationData(Tile::stairs_stone_Id, 3), 2, 1, 6, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, getOrientationData(Tile::stairs_stone_Id, 3), 3, 1, 5, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, getOrientationData(Tile::stairs_stone_Id, 1), 1, 2, 7, chunkBB); + placeBlock(level, Tile::stairs_stone_Id, getOrientationData(Tile::stairs_stone_Id, 0), 3, 2, 7, chunkBB); + + // windows + placeBlock(level, Tile::thinGlass_Id, 0, 0, 2, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 3, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 4, 2, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 4, 3, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 6, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 7, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 4, 6, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 4, 7, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 2, 6, 0, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 2, 7, 0, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 2, 6, 4, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 2, 7, 4, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 3, 6, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 4, 3, 6, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 2, 3, 8, chunkBB); + + // torches + placeBlock(level, Tile::torch_Id, 0, 2, 4, 7, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 1, 4, 6, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 3, 4, 6, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 2, 4, 5, chunkBB); + + // ladder + int orientationData = getOrientationData(Tile::ladder_Id, 4); + for (int y = 1; y <= 9; y++) + { + placeBlock(level, Tile::ladder_Id, orientationData, 3, y, 3, chunkBB); + } + + // entrance + placeBlock(level, 0, 0, 2, 1, 0, chunkBB); + placeBlock(level, 0, 0, 2, 2, 0, chunkBB); + createDoor(level, chunkBB, random, 2, 1, 0, getOrientationData(Tile::door_wood_Id, 1)); + if (getBlock(level, 2, 0, -1, chunkBB) == 0 && getBlock(level, 2, -1, -1, chunkBB) != 0) + { + placeBlock(level, Tile::stairs_stone_Id, getOrientationData(Tile::stairs_stone_Id, 3), 2, 0, -1, chunkBB); + } + + + for (int z = 0; z < depth; z++) + { + for (int x = 0; x < width; x++) + { + generateAirColumnUp(level, x, height, z, chunkBB); + fillColumnDown(level, Tile::stoneBrick_Id, 0, x, -1, z, chunkBB); + } + } + + spawnVillagers(level, chunkBB, 2, 1, 2, 1); + + return true; + +} + +int VillagePieces::SmallTemple::getVillagerProfession(int villagerNumber) +{ + return Villager::PROFESSION_PRIEST; +} + + +VillagePieces::BookHouse::BookHouse(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction) : VillagePiece(startPiece, genDepth) +{ + heightPosition = -1; // 4J added initialiser + orientation = direction; + boundingBox = stairsBox; +} + +VillagePieces::BookHouse *VillagePieces::BookHouse::createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, 0, 0, 0, width, height, depth, direction); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new BookHouse(startPiece, genDepth, random, box, direction); +} + +bool VillagePieces::BookHouse::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (heightPosition < 0) + { + heightPosition = getAverageGroundHeight(level, chunkBB); + if (heightPosition < 0) + { + return true; + } + boundingBox->move(0, heightPosition - boundingBox->y1 + height - 1, 0); + } + + // fill inside with air + generateBox(level, chunkBB, 1, 1, 1, 7, 5, 4, 0, 0, false); + + // floor + generateBox(level, chunkBB, 0, 0, 0, 8, 0, 5, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + // roof + generateBox(level, chunkBB, 0, 5, 0, 8, 5, 5, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 0, 6, 1, 8, 6, 4, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 0, 7, 2, 8, 7, 3, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + int southStairs = getOrientationData(Tile::stairs_wood_Id, 3); + int northStairs = getOrientationData(Tile::stairs_wood_Id, 2); + for (int d = -1; d <= 2; d++) { + for (int w = 0; w <= 8; w++) { + placeBlock(level, Tile::stairs_wood_Id, southStairs, w, 6 + d, d, chunkBB); + placeBlock(level, Tile::stairs_wood_Id, northStairs, w, 6 + d, 5 - d, chunkBB); + } + } + + // rock supports + generateBox(level, chunkBB, 0, 1, 0, 0, 1, 5, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 1, 1, 5, 8, 1, 5, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 8, 1, 0, 8, 1, 4, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 2, 1, 0, 7, 1, 0, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 0, 2, 0, 0, 4, 0, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 0, 2, 5, 0, 4, 5, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 8, 2, 5, 8, 4, 5, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 8, 2, 0, 8, 4, 0, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + + // wooden walls + generateBox(level, chunkBB, 0, 2, 1, 0, 4, 4, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 1, 2, 5, 7, 4, 5, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 8, 2, 1, 8, 4, 4, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 1, 2, 0, 7, 4, 0, Tile::wood_Id, Tile::wood_Id, false); + + // windows + placeBlock(level, Tile::thinGlass_Id, 0, 4, 2, 0, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 5, 2, 0, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 6, 2, 0, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 4, 3, 0, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 5, 3, 0, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 6, 3, 0, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 2, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 2, 3, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 3, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 3, 3, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 8, 2, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 8, 2, 3, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 8, 3, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 8, 3, 3, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 2, 2, 5, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 3, 2, 5, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 5, 2, 5, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 6, 2, 5, chunkBB); + + // roof inside and bookshelf + generateBox(level, chunkBB, 1, 4, 1, 7, 4, 1, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 1, 4, 4, 7, 4, 4, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 1, 3, 4, 7, 3, 4, Tile::bookshelf_Id, Tile::bookshelf_Id, false); + + // couch + placeBlock(level, Tile::wood_Id, 0, 7, 1, 4, chunkBB); + placeBlock(level, Tile::stairs_wood_Id, getOrientationData(Tile::stairs_wood_Id, 0), 7, 1, 3, chunkBB); + int orientationData = getOrientationData(Tile::stairs_wood_Id, 3); + placeBlock(level, Tile::stairs_wood_Id, orientationData, 6, 1, 4, chunkBB); + placeBlock(level, Tile::stairs_wood_Id, orientationData, 5, 1, 4, chunkBB); + placeBlock(level, Tile::stairs_wood_Id, orientationData, 4, 1, 4, chunkBB); + placeBlock(level, Tile::stairs_wood_Id, orientationData, 3, 1, 4, chunkBB); + + // tables + placeBlock(level, Tile::fence_Id, 0, 6, 1, 3, chunkBB); + placeBlock(level, Tile::pressurePlate_wood_Id, 0, 6, 2, 3, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 4, 1, 3, chunkBB); + placeBlock(level, Tile::pressurePlate_wood_Id, 0, 4, 2, 3, chunkBB); + placeBlock(level, Tile::workBench_Id, 0, 7, 1, 1, chunkBB); + + // entrance + placeBlock(level, 0, 0, 1, 1, 0, chunkBB); + placeBlock(level, 0, 0, 1, 2, 0, chunkBB); + createDoor(level, chunkBB, random, 1, 1, 0, getOrientationData(Tile::door_wood_Id, 1)); + if (getBlock(level, 1, 0, -1, chunkBB) == 0 && getBlock(level, 1, -1, -1, chunkBB) != 0) + { + placeBlock(level, Tile::stairs_stone_Id, getOrientationData(Tile::stairs_stone_Id, 3), 1, 0, -1, chunkBB); + } + + for (int z = 0; z < depth; z++) + { + for (int x = 0; x < width; x++) + { + generateAirColumnUp(level, x, height, z, chunkBB); + fillColumnDown(level, Tile::stoneBrick_Id, 0, x, -1, z, chunkBB); + } + } + + spawnVillagers(level, chunkBB, 2, 1, 2, 1); + + return true; + +} + +int VillagePieces::BookHouse::getVillagerProfession(int villagerNumber) +{ + return Villager::PROFESSION_LIBRARIAN; +} + +VillagePieces::SmallHut::SmallHut(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction) : VillagePiece(startPiece, genDepth), lowCeiling(random->nextBoolean()), tablePlacement(random->nextInt(3)) +{ + heightPosition = -1; // 4J added initialiser + + orientation = direction; + boundingBox = stairsBox; +} + +VillagePieces::SmallHut *VillagePieces::SmallHut::createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, 0, 0, 0, width, height, depth, direction); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new SmallHut(startPiece, genDepth, random, box, direction); +} + +bool VillagePieces::SmallHut::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (heightPosition < 0) + { + heightPosition = getAverageGroundHeight(level, chunkBB); + if (heightPosition < 0) + { + return true; + } + boundingBox->move(0, heightPosition - boundingBox->y1 + height - 1, 0); + } + + // fill inside with air + generateBox(level, chunkBB, 1, 1, 1, 3, 5, 4, 0, 0, false); + + // floor + generateBox(level, chunkBB, 0, 0, 0, 3, 0, 4, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 1, 0, 1, 2, 0, 3, Tile::dirt_Id, Tile::dirt_Id, false); + // roof + if (lowCeiling) { + generateBox(level, chunkBB, 1, 4, 1, 2, 4, 3, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + } else { + generateBox(level, chunkBB, 1, 5, 1, 2, 5, 3, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + } + placeBlock(level, Tile::treeTrunk_Id, 0, 1, 4, 0, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 2, 4, 0, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 1, 4, 4, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 2, 4, 4, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 0, 4, 1, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 0, 4, 2, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 0, 4, 3, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 3, 4, 1, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 3, 4, 2, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 3, 4, 3, chunkBB); + + // corners + generateBox(level, chunkBB, 0, 1, 0, 0, 3, 0, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 3, 1, 0, 3, 3, 0, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 0, 1, 4, 0, 3, 4, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 3, 1, 4, 3, 3, 4, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + + // wooden walls + generateBox(level, chunkBB, 0, 1, 1, 0, 3, 3, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 3, 1, 1, 3, 3, 3, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 1, 1, 0, 2, 3, 0, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 1, 1, 4, 2, 3, 4, Tile::wood_Id, Tile::wood_Id, false); + + // windows + placeBlock(level, Tile::thinGlass_Id, 0, 0, 2, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 3, 2, 2, chunkBB); + + // table + if (tablePlacement > 0) { + placeBlock(level, Tile::fence_Id, 0, tablePlacement, 1, 3, chunkBB); + placeBlock(level, Tile::pressurePlate_wood_Id, 0, tablePlacement, 2, 3, chunkBB); + } + + // entrance + placeBlock(level, 0, 0, 1, 1, 0, chunkBB); + placeBlock(level, 0, 0, 1, 2, 0, chunkBB); + createDoor(level, chunkBB, random, 1, 1, 0, getOrientationData(Tile::door_wood_Id, 1)); + if (getBlock(level, 1, 0, -1, chunkBB) == 0 && getBlock(level, 1, -1, -1, chunkBB) != 0) + { + placeBlock(level, Tile::stairs_stone_Id, getOrientationData(Tile::stairs_stone_Id, 3), 1, 0, -1, chunkBB); + } + + for (int z = 0; z < depth; z++) + { + for (int x = 0; x < width; x++) + { + generateAirColumnUp(level, x, height, z, chunkBB); + fillColumnDown(level, Tile::stoneBrick_Id, 0, x, -1, z, chunkBB); + } + } + + spawnVillagers(level, chunkBB, 1, 1, 2, 1); + + return true; + +} + +VillagePieces::PigHouse::PigHouse(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction) : VillagePiece(startPiece, genDepth) +{ + heightPosition = -1; // 4J added initialiser + orientation = direction; + boundingBox = stairsBox; +} + +VillagePieces::PigHouse *VillagePieces::PigHouse::createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, 0, 0, 0, width, height, depth, direction); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new PigHouse(startPiece, genDepth, random, box, direction); +} + +bool VillagePieces::PigHouse::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (heightPosition < 0) + { + heightPosition = getAverageGroundHeight(level, chunkBB); + if (heightPosition < 0) + { + return true; + } + boundingBox->move(0, heightPosition - boundingBox->y1 + height - 1, 0); + } + + // fill inside with air + generateBox(level, chunkBB, 1, 1, 1, 7, 4, 4, 0, 0, false); + generateBox(level, chunkBB, 2, 1, 6, 8, 4, 10, 0, 0, false); + + // pig floor + generateBox(level, chunkBB, 2, 0, 6, 8, 0, 10, Tile::dirt_Id, Tile::dirt_Id, false); + placeBlock(level, Tile::stoneBrick_Id, 0, 6, 0, 6, chunkBB); + // pig fence + generateBox(level, chunkBB, 2, 1, 6, 2, 1, 10, Tile::fence_Id, Tile::fence_Id, false); + generateBox(level, chunkBB, 8, 1, 6, 8, 1, 10, Tile::fence_Id, Tile::fence_Id, false); + generateBox(level, chunkBB, 3, 1, 10, 7, 1, 10, Tile::fence_Id, Tile::fence_Id, false); + + // floor + generateBox(level, chunkBB, 1, 0, 1, 7, 0, 4, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 0, 0, 0, 0, 3, 5, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 8, 0, 0, 8, 3, 5, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 1, 0, 0, 7, 1, 0, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 1, 0, 5, 7, 1, 5, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + + // roof + generateBox(level, chunkBB, 1, 2, 0, 7, 3, 0, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 1, 2, 5, 7, 3, 5, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 0, 4, 1, 8, 4, 1, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 0, 4, 4, 8, 4, 4, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 0, 5, 2, 8, 5, 3, Tile::wood_Id, Tile::wood_Id, false); + placeBlock(level, Tile::wood_Id, 0, 0, 4, 2, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 0, 4, 3, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 8, 4, 2, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 8, 4, 3, chunkBB); + + int southStairs = getOrientationData(Tile::stairs_wood_Id, 3); + int northStairs = getOrientationData(Tile::stairs_wood_Id, 2); + for (int d = -1; d <= 2; d++) + { + for (int w = 0; w <= 8; w++) + { + placeBlock(level, Tile::stairs_wood_Id, southStairs, w, 4 + d, d, chunkBB); + placeBlock(level, Tile::stairs_wood_Id, northStairs, w, 4 + d, 5 - d, chunkBB); + } + } + + // windows etc + placeBlock(level, Tile::treeTrunk_Id, 0, 0, 2, 1, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 0, 2, 4, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 8, 2, 1, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 8, 2, 4, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 2, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 2, 3, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 8, 2, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 8, 2, 3, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 2, 2, 5, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 3, 2, 5, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 5, 2, 0, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 6, 2, 5, chunkBB); + + // table + placeBlock(level, Tile::fence_Id, 0, 2, 1, 3, chunkBB); + placeBlock(level, Tile::pressurePlate_wood_Id, 0, 2, 2, 3, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 1, 1, 4, chunkBB); + placeBlock(level, Tile::stairs_wood_Id, getOrientationData(Tile::stairs_wood_Id, 3), 2, 1, 4, chunkBB); + placeBlock(level, Tile::stairs_wood_Id, getOrientationData(Tile::stairs_wood_Id, 1), 1, 1, 3, chunkBB); + + // butcher table + generateBox(level, chunkBB, 5, 0, 1, 7, 0, 3, Tile::stoneSlab_Id, Tile::stoneSlab_Id, false); + placeBlock(level, Tile::stoneSlab_Id, 0, 6, 1, 1, chunkBB); + placeBlock(level, Tile::stoneSlab_Id, 0, 6, 1, 2, chunkBB); + + // entrance + placeBlock(level, 0, 0, 2, 1, 0, chunkBB); + placeBlock(level, 0, 0, 2, 2, 0, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 2, 3, 1, chunkBB); + createDoor(level, chunkBB, random, 2, 1, 0, getOrientationData(Tile::door_wood_Id, 1)); + if (getBlock(level, 2, 0, -1, chunkBB) == 0 && getBlock(level, 2, -1, -1, chunkBB) != 0) + { + placeBlock(level, Tile::stairs_stone_Id, getOrientationData(Tile::stairs_stone_Id, 3), 2, 0, -1, chunkBB); + } + + // pig entrance + placeBlock(level, 0, 0, 6, 1, 5, chunkBB); + placeBlock(level, 0, 0, 6, 2, 5, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 6, 3, 4, chunkBB); + createDoor(level, chunkBB, random, 6, 1, 5, getOrientationData(Tile::door_wood_Id, 1)); + + for (int z = 0; z < 5; z++) + { + for (int x = 0; x < width; x++) + { + generateAirColumnUp(level, x, height, z, chunkBB); + fillColumnDown(level, Tile::stoneBrick_Id, 0, x, -1, z, chunkBB); + } + } + + spawnVillagers(level, chunkBB, 4, 1, 2, 2); + + return true; + +} + +int VillagePieces::PigHouse::getVillagerProfession(int villagerNumber) +{ + if (villagerNumber == 0) + { + return Villager::PROFESSION_BUTCHER; + } + return Villager::PROFESSION_FARMER; +} + +VillagePieces::TwoRoomHouse::TwoRoomHouse(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction) : VillagePiece(startPiece, genDepth) +{ + heightPosition = -1; // 4J added initialiser + + orientation = direction; + boundingBox = stairsBox; +} + +VillagePieces::TwoRoomHouse *VillagePieces::TwoRoomHouse::createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, 0, 0, 0, width, height, depth, direction); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new TwoRoomHouse(startPiece, genDepth, random, box, direction); +} + +bool VillagePieces::TwoRoomHouse::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (heightPosition < 0) + { + heightPosition = getAverageGroundHeight(level, chunkBB); + if (heightPosition < 0) + { + return true; + } + boundingBox->move(0, heightPosition - boundingBox->y1 + height - 1, 0); + } + + // fill inside with air + generateBox(level, chunkBB, 1, 1, 1, 7, 4, 4, 0, 0, false); + generateBox(level, chunkBB, 2, 1, 6, 8, 4, 10, 0, 0, false); + + // floor + generateBox(level, chunkBB, 2, 0, 5, 8, 0, 10, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 1, 0, 1, 7, 0, 4, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 0, 0, 0, 0, 3, 5, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 8, 0, 0, 8, 3, 10, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 1, 0, 0, 7, 2, 0, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 1, 0, 5, 2, 1, 5, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 2, 0, 6, 2, 3, 10, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 3, 0, 10, 7, 3, 10, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + + // room 1 roof + generateBox(level, chunkBB, 1, 2, 0, 7, 3, 0, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 1, 2, 5, 2, 3, 5, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 0, 4, 1, 8, 4, 1, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 0, 4, 4, 3, 4, 4, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 0, 5, 2, 8, 5, 3, Tile::wood_Id, Tile::wood_Id, false); + placeBlock(level, Tile::wood_Id, 0, 0, 4, 2, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 0, 4, 3, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 8, 4, 2, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 8, 4, 3, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 8, 4, 4, chunkBB); + + int southStairs = getOrientationData(Tile::stairs_wood_Id, 3); + int northStairs = getOrientationData(Tile::stairs_wood_Id, 2); + for (int d = -1; d <= 2; d++) + { + for (int w = 0; w <= 8; w++) + { + placeBlock(level, Tile::stairs_wood_Id, southStairs, w, 4 + d, d, chunkBB); + if ((d > -1 || w <= 1) && (d > 0 || w <= 3) && (d > 1 || w <= 4 || w >= 6)) { + placeBlock(level, Tile::stairs_wood_Id, northStairs, w, 4 + d, 5 - d, chunkBB); + } + } + } + + // room 2 roof + generateBox(level, chunkBB, 3, 4, 5, 3, 4, 10, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 7, 4, 2, 7, 4, 10, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 4, 5, 4, 4, 5, 10, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 6, 5, 4, 6, 5, 10, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 5, 6, 3, 5, 6, 10, Tile::wood_Id, Tile::wood_Id, false); + int westStairs = getOrientationData(Tile::stairs_wood_Id, 0); + for (int w = 4; w >= 1; w--) + { + placeBlock(level, Tile::wood_Id, 0, w, 2 + w, 7 - w, chunkBB); + for (int d = 8 - w; d <= 10; d++) + { + placeBlock(level, Tile::stairs_wood_Id, westStairs, w, 2 + w, d, chunkBB); + } + } + int eastStairs = getOrientationData(Tile::stairs_wood_Id, 1); + placeBlock(level, Tile::wood_Id, 0, 6, 6, 3, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 7, 5, 4, chunkBB); + placeBlock(level, Tile::stairs_wood_Id, eastStairs, 6, 6, 4, chunkBB); + for (int w = 6; w <= 8; w++) + { + for (int d = 5; d <= 10; d++) + { + placeBlock(level, Tile::stairs_wood_Id, eastStairs, w, 12 - w, d, chunkBB); + } + } + + // windows etc + placeBlock(level, Tile::treeTrunk_Id, 0, 0, 2, 1, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 0, 2, 4, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 2, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 2, 3, chunkBB); + + placeBlock(level, Tile::treeTrunk_Id, 0, 4, 2, 0, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 5, 2, 0, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 6, 2, 0, chunkBB); + + placeBlock(level, Tile::treeTrunk_Id, 0, 8, 2, 1, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 8, 2, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 8, 2, 3, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 8, 2, 4, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 8, 2, 5, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 8, 2, 6, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 8, 2, 7, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 8, 2, 8, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 8, 2, 9, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 2, 2, 6, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 2, 2, 7, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 2, 2, 8, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 2, 2, 9, chunkBB); + + placeBlock(level, Tile::treeTrunk_Id, 0, 4, 4, 10, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 5, 4, 10, chunkBB); + placeBlock(level, Tile::treeTrunk_Id, 0, 6, 4, 10, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 5, 5, 10, chunkBB); + + // entrance + placeBlock(level, 0, 0, 2, 1, 0, chunkBB); + placeBlock(level, 0, 0, 2, 2, 0, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 2, 3, 1, chunkBB); + createDoor(level, chunkBB, random, 2, 1, 0, getOrientationData(Tile::door_wood_Id, 1)); + generateBox(level, chunkBB, 1, 0, -1, 3, 2, -1, 0, 0, false); + if (getBlock(level, 2, 0, -1, chunkBB) == 0 && getBlock(level, 2, -1, -1, chunkBB) != 0) { + placeBlock(level, Tile::stairs_stone_Id, getOrientationData(Tile::stairs_stone_Id, 3), 2, 0, -1, chunkBB); + } + + for (int z = 0; z < 5; z++) + { + for (int x = 0; x < width; x++) + { + generateAirColumnUp(level, x, height, z, chunkBB); + fillColumnDown(level, Tile::stoneBrick_Id, 0, x, -1, z, chunkBB); + } + } + for (int z = 5; z < depth - 1; z++) + { + for (int x = 2; x < width; x++) + { + generateAirColumnUp(level, x, height, z, chunkBB); + fillColumnDown(level, Tile::stoneBrick_Id, 0, x, -1, z, chunkBB); + } + } + + spawnVillagers(level, chunkBB, 4, 1, 2, 2); + + return true; + +} + +void VillagePieces::Smithy::staticCtor() +{ + treasureItems = WeighedTreasureArray(13); + treasureItems[0] = new WeighedTreasure(Item::diamond_Id, 0, 1, 3, 3); + treasureItems[1] = new WeighedTreasure(Item::ironIngot_Id, 0, 1, 5, 10); + treasureItems[2] = new WeighedTreasure(Item::goldIngot_Id, 0, 1, 3, 5); + treasureItems[3] = new WeighedTreasure(Item::bread_Id, 0, 1, 3, 15); + treasureItems[4] = new WeighedTreasure(Item::apple_Id, 0, 1, 3, 15); + treasureItems[5] = new WeighedTreasure(Item::pickAxe_iron_Id, 0, 1, 1, 5); + treasureItems[6] = new WeighedTreasure(Item::sword_iron_Id, 0, 1, 1, 5); + treasureItems[7] = new WeighedTreasure(Item::chestplate_iron_Id, 0, 1, 1, 5); + treasureItems[8] = new WeighedTreasure(Item::helmet_iron_Id, 0, 1, 1, 5); + treasureItems[9] = new WeighedTreasure(Item::leggings_iron_Id, 0, 1, 1, 5); + treasureItems[10] = new WeighedTreasure(Item::boots_iron_Id, 0, 1, 1, 5); + treasureItems[11] = new WeighedTreasure(Tile::obsidian_Id, 0, 3, 7, 5); + treasureItems[12] = new WeighedTreasure(Tile::sapling_Id, 0, 3, 7, 5); +} + +VillagePieces::Smithy::Smithy(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction) : VillagePiece(startPiece, genDepth) +{ + heightPosition = -1; // 4J added initialiser + hasPlacedChest = false; + + orientation = direction; + boundingBox = stairsBox; +} + +VillagePieces::Smithy *VillagePieces::Smithy::createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, 0, 0, 0, width, height, depth, direction); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new Smithy(startPiece, genDepth, random, box, direction); +} + +bool VillagePieces::Smithy::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (heightPosition < 0) + { + heightPosition = getAverageGroundHeight(level, chunkBB); + if (heightPosition < 0) + { + return true; + } + boundingBox->move(0, heightPosition - boundingBox->y1 + height - 1, 0); + } + + // fill inside with air + generateBox(level, chunkBB, 0, 1, 0, 9, 4, 6, 0, 0, false); + + // floor + generateBox(level, chunkBB, 0, 0, 0, 9, 0, 6, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + + // roof + generateBox(level, chunkBB, 0, 4, 0, 9, 4, 6, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + generateBox(level, chunkBB, 0, 5, 0, 9, 5, 6, Tile::stoneSlabHalf_Id, Tile::stoneSlabHalf_Id, false); + generateBox(level, chunkBB, 1, 5, 1, 8, 5, 5, 0, 0, false); + + // room walls + generateBox(level, chunkBB, 1, 1, 0, 2, 3, 0, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 0, 1, 0, 0, 4, 0, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 3, 1, 0, 3, 4, 0, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 0, 1, 6, 0, 4, 6, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + placeBlock(level, Tile::wood_Id, 0, 3, 3, 1, chunkBB); + generateBox(level, chunkBB, 3, 1, 2, 3, 3, 2, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 4, 1, 3, 5, 3, 3, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 0, 1, 1, 0, 3, 5, Tile::wood_Id, Tile::wood_Id, false); + generateBox(level, chunkBB, 1, 1, 6, 5, 3, 6, Tile::wood_Id, Tile::wood_Id, false); + + // pillars + generateBox(level, chunkBB, 5, 1, 0, 5, 3, 0, Tile::fence_Id, Tile::fence_Id, false); + generateBox(level, chunkBB, 9, 1, 0, 9, 3, 0, Tile::fence_Id, Tile::fence_Id, false); + + // furnace + generateBox(level, chunkBB, 6, 1, 4, 9, 4, 6, Tile::stoneBrick_Id, Tile::stoneBrick_Id, false); + placeBlock(level, Tile::lava_Id, 0, 7, 1, 5, chunkBB); + placeBlock(level, Tile::lava_Id, 0, 8, 1, 5, chunkBB); + placeBlock(level, Tile::ironFence_Id, 0, 9, 2, 5, chunkBB); + placeBlock(level, Tile::ironFence_Id, 0, 9, 2, 4, chunkBB); + generateBox(level, chunkBB, 7, 2, 4, 8, 2, 5, 0, 0, false); + placeBlock(level, Tile::stoneBrick_Id, 0, 6, 1, 3, chunkBB); + placeBlock(level, Tile::furnace_Id, 0, 6, 2, 3, chunkBB); + placeBlock(level, Tile::furnace_Id, 0, 6, 3, 3, chunkBB); + placeBlock(level, Tile::stoneSlab_Id, 0, 8, 1, 1, chunkBB); + + // windows etc + placeBlock(level, Tile::thinGlass_Id, 0, 0, 2, 2, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 0, 2, 4, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 2, 2, 6, chunkBB); + placeBlock(level, Tile::thinGlass_Id, 0, 4, 2, 6, chunkBB); + + // table + placeBlock(level, Tile::fence_Id, 0, 2, 1, 4, chunkBB); + placeBlock(level, Tile::pressurePlate_wood_Id, 0, 2, 2, 4, chunkBB); + placeBlock(level, Tile::wood_Id, 0, 1, 1, 5, chunkBB); + placeBlock(level, Tile::stairs_wood_Id, getOrientationData(Tile::stairs_wood_Id, 3), 2, 1, 5, chunkBB); + placeBlock(level, Tile::stairs_wood_Id, getOrientationData(Tile::stairs_wood_Id, 1), 1, 1, 4, chunkBB); + + if (!hasPlacedChest) + { + int y = getWorldY(1); + int x = getWorldX(5, 5), z = getWorldZ(5, 5); + if (chunkBB->isInside(x, y, z)) + { + hasPlacedChest = true; + createChest(level, chunkBB, random, 5, 1, 5, treasureItems, 3 + random->nextInt(6)); + } + } + + // entrance + for (int x = 6; x <= 8; x++) + { + if (getBlock(level, x, 0, -1, chunkBB) == 0 && getBlock(level, x, -1, -1, chunkBB) != 0 ) + { + placeBlock(level, Tile::stairs_stone_Id, getOrientationData(Tile::stairs_stone_Id, 3), x, 0, -1, chunkBB); + } + } + + for (int z = 0; z < depth; z++) + { + for (int x = 0; x < width; x++) + { + generateAirColumnUp(level, x, height, z, chunkBB); + fillColumnDown(level, Tile::stoneBrick_Id, 0, x, -1, z, chunkBB); + } + } + + spawnVillagers(level, chunkBB, 7, 1, 1, 1); + + return true; + +} + +int VillagePieces::Smithy::getVillagerProfession(int villagerNumber) +{ + return Villager::PROFESSION_SMITH; +} + +VillagePieces::Farmland::Farmland(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction) : VillagePiece(startPiece, genDepth) +{ + heightPosition = -1; // 4J added initialiser + orientation = direction; + boundingBox = stairsBox; + + cropsA = selectCrops(random); + cropsB = selectCrops(random); +} + +int VillagePieces::Farmland::selectCrops(Random *random) +{ + switch (random->nextInt(5)) + { + default: + return Tile::crops_Id; + case 0: + return Tile::carrots_Id; + case 1: + return Tile::potatoes_Id; + } +} + +VillagePieces::Farmland *VillagePieces::Farmland::createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, 0, 0, 0, width, height, depth, direction); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new Farmland(startPiece, genDepth, random, box, direction); +} + +bool VillagePieces::Farmland::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (heightPosition < 0) + { + heightPosition = getAverageGroundHeight(level, chunkBB); + if (heightPosition < 0) + { + return true; + } + boundingBox->move(0, heightPosition - boundingBox->y1 + height - 1, 0); + } + + // fill inside with air + generateBox(level, chunkBB, 0, 1, 0, 6, 4, 8, 0, 0, false); + + // farmlands + generateBox(level, chunkBB, 1, 0, 1, 2, 0, 7, Tile::farmland_Id, Tile::farmland_Id, false); + generateBox(level, chunkBB, 4, 0, 1, 5, 0, 7, Tile::farmland_Id, Tile::farmland_Id, false); + // walkpaths + generateBox(level, chunkBB, 0, 0, 0, 0, 0, 8, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 6, 0, 0, 6, 0, 8, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 1, 0, 0, 5, 0, 0, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 1, 0, 8, 5, 0, 8, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + // water + generateBox(level, chunkBB, 3, 0, 1, 3, 0, 7, Tile::water_Id, Tile::water_Id, false); + // crops + for (int d = 1; d <= 7; d++) + { + placeBlock(level, cropsA, Mth::nextInt(random, 2, 7), 1, 1, d, chunkBB); + placeBlock(level, cropsA, Mth::nextInt(random, 2, 7), 2, 1, d, chunkBB); + placeBlock(level, cropsB, Mth::nextInt(random, 2, 7), 4, 1, d, chunkBB); + placeBlock(level, cropsB, Mth::nextInt(random, 2, 7), 5, 1, d, chunkBB); + } + + for (int z = 0; z < depth; z++) + { + for (int x = 0; x < width; x++) + { + generateAirColumnUp(level, x, height, z, chunkBB); + fillColumnDown(level, Tile::dirt_Id, 0, x, -1, z, chunkBB); + } + } + + return true; + +} + +VillagePieces::DoubleFarmland::DoubleFarmland(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction) : VillagePiece(startPiece, genDepth) +{ + heightPosition = -1; // 4J added initialiser + orientation = direction; + boundingBox = stairsBox; + + cropsA = selectCrops(random); + cropsB = selectCrops(random); + cropsC = selectCrops(random); + cropsD = selectCrops(random); +} + +int VillagePieces::DoubleFarmland::selectCrops(Random *random) +{ + switch (random->nextInt(5)) + { + default: + return Tile::crops_Id; + case 0: + return Tile::carrots_Id; + case 1: + return Tile::potatoes_Id; + } +} + +VillagePieces::DoubleFarmland *VillagePieces::DoubleFarmland::createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, 0, 0, 0, width, height, depth, direction); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return new DoubleFarmland(startPiece, genDepth, random, box, direction); +} + +bool VillagePieces::DoubleFarmland::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (heightPosition < 0) + { + heightPosition = getAverageGroundHeight(level, chunkBB); + if (heightPosition < 0) + { + return true; + } + boundingBox->move(0, heightPosition - boundingBox->y1 + height - 1, 0); + } + + // fill inside with air + generateBox(level, chunkBB, 0, 1, 0, 12, 4, 8, 0, 0, false); + + // farmlands + generateBox(level, chunkBB, 1, 0, 1, 2, 0, 7, Tile::farmland_Id, Tile::farmland_Id, false); + generateBox(level, chunkBB, 4, 0, 1, 5, 0, 7, Tile::farmland_Id, Tile::farmland_Id, false); + generateBox(level, chunkBB, 7, 0, 1, 8, 0, 7, Tile::farmland_Id, Tile::farmland_Id, false); + generateBox(level, chunkBB, 10, 0, 1, 11, 0, 7, Tile::farmland_Id, Tile::farmland_Id, false); + // walkpaths + generateBox(level, chunkBB, 0, 0, 0, 0, 0, 8, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 6, 0, 0, 6, 0, 8, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 12, 0, 0, 12, 0, 8, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 1, 0, 0, 11, 0, 0, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + generateBox(level, chunkBB, 1, 0, 8, 11, 0, 8, Tile::treeTrunk_Id, Tile::treeTrunk_Id, false); + // water + generateBox(level, chunkBB, 3, 0, 1, 3, 0, 7, Tile::water_Id, Tile::water_Id, false); + generateBox(level, chunkBB, 9, 0, 1, 9, 0, 7, Tile::water_Id, Tile::water_Id, false); + // crops + for (int d = 1; d <= 7; d++) + { + placeBlock(level, cropsA, Mth::nextInt(random, 2, 7), 1, 1, d, chunkBB); + placeBlock(level, cropsA, Mth::nextInt(random, 2, 7), 2, 1, d, chunkBB); + placeBlock(level, cropsB, Mth::nextInt(random, 2, 7), 4, 1, d, chunkBB); + placeBlock(level, cropsB, Mth::nextInt(random, 2, 7), 5, 1, d, chunkBB); + placeBlock(level, cropsC, Mth::nextInt(random, 2, 7), 7, 1, d, chunkBB); + placeBlock(level, cropsC, Mth::nextInt(random, 2, 7), 8, 1, d, chunkBB); + placeBlock(level, cropsD, Mth::nextInt(random, 2, 7), 10, 1, d, chunkBB); + placeBlock(level, cropsD, Mth::nextInt(random, 2, 7), 11, 1, d, chunkBB); + } + + for (int z = 0; z < depth; z++) + { + for (int x = 0; x < width; x++) + { + generateAirColumnUp(level, x, height, z, chunkBB); + fillColumnDown(level, Tile::dirt_Id, 0, x, -1, z, chunkBB); + } + } + + + return true; + +} + +VillagePieces::LightPost::LightPost(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *box, int direction) : VillagePiece(startPiece, genDepth) +{ + heightPosition = -1; // 4J - added initialiser + orientation = direction; + boundingBox = box; +} + +BoundingBox *VillagePieces::LightPost::findPieceBox(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction) +{ + BoundingBox *box = BoundingBox::orientBox(footX, footY, footZ, 0, 0, 0, width, height, depth, direction); + + if (!isOkBox(box, startPiece) || StructurePiece::findCollisionPiece(pieces, box) != NULL) + { + delete box; + return NULL; + } + + return box; +} + +bool VillagePieces::LightPost::postProcess(Level *level, Random *random, BoundingBox *chunkBB) +{ + if (heightPosition < 0) + { + heightPosition = getAverageGroundHeight(level, chunkBB); + if (heightPosition < 0) + { + return true; + } + boundingBox->move(0, heightPosition - boundingBox->y1 + height - 1, 0); + } + + // fill with air + generateBox(level, chunkBB, 0, 0, 0, 2, 3, 1, 0, 0, false); + + // pillar + placeBlock(level, Tile::fence_Id, 0, 1, 0, 0, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 1, 1, 0, chunkBB); + placeBlock(level, Tile::fence_Id, 0, 1, 2, 0, chunkBB); + + // head + placeBlock(level, Tile::cloth_Id, DyePowderItem::WHITE, 1, 3, 0, chunkBB); + + // torches + placeBlock(level, Tile::torch_Id, 0, 0, 3, 0, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 1, 3, 1, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 2, 3, 0, chunkBB); + placeBlock(level, Tile::torch_Id, 0, 1, 3, -1, chunkBB); + + return true; +} diff --git a/Minecraft.World/VillagePieces.h b/Minecraft.World/VillagePieces.h new file mode 100644 index 00000000..a80dfcf7 --- /dev/null +++ b/Minecraft.World/VillagePieces.h @@ -0,0 +1,353 @@ +#pragma once +#include "StructurePiece.h" + +class BiomeSource; + +class VillagePieces +{ + +private: + static const int MAX_DEPTH = 50; + static const int BASE_ROAD_DEPTH = 3; + // the dungeon starts at 64 and traverses downwards to this point + static const int LOWEST_Y_POSITION = 10; + +public: + static const int SIZE_SMALL = 0; + static const int SIZE_BIG = 1; + static const int SIZE_BIGGEST = 2; + + // 4J - added to replace use of Class within this class + enum EPieceClass + { + EPieceClass_SimpleHouse, + EPieceClass_SmallTemple, + EPieceClass_BookHouse, + EPieceClass_SmallHut, + EPieceClass_PigHouse, + EPieceClass_DoubleFarmland, + EPieceClass_Farmland, + EPieceClass_Smithy, + EPieceClass_TwoRoomHouse + }; + + class PieceWeight { + public: + EPieceClass pieceClass; // 4J - EPieceClass was Class + const int weight; + int placeCount; + int maxPlaceCount; + + PieceWeight(EPieceClass pieceClass, int weight, int maxPlaceCount); // 4J - EPieceClass was Class + bool doPlace(int depth); + bool isValid(); + }; + + static list *createPieceSet(Random *random, int villageSize); // 4J - was ArrayList + + class StartPiece; +private: + class VillagePiece; + static int updatePieceWeight(list *currentPieces); // 4J = was array list + static VillagePiece *findAndCreatePieceFactory(StartPiece *startPiece, PieceWeight *piece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth); + static VillagePiece *generatePieceFromSmallDoor(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth); + static StructurePiece *generateAndAddPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth); + static StructurePiece *generateAndAddRoadPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int depth); + + + /** + * + * + */ +private: + class VillagePiece : public StructurePiece + { + private: + int spawnedVillagerCount; + protected: + StartPiece *startPiece; + + VillagePiece(StartPiece *startPiece, int genDepth); + StructurePiece *generateHouseNorthernLeft(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff); + StructurePiece *generateHouseNorthernRight(StartPiece *startPiece, list *pieces, Random *random, int yOff, int zOff); + int getAverageGroundHeight(Level *level, BoundingBox *chunkBB); + static bool isOkBox(BoundingBox *box, StartPiece *startRoom); // 4J added startRoom param + void spawnVillagers(Level *level, BoundingBox *chunkBB, int x, int y, int z, int count); + virtual int getVillagerProfession(int villagerNumber); + virtual int biomeBlock(int tile, int data); + virtual int biomeData(int tile, int data); + virtual void placeBlock(Level *level, int block, int data, int x, int y, int z, BoundingBox *chunkBB); + virtual void generateBox(Level *level, BoundingBox *chunkBB, int x0, int y0, int z0, int x1, int y1, int z1, int edgeTile, int fillTile, bool skipAir); + virtual void fillColumnDown(Level *level, int block, int data, int x, int startY, int z, BoundingBox *chunkBB); + }; + + /** + * + * + */ +public: + class Well : public VillagePiece + { + private: + static const int width = 6; + static const int height = 15; + static const int depth = 6; + + const bool isSource; + int heightPosition; + + public: + Well(StartPiece *startPiece, int genDepth, Random *random, int west, int north); + + Well(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + //static Well *createPiece(list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + +public: + class StartPiece : public Well + { + public: + BiomeSource *biomeSource; + bool isDesertVillage; + + int villageSize; + bool isLibraryAdded; + PieceWeight *previousPiece; + list *pieceSet; // 4J - was ArrayList + Level *m_level; + + // these queues are used so that the addChildren calls are + // called in a random order + vector pendingHouses; // 4J - was ArrayList + vector pendingRoads; // 4J - was ArrayList + + StartPiece(BiomeSource *biomeSource, int genDepth, Random *random, int west, int north, list *pieceSet, int villageSize, Level *level); // 4J Added level param + virtual ~StartPiece(); + + BiomeSource *getBiomeSource(); + + }; + +public: + class VillageRoadPiece : public VillagePiece + { + protected : + VillageRoadPiece(StartPiece *startPiece, int genDepth) : VillagePiece(startPiece, genDepth) {} + }; + + /** + * + * + */ +public: + class StraightRoad : public VillageRoadPiece + { + + private: + static const int width = 3; + int length; + public: + StraightRoad(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction); + virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); + static BoundingBox *findPieceBox(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + + /** + * + * + */ +public: + class SimpleHouse : public VillagePiece + { + private: + static const int width = 5; + static const int height = 6; + static const int depth = 5; + + private: + int heightPosition; + const bool hasTerrace; + + public: + SimpleHouse(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction); + public: + static SimpleHouse *createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + +public: + class SmallTemple : public VillagePiece + { + private: + static const int width = 5; + static const int height = 12; + static const int depth = 9; + + int heightPosition; + + public: + SmallTemple(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction); + + static SmallTemple *createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + virtual int getVillagerProfession(int villagerNumber); + }; + +public: + class BookHouse : public VillagePiece + { + private: + static const int width = 9; + static const int height = 9; + static const int depth = 6; + + int heightPosition; + + public: + BookHouse(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction); + + static BookHouse *createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + virtual int getVillagerProfession(int villagerNumber); + }; + + public: + class SmallHut : public VillagePiece + { + + private: + static const int width = 4; + static const int height = 6; + static const int depth = 5; + + int heightPosition; + const bool lowCeiling; + const int tablePlacement; + + public: + SmallHut(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction); + static SmallHut *createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + +public: + class PigHouse : public VillagePiece + { + + private: + static const int width = 9; + static const int height = 7; + static const int depth = 11; + + int heightPosition; + + public: + PigHouse(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction); + static PigHouse *createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + virtual int getVillagerProfession(int villagerNumber); + }; + +public: + class TwoRoomHouse : public VillagePiece + { + private: + static const int width = 9; + static const int height = 7; + static const int depth = 12; + + int heightPosition; + + public: + TwoRoomHouse(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction); + static TwoRoomHouse *createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + +public: + class Smithy : public VillagePiece + { + + private: + static const int width = 10; + static const int height = 6; + static const int depth = 7; + + int heightPosition; + bool hasPlacedChest; + + static WeighedTreasureArray treasureItems; + + public: + static void staticCtor(); + + Smithy(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction); + static Smithy *createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + virtual int getVillagerProfession(int villagerNumber); + }; + +public: + class Farmland : public VillagePiece + { + + private: + static const int width = 7; + static const int height = 4; + static const int depth = 9; + + int heightPosition; + + int cropsA; + int cropsB; + + int selectCrops(Random *random); + + public: + Farmland(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction); + static Farmland *createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + +public: + class DoubleFarmland : public VillagePiece + { + private: + static const int width = 13; + static const int height = 4; + static const int depth = 9; + + int heightPosition; + + int cropsA; + int cropsB; + int cropsC; + int cropsD; + + int selectCrops(Random *random); + + public: + DoubleFarmland(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *stairsBox, int direction); + static DoubleFarmland *createPiece(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction, int genDepth); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; + +public: + class LightPost : public VillagePiece + { + private: + static const int width = 3; + static const int height = 4; + static const int depth = 2; + + int heightPosition; + + public: + LightPost(StartPiece *startPiece, int genDepth, Random *random, BoundingBox *box, int direction); + static BoundingBox *findPieceBox(StartPiece *startPiece, list *pieces, Random *random, int footX, int footY, int footZ, int direction); + virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); + }; +}; diff --git a/Minecraft.World/VillageSiege.cpp b/Minecraft.World/VillageSiege.cpp new file mode 100644 index 00000000..55b2a3e8 --- /dev/null +++ b/Minecraft.World/VillageSiege.cpp @@ -0,0 +1,169 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.village.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.level.h" +#include "VillageSiege.h" + +VillageSiege::VillageSiege(Level *level) +{ + hasSetupSiege = false; + siegeState = SIEGE_NOT_INITED; + siegeCount = 0; + nextSpawnTime = 0; + village = weak_ptr(); + spawnX = spawnY = spawnZ = 0; + + this->level = level; +} + +void VillageSiege::tick() +{ + bool debug = false; + if (debug) + { + if (siegeState == SIEGE_DONE) + { + siegeCount = 100; + return; + } + // return; + } + else + { + if (level->isDay()) + { + siegeState = SIEGE_CAN_ACTIVATE; + return; + } + + if (siegeState == SIEGE_DONE) return; + + if (siegeState == SIEGE_CAN_ACTIVATE) + { + float timeOfDay = level->getTimeOfDay(0); + if (timeOfDay < 0.50 || timeOfDay > 0.501) return; + siegeState = level->random->nextInt(10) == 0 ? SIEGE_TONIGHT : SIEGE_DONE; + hasSetupSiege = false; + if (siegeState == SIEGE_DONE) return; + } + } + + if (!hasSetupSiege) + { + if (tryToSetupSiege()) hasSetupSiege = true; + else return; + } + + // Siege! + if (nextSpawnTime > 0) + { + --nextSpawnTime; + return; + } + + nextSpawnTime = 2; // 50 + level.random.nextInt(100); + if (siegeCount > 0) + { + trySpawn(); + --siegeCount; + } + else + { + siegeState = SIEGE_DONE; + } + +} + +bool VillageSiege::tryToSetupSiege() +{ + vector > *players = &level->players; + //for (Player player : players) + for(AUTO_VAR(it, players->begin()); it != players->end(); ++it) + { + shared_ptr player = *it; + shared_ptr _village = level->villages->getClosestVillage((int) player->x, (int) player->y, (int) player->z, 1); + village = _village; + + if (_village == NULL) continue; + if (_village->getDoorCount() < 10) continue; + if (_village->getStableAge() < 20) continue; + if (_village->getPopulationSize() < 20) continue; + + // setup siege origin + Pos *center = _village->getCenter(); + float radius = _village->getRadius(); + + bool overlaps = false; + for (int i = 0; i < 10; ++i) + { + spawnX = center->x + (int) (Mth::cos(level->random->nextFloat() * PI * 2.f) * radius * 0.9); + spawnY = center->y; + spawnZ = center->z + (int) (Mth::sin(level->random->nextFloat() * PI * 2.f) * radius * 0.9); + overlaps = false; + vector > *villages = level->villages->getVillages(); + //for (Village v : level.villages.getVillages()) + for(AUTO_VAR(itV, villages->begin()); itV != villages->end(); ++itV) + { + shared_ptrv = *itV; + if (v == _village) continue; + if (v->isInside(spawnX, spawnY, spawnZ)) + { + overlaps = true; + break; + } + } + if (!overlaps) break; + } + if (overlaps) return false; + + Vec3 *spawnPos = findRandomSpawnPos(spawnX, spawnY, spawnZ); + if (spawnPos == NULL) continue; + + nextSpawnTime = 0; + siegeCount = 20; + return true; + } + return false; +} + +bool VillageSiege::trySpawn() +{ + Vec3 *spawnPos = findRandomSpawnPos(spawnX, spawnY, spawnZ); + if (spawnPos == NULL) return false; + shared_ptr mob; + //try + { + mob = shared_ptr( new Zombie(level) ); + mob->finalizeMobSpawn(); + mob->setVillager(false); + } + //catch (Exception e) { + // e.printStackTrace(); + // return false; + //} + mob->moveTo(spawnPos->x, spawnPos->y, spawnPos->z, level->random->nextFloat() * 360, 0); + level->addEntity(mob); + shared_ptr _village = village.lock(); + if( _village == NULL ) return false; + + Pos *center = _village->getCenter(); + mob->restrictTo(center->x, center->y, center->z, _village->getRadius()); + return true; +} + +Vec3 *VillageSiege::findRandomSpawnPos(int x, int y, int z) +{ + shared_ptr _village = village.lock(); + if( _village == NULL ) return NULL; + + for (int i = 0; i < 10; ++i) + { + int xx = x + level->random->nextInt(16) - 8; + int yy = y + level->random->nextInt(6) - 3; + int zz = z + level->random->nextInt(16) - 8; + if (!_village->isInside(xx, yy, zz)) continue; + if (MobSpawner::isSpawnPositionOk(MobCategory::monster, level, xx, yy, zz)) return Vec3::newTemp(xx, yy, zz); + } + return NULL; +} \ No newline at end of file diff --git a/Minecraft.World/VillageSiege.h b/Minecraft.World/VillageSiege.h new file mode 100644 index 00000000..61c1982e --- /dev/null +++ b/Minecraft.World/VillageSiege.h @@ -0,0 +1,28 @@ +#pragma once + +class VillageSiege +{ +private: + Level *level; + bool hasSetupSiege; + int siegeState; + int siegeCount; + int nextSpawnTime; + weak_ptr village; + int spawnX, spawnY, spawnZ; + + static const int SIEGE_NOT_INITED = -1; + static const int SIEGE_CAN_ACTIVATE = 0; + static const int SIEGE_TONIGHT = 1; + static const int SIEGE_DONE = 2; + +public: + VillageSiege(Level *level); + + void tick(); + +private: + bool tryToSetupSiege(); + bool trySpawn(); + Vec3 *findRandomSpawnPos(int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/Villager.cpp b/Minecraft.World/Villager.cpp new file mode 100644 index 00000000..328c0c70 --- /dev/null +++ b/Minecraft.World/Villager.cpp @@ -0,0 +1,782 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.village.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.effect.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.item.enchantment.h" +#include "net.minecraft.world.item.trading.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.h" +#include "..\Minecraft.Client\Textures.h" +#include "Villager.h" + +unordered_map > Villager::MIN_MAX_VALUES; +unordered_map > Villager::MIN_MAX_PRICES; + +void Villager::_init(int profession) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + setProfession(profession); + setSize(.6f, 1.8f); + + runSpeed = 0.5f; + + villageUpdateInterval = 0; + inLove = false; + chasing = false; + village = weak_ptr(); + + tradingPlayer = weak_ptr(); + offers = NULL; + updateMerchantTimer = 0; + addRecipeOnUpdate = false; + riches = 0; + lastPlayerTradeName = L""; + rewardPlayersOnFirstVillage = false; + baseRecipeChanceMod = 0.0f; + + getNavigation()->setCanOpenDoors(true); + getNavigation()->setAvoidWater(true); + + goalSelector.addGoal(0, new FloatGoal(this)); + goalSelector.addGoal(1, new AvoidPlayerGoal(this, typeid(Zombie), 8, 0.3f, 0.35f)); + goalSelector.addGoal(1, new TradeWithPlayerGoal(this)); + goalSelector.addGoal(1, new LookAtTradingPlayerGoal(this)); + goalSelector.addGoal(2, new MoveIndoorsGoal(this)); + goalSelector.addGoal(3, new RestrictOpenDoorGoal(this)); + goalSelector.addGoal(4, new OpenDoorGoal(this, true)); + goalSelector.addGoal(5, new MoveTowardsRestrictionGoal(this, 0.3f)); + goalSelector.addGoal(6, new MakeLoveGoal(this)); + goalSelector.addGoal(7, new TakeFlowerGoal(this)); + goalSelector.addGoal(8, new PlayGoal(this, 0.32f)); + goalSelector.addGoal(9, new InteractGoal(this, typeid(Player), 3, 1.f)); + goalSelector.addGoal(9, new InteractGoal(this, typeid(Villager), 5, 0.02f)); + goalSelector.addGoal(9, new RandomStrollGoal(this, 0.3f)); + goalSelector.addGoal(10, new LookAtPlayerGoal(this, typeid(Mob), 8)); +} + +Villager::Villager(Level *level) : AgableMob(level) +{ + _init(0); +} + +Villager::Villager(Level *level, int profession) : AgableMob(level) +{ + _init(profession); +} + +Villager::~Villager() +{ + delete offers; +} + +bool Villager::useNewAi() +{ + return true; +} + +void Villager::serverAiMobStep() +{ + if (--villageUpdateInterval <= 0) + { + level->villages->queryUpdateAround(Mth::floor(x), Mth::floor(y), Mth::floor(z)); + villageUpdateInterval = 70 + random->nextInt(50); + + shared_ptr _village = level->villages->getClosestVillage(Mth::floor(x), Mth::floor(y), Mth::floor(z), Villages::MaxDoorDist); + village = _village; + if (_village == NULL) clearRestriction(); + else + { + Pos *center = _village->getCenter(); + restrictTo(center->x, center->y, center->z, (int)((float)_village->getRadius() * 0.6f)); + if (rewardPlayersOnFirstVillage) + { + rewardPlayersOnFirstVillage = false; + _village->rewardAllPlayers(5); + } + } + } + + if (!isTrading() && updateMerchantTimer > 0) + { + updateMerchantTimer--; + if (updateMerchantTimer <= 0) + { + if (addRecipeOnUpdate) + { + // improve max uses for all obsolete recipes + if (offers->size() > 0) + { + //for (MerchantRecipe recipe : offers) + for(AUTO_VAR(it, offers->begin()); it != offers->end(); ++it) + { + MerchantRecipe *recipe = *it; + if (recipe->isDeprecated()) + { + recipe->increaseMaxUses(random->nextInt(6) + random->nextInt(6) + 2); + } + } + } + addOffers(1); + addRecipeOnUpdate = false; + + if (village.lock() != NULL && !lastPlayerTradeName.empty()) + { + level->broadcastEntityEvent(shared_from_this(), EntityEvent::VILLAGER_HAPPY); + village.lock()->modifyStanding(lastPlayerTradeName, 1); + } + } + addEffect(new MobEffectInstance(MobEffect::regeneration->id, SharedConstants::TICKS_PER_SECOND * 10, 0)); + } + } + + AgableMob::serverAiMobStep(); +} + +bool Villager::interact(shared_ptr player) +{ + // [EB]: Truly dislike this code but I don't see another easy way + shared_ptr item = player->inventory->getSelected(); + bool holdingSpawnEgg = item != NULL && item->id == Item::monsterPlacer_Id; + + if (!holdingSpawnEgg && isAlive() && !isTrading() && !isBaby()) + { + if (!level->isClientSide) + { + // note: stop() logic is controlled by trading ai goal + setTradingPlayer(player); + player->openTrading(dynamic_pointer_cast(shared_from_this())); + } + return true; + } + return AgableMob::interact(player); +} + +void Villager::defineSynchedData() +{ + AgableMob::defineSynchedData(); + entityData->define(DATA_PROFESSION_ID, 0); +} + +int Villager::getMaxHealth() +{ + return 20; +} + +void Villager::addAdditonalSaveData(CompoundTag *tag) +{ + AgableMob::addAdditonalSaveData(tag); + tag->putInt(L"Profession", getProfession()); + tag->putInt(L"Riches", riches); + if (offers != NULL) + { + tag->putCompound(L"Offers", offers->createTag()); + } +} + +void Villager::readAdditionalSaveData(CompoundTag *tag) +{ + AgableMob::readAdditionalSaveData(tag); + setProfession(tag->getInt(L"Profession")); + riches = tag->getInt(L"Riches"); + if (tag->contains(L"Offers")) + { + CompoundTag *compound = tag->getCompound(L"Offers"); + delete offers; + offers = new MerchantRecipeList(compound); + } +} + +int Villager::getTexture() +{ + // 4J Made switch + switch(getProfession()) + { + case PROFESSION_FARMER: + return TN_MOB_VILLAGER_FARMER; // 4J was "/mob/villager/farmer.png"; + break; + case PROFESSION_LIBRARIAN: + return TN_MOB_VILLAGER_LIBRARIAN; // 4J was "/mob/villager/librarian.png"; + break; + case PROFESSION_PRIEST: + return TN_MOB_VILLAGER_PRIEST; // 4J was "/mob/villager/priest.png"; + break; + case PROFESSION_SMITH: + return TN_MOB_VILLAGER_SMITH; // 4J was "/mob/villager/smith.png"; + break; + case PROFESSION_BUTCHER: + return TN_MOB_VILLAGER_BUTCHER; // 4J was "/mob/villager/butcher.png"; + break; + //default: + // return TN_MOB_VILLAGER_VILLAGER; // 4J was "/mob/villager/villager.png"; + // break; + } + + return AgableMob::getTexture(); +} + +bool Villager::removeWhenFarAway() +{ + return false; +} + +int Villager::getAmbientSound() +{ + if(isTrading()) + { + return eSoundType_MOB_VILLAGER_HAGGLE; + } + return eSoundType_MOB_VILLAGER_IDLE; +} + +int Villager::getHurtSound() +{ + return eSoundType_MOB_VILLAGER_HIT; +} + +int Villager::getDeathSound() +{ + return eSoundType_MOB_VILLAGER_DEATH; +} + +void Villager::setProfession(int profession) +{ + entityData->set(DATA_PROFESSION_ID, profession); +} + +int Villager::getProfession() +{ + return entityData->getInteger(DATA_PROFESSION_ID); +} + +bool Villager::isInLove() +{ + return inLove; +} + +void Villager::setInLove(bool inLove) +{ + this->inLove = inLove; +} + +void Villager::setChasing(bool chasing) +{ + this->chasing = chasing; +} + +bool Villager::isChasing() +{ + return chasing; +} + +void Villager::setLastHurtByMob(shared_ptr mob) +{ + AgableMob::setLastHurtByMob(mob); + shared_ptr _village = village.lock(); + if (_village != NULL && mob != NULL) + { + _village->addAggressor(mob); + + shared_ptr player = dynamic_pointer_cast(mob); + if (player) + { + int amount = -1; + if (isBaby()) + { + amount = -3; + } + _village->modifyStanding(player->getName(), amount); + if (isAlive()) + { + level->broadcastEntityEvent(shared_from_this(), EntityEvent::VILLAGER_ANGRY); + } + } + } +} + +void Villager::die(DamageSource *source) +{ + shared_ptr _village = village.lock(); + if (_village != NULL) + { + shared_ptr sourceEntity = source->getEntity(); + if (sourceEntity != NULL) + { + if ((sourceEntity->GetType() & eTYPE_PLAYER) == eTYPE_PLAYER) + { + shared_ptr player = dynamic_pointer_cast(sourceEntity); + _village->modifyStanding(player->getName(), -2); + } + else if ((sourceEntity->GetType() & eTYPE_ENEMY) == eTYPE_ENEMY) + { + _village->resetNoBreedTimer(); + } + } + else if (sourceEntity == NULL) + { + // if the villager was killed by the world (such as lava or falling), blame + // the nearest player by not reproducing for a while + shared_ptr nearestPlayer = level->getNearestPlayer(shared_from_this(), 16.0f); + if (nearestPlayer != NULL) + { + _village->resetNoBreedTimer(); + } + } + } + + AgableMob::die(source); +} + +void Villager::setTradingPlayer(shared_ptr player) +{ + tradingPlayer = weak_ptr(player); +} + +shared_ptr Villager::getTradingPlayer() +{ + return tradingPlayer.lock(); +} + +bool Villager::isTrading() +{ + return tradingPlayer.lock() != NULL; +} + +void Villager::notifyTrade(MerchantRecipe *activeRecipe) +{ + activeRecipe->increaseUses(); + ambientSoundTime = -getAmbientSoundInterval(); + playSound(eSoundType_MOB_VILLAGER_YES, getSoundVolume(), getVoicePitch()); + + // when the player buys the latest item, we improve the merchant a little while later + if (activeRecipe->isSame(offers->at(offers->size() - 1))) + { + updateMerchantTimer = SharedConstants::TICKS_PER_SECOND * 2; + addRecipeOnUpdate = true; + if (tradingPlayer.lock() != NULL) + { + lastPlayerTradeName = tradingPlayer.lock()->getName(); + } + else + { + lastPlayerTradeName = L""; + } + } + + if (activeRecipe->getBuyAItem()->id == Item::emerald_Id) + { + riches += activeRecipe->getBuyAItem()->count; + } +} + +void Villager::notifyTradeUpdated(shared_ptr item) +{ + if (!level->isClientSide && (ambientSoundTime > (-getAmbientSoundInterval() + SharedConstants::TICKS_PER_SECOND))) + { + ambientSoundTime = -getAmbientSoundInterval(); + if (item != NULL) + { + playSound(eSoundType_MOB_VILLAGER_YES, getSoundVolume(), getVoicePitch()); + } + else + { + playSound(eSoundType_MOB_VILLAGER_NO, getSoundVolume(), getVoicePitch()); + } + } +} + +MerchantRecipeList *Villager::getOffers(shared_ptr forPlayer) +{ + if (offers == NULL) + { + addOffers(1); + } + return offers; +} + +float Villager::getRecipeChance(float baseChance) +{ + float newChance = baseChance + baseRecipeChanceMod; + if (newChance > .9f) + { + return .9f - (newChance - .9f); + } + return newChance; +} + +void Villager::addOffers(int addCount) +{ + MerchantRecipeList *newOffers = new MerchantRecipeList(); + switch (getProfession()) + { + case PROFESSION_FARMER: + addItemForTradeIn(newOffers, Item::wheat_Id, random, getRecipeChance(.9f)); + addItemForTradeIn(newOffers, Tile::cloth_Id, random, getRecipeChance(.5f)); + addItemForTradeIn(newOffers, Item::chicken_raw_Id, random, getRecipeChance(.5f)); + addItemForTradeIn(newOffers, Item::fish_cooked_Id, random, getRecipeChance(.4f)); + addItemForPurchase(newOffers, Item::bread_Id, random, getRecipeChance(.9f)); + addItemForPurchase(newOffers, Item::melon_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::apple_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::cookie_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::shears_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::flintAndSteel_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::chicken_cooked_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::arrow_Id, random, getRecipeChance(.5f)); + if (random->nextFloat() < .5f) + { + newOffers->push_back(new MerchantRecipe(shared_ptr( new ItemInstance(Tile::gravel, 10) ), shared_ptr( new ItemInstance(Item::emerald) ), shared_ptr( new ItemInstance(Item::flint_Id, 2 + random->nextInt(2), 0)))); + } + break; + case PROFESSION_BUTCHER: + addItemForTradeIn(newOffers, Item::coal_Id, random, getRecipeChance(.7f)); + addItemForTradeIn(newOffers, Item::porkChop_raw_Id, random, getRecipeChance(.5f)); + addItemForTradeIn(newOffers, Item::beef_raw_Id, random, getRecipeChance(.5f)); + addItemForPurchase(newOffers, Item::saddle_Id, random, getRecipeChance(.1f)); + addItemForPurchase(newOffers, Item::chestplate_cloth_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::boots_cloth_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::helmet_cloth_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::leggings_cloth_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::porkChop_cooked_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::beef_cooked_Id, random, getRecipeChance(.3f)); + break; + case PROFESSION_SMITH: + addItemForTradeIn(newOffers, Item::coal_Id, random, getRecipeChance(.7f)); + addItemForTradeIn(newOffers, Item::ironIngot_Id, random, getRecipeChance(.5f)); + addItemForTradeIn(newOffers, Item::goldIngot_Id, random, getRecipeChance(.5f)); + addItemForTradeIn(newOffers, Item::diamond_Id, random, getRecipeChance(.5f)); + + addItemForPurchase(newOffers, Item::sword_iron_Id, random, getRecipeChance(.5f)); + addItemForPurchase(newOffers, Item::sword_diamond_Id, random, getRecipeChance(.5f)); + addItemForPurchase(newOffers, Item::hatchet_iron_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::hatchet_diamond_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::pickAxe_iron_Id, random, getRecipeChance(.5f)); + addItemForPurchase(newOffers, Item::pickAxe_diamond_Id, random, getRecipeChance(.5f)); + addItemForPurchase(newOffers, Item::shovel_iron_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::shovel_diamond_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::hoe_iron_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::hoe_diamond_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::boots_iron_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::boots_diamond_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::helmet_iron_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::helmet_diamond_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::chestplate_iron_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::chestplate_diamond_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::leggings_iron_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::leggings_diamond_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::boots_chain_Id, random, getRecipeChance(.1f)); + addItemForPurchase(newOffers, Item::helmet_chain_Id, random, getRecipeChance(.1f)); + addItemForPurchase(newOffers, Item::chestplate_chain_Id, random, getRecipeChance(.1f)); + addItemForPurchase(newOffers, Item::leggings_chain_Id, random, getRecipeChance(.1f)); + break; + case PROFESSION_LIBRARIAN: + addItemForTradeIn(newOffers, Item::paper_Id, random, getRecipeChance(.8f)); + addItemForTradeIn(newOffers, Item::book_Id, random, getRecipeChance(.8f)); + //addItemForTradeIn(newOffers, Item::writtenBook_Id, random, getRecipeChance(0.3f)); + addItemForPurchase(newOffers, Tile::bookshelf_Id, random, getRecipeChance(.8f)); + addItemForPurchase(newOffers, Tile::glass_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::compass_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::clock_Id, random, getRecipeChance(.2f)); + + if (random->nextFloat() < getRecipeChance(0.07f)) + { + Enchantment *enchantment = Enchantment::validEnchantments[random->nextInt(Enchantment::validEnchantments.size())]; + int level = Mth::nextInt(random, enchantment->getMinLevel(), enchantment->getMaxLevel()); + shared_ptr book = Item::enchantedBook->createForEnchantment(new EnchantmentInstance(enchantment, level)); + int cost = 2 + random->nextInt(5 + (level * 10)) + 3 * level; + + newOffers->push_back(new MerchantRecipe(shared_ptr(new ItemInstance(Item::book)), shared_ptr(new ItemInstance(Item::emerald, cost)), book)); + } + break; + case PROFESSION_PRIEST: + addItemForPurchase(newOffers, Item::eyeOfEnder_Id, random, getRecipeChance(.3f)); + addItemForPurchase(newOffers, Item::expBottle_Id, random, getRecipeChance(.2f)); + addItemForPurchase(newOffers, Item::redStone_Id, random, getRecipeChance(.4f)); + addItemForPurchase(newOffers, Tile::lightGem_Id, random, getRecipeChance(.3f)); + { + int enchantItems[] = { + Item::sword_iron_Id, Item::sword_diamond_Id, Item::chestplate_iron_Id, Item::chestplate_diamond_Id, Item::hatchet_iron_Id, Item::hatchet_diamond_Id, Item::pickAxe_iron_Id, + Item::pickAxe_diamond_Id + }; + for (unsigned int i = 0; i < 8; ++i) + { + int id = enchantItems[i]; + if (random->nextFloat() < getRecipeChance(.05f)) + { + newOffers->push_back(new MerchantRecipe(shared_ptr(new ItemInstance(id, 1, 0)), + shared_ptr(new ItemInstance(Item::emerald, 2 + random->nextInt(3), 0)), + EnchantmentHelper::enchantItem(random, shared_ptr(new ItemInstance(id, 1, 0)), 5 + random->nextInt(15)))); + } + } + } + break; + } + + if (newOffers->empty()) + { + addItemForTradeIn(newOffers, Item::goldIngot_Id, random, 1.0f); + } + + // shuffle the list to make it more interesting + std::random_shuffle(newOffers->begin(), newOffers->end()); + + if (offers == NULL) + { + offers = new MerchantRecipeList(); + } + for (int i = 0; i < addCount && i < newOffers->size(); i++) + { + if( offers->addIfNewOrBetter(newOffers->at(i))) + { + // 4J Added so we can delete newOffers + newOffers->erase(newOffers->begin() + i); + } + } + delete newOffers; +} + +void Villager::overrideOffers(MerchantRecipeList *recipeList) +{ +} + + +void Villager::staticCtor() +{ + MIN_MAX_VALUES[Item::coal_Id] = pair(16, 24); + MIN_MAX_VALUES[Item::ironIngot_Id] = pair(8, 10); + MIN_MAX_VALUES[Item::goldIngot_Id] = pair(8, 10); + MIN_MAX_VALUES[Item::diamond_Id] = pair(4, 6); + MIN_MAX_VALUES[Item::paper_Id] = pair(24, 36); + MIN_MAX_VALUES[Item::book_Id] = pair(11, 13); + //MIN_MAX_VALUES.insert(Item::writtenBook_Id, pair(1, 1)); + MIN_MAX_VALUES[Item::enderPearl_Id] = pair(3, 4); + MIN_MAX_VALUES[Item::eyeOfEnder_Id] = pair(2, 3); + MIN_MAX_VALUES[Item::porkChop_raw_Id] = pair(14, 18); + MIN_MAX_VALUES[Item::beef_raw_Id] = pair(14, 18); + MIN_MAX_VALUES[Item::chicken_raw_Id] = pair(14, 18); + MIN_MAX_VALUES[Item::fish_cooked_Id] = pair(9, 13); + MIN_MAX_VALUES[Item::seeds_wheat_Id] = pair(34, 48); + MIN_MAX_VALUES[Item::seeds_melon_Id] = pair(30, 38); + MIN_MAX_VALUES[Item::seeds_pumpkin_Id] = pair(30, 38); + MIN_MAX_VALUES[Item::wheat_Id] = pair(18, 22); + MIN_MAX_VALUES[Tile::cloth_Id] = pair(14, 22); + MIN_MAX_VALUES[Item::rotten_flesh_Id] = pair(36, 64); + + MIN_MAX_PRICES[Item::flintAndSteel_Id] = pair(3, 4); + MIN_MAX_PRICES[Item::shears_Id] = pair(3, 4); + MIN_MAX_PRICES[Item::sword_iron_Id] = pair(7, 11); + MIN_MAX_PRICES[Item::sword_diamond_Id] = pair(12, 14); + MIN_MAX_PRICES[Item::hatchet_iron_Id] = pair(6, 8); + MIN_MAX_PRICES[Item::hatchet_diamond_Id] = pair(9, 12); + MIN_MAX_PRICES[Item::pickAxe_iron_Id] = pair(7, 9); + MIN_MAX_PRICES[Item::pickAxe_diamond_Id] = pair(10, 12); + MIN_MAX_PRICES[Item::shovel_iron_Id] = pair(4, 6); + MIN_MAX_PRICES[Item::shovel_diamond_Id] = pair(7, 8); + MIN_MAX_PRICES[Item::hoe_iron_Id] = pair(4, 6); + MIN_MAX_PRICES[Item::hoe_diamond_Id] = pair(7, 8); + MIN_MAX_PRICES[Item::boots_iron_Id] = pair(4, 6); + MIN_MAX_PRICES[Item::boots_diamond_Id] = pair(7, 8); + MIN_MAX_PRICES[Item::helmet_iron_Id] = pair(4, 6); + MIN_MAX_PRICES[Item::helmet_diamond_Id] = pair(7, 8); + MIN_MAX_PRICES[Item::chestplate_iron_Id] = pair(10, 14); + MIN_MAX_PRICES[Item::chestplate_diamond_Id] = pair(16, 19); + MIN_MAX_PRICES[Item::leggings_iron_Id] = pair(8, 10); + MIN_MAX_PRICES[Item::leggings_diamond_Id] = pair(11, 14); + MIN_MAX_PRICES[Item::boots_chain_Id] = pair(5, 7); + MIN_MAX_PRICES[Item::helmet_chain_Id] = pair(5, 7); + MIN_MAX_PRICES[Item::chestplate_chain_Id] = pair(11, 15); + MIN_MAX_PRICES[Item::leggings_chain_Id] = pair(9, 11); + MIN_MAX_PRICES[Item::bread_Id] = pair(-4, -2); + MIN_MAX_PRICES[Item::melon_Id] = pair(-8, -4); + MIN_MAX_PRICES[Item::apple_Id] = pair(-8, -4); + MIN_MAX_PRICES[Item::cookie_Id] = pair(-10, -7); + MIN_MAX_PRICES[Tile::glass_Id] = pair(-5, -3); + MIN_MAX_PRICES[Tile::bookshelf_Id] = pair(3, 4); + MIN_MAX_PRICES[Item::chestplate_cloth_Id] = pair(4, 5); + MIN_MAX_PRICES[Item::boots_cloth_Id] = pair(2, 4); + MIN_MAX_PRICES[Item::helmet_cloth_Id] = pair(2, 4); + MIN_MAX_PRICES[Item::leggings_cloth_Id] = pair(2, 4); + MIN_MAX_PRICES[Item::saddle_Id] = pair(6, 8); + MIN_MAX_PRICES[Item::expBottle_Id] = pair(-4, -1); + MIN_MAX_PRICES[Item::redStone_Id] = pair(-4, -1); + MIN_MAX_PRICES[Item::compass_Id] = pair(10, 12); + MIN_MAX_PRICES[Item::clock_Id] = pair(10, 12); + MIN_MAX_PRICES[Tile::lightGem_Id] = pair(-3, -1); + MIN_MAX_PRICES[Item::porkChop_cooked_Id] = pair(-7, -5); + MIN_MAX_PRICES[Item::beef_cooked_Id] = pair(-7, -5); + MIN_MAX_PRICES[Item::chicken_cooked_Id] = pair(-8, -6); + MIN_MAX_PRICES[Item::eyeOfEnder_Id] = pair(7, 11); + MIN_MAX_PRICES[Item::arrow_Id] = pair(-12, -8); +} + +/** +* Adds a merchant recipe that trades items for a single ruby. +* +* @param list +* @param itemId +* @param random +* @param likelyHood +*/ +void Villager::addItemForTradeIn(MerchantRecipeList *list, int itemId, Random *random, float likelyHood) +{ + if (random->nextFloat() < likelyHood) + { + list->push_back(new MerchantRecipe(getItemTradeInValue(itemId, random), Item::emerald)); + } +} + +shared_ptr Villager::getItemTradeInValue(int itemId, Random *random) +{ + return shared_ptr(new ItemInstance(itemId, getTradeInValue(itemId, random), 0)); +} + +int Villager::getTradeInValue(int itemId, Random *random) +{ + AUTO_VAR(it,MIN_MAX_VALUES.find(itemId)); + if (it == MIN_MAX_VALUES.end()) + { + return 1; + } + pair minMax = it->second; + if (minMax.first >= minMax.second) + { + return minMax.first; + } + return minMax.first + random->nextInt(minMax.second - minMax.first); +} + +/** +* Adds a merchant recipe that trades rubies for an item. If the cost is +* negative, one ruby will give several of that item. +* +* @param list +* @param itemId +* @param random +* @param likelyHood +*/ +void Villager::addItemForPurchase(MerchantRecipeList *list, int itemId, Random *random, float likelyHood) +{ + if (random->nextFloat() < likelyHood) + { + int purchaseCost = getPurchaseCost(itemId, random); + shared_ptr rubyItem; + shared_ptr resultItem; + if (purchaseCost < 0) + { + rubyItem = shared_ptr( new ItemInstance(Item::emerald_Id, 1, 0) ); + resultItem = shared_ptr( new ItemInstance(itemId, -purchaseCost, 0) ); + } + else + { + rubyItem = shared_ptr( new ItemInstance(Item::emerald_Id, purchaseCost, 0) ); + resultItem = shared_ptr( new ItemInstance(itemId, 1, 0) ); + } + list->push_back(new MerchantRecipe(rubyItem, resultItem)); + } +} + +int Villager::getPurchaseCost(int itemId, Random *random) +{ + AUTO_VAR(it,MIN_MAX_PRICES.find(itemId)); + if (it == MIN_MAX_PRICES.end()) + { + return 1; + } + pair minMax = it->second; + if (minMax.first >= minMax.second) + { + return minMax.first; + } + return minMax.first + random->nextInt(minMax.second - minMax.first); +} + +void Villager::handleEntityEvent(byte id) +{ + if (id == EntityEvent::LOVE_HEARTS) + { + addParticlesAroundSelf(eParticleType_heart); + } + else if (id == EntityEvent::VILLAGER_ANGRY) + { + addParticlesAroundSelf(eParticleType_angryVillager); + } + else if (id == EntityEvent::VILLAGER_HAPPY) + { + addParticlesAroundSelf(eParticleType_happyVillager); + } + else + { + AgableMob::handleEntityEvent(id); + } +} + +void Villager::addParticlesAroundSelf(ePARTICLE_TYPE particle) +{ + for (int i = 0; i < 5; i++) + { + double xa = random->nextGaussian() * 0.02; + double ya = random->nextGaussian() * 0.02; + double za = random->nextGaussian() * 0.02; + level->addParticle(particle, x + random->nextFloat() * bbWidth * 2 - bbWidth, y + 1.0f + random->nextFloat() * bbHeight, z + random->nextFloat() * bbWidth * 2 - bbWidth, xa, ya, za); + } +} + +void Villager::finalizeMobSpawn() +{ + setProfession(level->random->nextInt(Villager::PROFESSION_MAX)); +} + +void Villager::setRewardPlayersInVillage() +{ + rewardPlayersOnFirstVillage = true; +} + +shared_ptr Villager::getBreedOffspring(shared_ptr target) +{ + // 4J - added limit to villagers that can be bred + if(level->canCreateMore(GetType(), Level::eSpawnType_Breed) ) + { + shared_ptr villager = shared_ptr(new Villager(level)); + villager->finalizeMobSpawn(); + return villager; + } + else + { + return nullptr; + } +} + +int Villager::getDisplayName() +{ + int name = IDS_VILLAGER; + switch(getProfession()) + { + case PROFESSION_FARMER: + name = IDS_VILLAGER_FARMER; + break; + case PROFESSION_LIBRARIAN: + name = IDS_VILLAGER_LIBRARIAN; + break; + case PROFESSION_PRIEST: + name = IDS_VILLAGER_PRIEST; + break; + case PROFESSION_SMITH: + name = IDS_VILLAGER_SMITH; + break; + case PROFESSION_BUTCHER: + name = IDS_VILLAGER_BUTCHER; + break; + }; + return name; +} diff --git a/Minecraft.World/Villager.h b/Minecraft.World/Villager.h new file mode 100644 index 00000000..b6ad7d1c --- /dev/null +++ b/Minecraft.World/Villager.h @@ -0,0 +1,149 @@ +#pragma once + +#include "AgableMob.h" +#include "Npc.h" +#include "Merchant.h" +#include "ParticleTypes.h" + +class Level; +class Village; +class MerchantRecipeList; +class MerchantRecipe; + +class Villager : public AgableMob, public Npc, public Merchant +{ +public: + eINSTANCEOF GetType() { return eTYPE_VILLAGER; } + static Entity *create(Level *level) { return new Villager(level); } + + //public static final String comment = "No, I won't 'fix' these! They're fine!! - Notch"; + +public: + static const int PROFESSION_FARMER = 0; + static const int PROFESSION_LIBRARIAN = 1; + static const int PROFESSION_PRIEST = 2; + static const int PROFESSION_SMITH = 3; + static const int PROFESSION_BUTCHER = 4; + static const int PROFESSION_MAX = 5; + +private: + static const int DATA_PROFESSION_ID = 16; + int villageUpdateInterval; + + bool inLove; + bool chasing; + weak_ptr village; + + weak_ptr tradingPlayer; + MerchantRecipeList *offers; + int updateMerchantTimer; + bool addRecipeOnUpdate; + int riches; + wstring lastPlayerTradeName; + + bool rewardPlayersOnFirstVillage; + +private: + + void _init(int profession); + +public: + Villager(Level *level); + Villager(Level *level, int profession); + ~Villager(); + + virtual bool useNewAi(); + +protected: + virtual void serverAiMobStep(); + +public: + virtual bool interact(shared_ptr player); + +protected: + virtual void defineSynchedData(); + +public: + virtual int getMaxHealth(); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + + virtual int getTexture(); + +protected: + virtual bool removeWhenFarAway(); + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + +public: + void setProfession(int profession); + int getProfession(); + bool isInLove(); + void setInLove(bool inLove); + void setChasing(bool chasing); + bool isChasing(); + void setLastHurtByMob(shared_ptr mob); + void die(DamageSource *source); + + void handleEntityEvent(byte id); + +private: + void addParticlesAroundSelf(ePARTICLE_TYPE particle); + +public: + void setTradingPlayer(shared_ptr player); + shared_ptr getTradingPlayer(); + bool isTrading(); + void notifyTrade(MerchantRecipe *activeRecipe); + void notifyTradeUpdated(shared_ptr item); + MerchantRecipeList *getOffers(shared_ptr forPlayer); + +private: + float baseRecipeChanceMod; + + float getRecipeChance(float baseChance); + void addOffers(int addCount); + +public: + void overrideOffers(MerchantRecipeList *recipeList); + +private: + static unordered_map > MIN_MAX_VALUES; + static unordered_map > MIN_MAX_PRICES; + +public: + static void staticCtor(); + +private: + /** + * Adds a merchant recipe that trades items for a single ruby. + * + * @param list + * @param itemId + * @param random + * @param likelyHood + */ + static void addItemForTradeIn(MerchantRecipeList *list, int itemId, Random *random, float likelyHood); + static shared_ptr getItemTradeInValue(int itemId, Random *random); + static int getTradeInValue(int itemId, Random *random); + + /** + * Adds a merchant recipe that trades rubies for an item. If the cost is + * negative, one ruby will give several of that item. + * + * @param list + * @param itemId + * @param random + * @param likelyHood + */ + static void addItemForPurchase(MerchantRecipeList *list, int itemId, Random *random, float likelyHood); + static int getPurchaseCost(int itemId, Random *random); + +public: + void finalizeMobSpawn(); + void setRewardPlayersInVillage(); + shared_ptr getBreedOffspring(shared_ptr target); + + virtual int getDisplayName(); +}; \ No newline at end of file diff --git a/Minecraft.World/VillagerGolem.cpp b/Minecraft.World/VillagerGolem.cpp new file mode 100644 index 00000000..a0f05472 --- /dev/null +++ b/Minecraft.World/VillagerGolem.cpp @@ -0,0 +1,240 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.control.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.ai.goal.target.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.ai.village.h" +#include "net.minecraft.world.entity.animal.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.monster.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "..\Minecraft.Client\Textures.h" +#include "SynchedEntityData.h" +#include "VillagerGolem.h" +#include "ParticleTypes.h" + +VillagerGolem::VillagerGolem(Level *level) : Golem(level) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + villageUpdateInterval = 0; + village = weak_ptr(); + attackAnimationTick = 0; + offerFlowerTick = 0; + + this->textureIdx = TN_MOB_VILLAGER_GOLEM; // "/mob/villager_golem.png"; + this->setSize(1.4f, 2.9f); + + getNavigation()->setAvoidWater(true); + + // 4J-JEV: These speed values are as they appear before 1.6.4 (1.5), + // as the movement speed system changes then. (Mob attributes added) + goalSelector.addGoal(1, new MeleeAttackGoal(this, 0.25f, true)); + goalSelector.addGoal(2, new MoveTowardsTargetGoal(this, 0.22f, 32)); + goalSelector.addGoal(3, new MoveThroughVillageGoal(this, 0.16f, true)); + goalSelector.addGoal(4, new MoveTowardsRestrictionGoal(this, 0.16f)); + goalSelector.addGoal(5, new OfferFlowerGoal(this)); + goalSelector.addGoal(6, new RandomStrollGoal(this, 0.16f)); + goalSelector.addGoal(7, new LookAtPlayerGoal(this, typeid(Player), 6)); + goalSelector.addGoal(8, new RandomLookAroundGoal(this)); + + targetSelector.addGoal(1, new DefendVillageTargetGoal(this)); + targetSelector.addGoal(2, new HurtByTargetGoal(this, false)); + targetSelector.addGoal(3, new NearestAttackableTargetGoal(this, typeid(Monster), 16, 0, false, true)); +} + +void VillagerGolem::defineSynchedData() +{ + Golem::defineSynchedData(); + entityData->define(DATA_FLAGS_ID, (byte) 0); +} + +bool VillagerGolem::useNewAi() +{ + return true; +} + +void VillagerGolem::serverAiMobStep() +{ + if (--villageUpdateInterval <= 0) + { + villageUpdateInterval = 70 + random->nextInt(50); + shared_ptr _village = level->villages->getClosestVillage(Mth::floor(x), Mth::floor(y), Mth::floor(z), Villages::MaxDoorDist); + village = _village; + if (_village == NULL) clearRestriction(); + else + { + Pos *center = _village->getCenter(); + restrictTo(center->x, center->y, center->z, (int)((float)_village->getRadius()) * 0.6f); + } + } + + Golem::serverAiMobStep(); +} + +int VillagerGolem::getMaxHealth() +{ + return 100; +} + +int VillagerGolem::decreaseAirSupply(int currentSupply) +{ + // infinite air supply + return currentSupply; +} + +void VillagerGolem::aiStep() +{ + Golem::aiStep(); + + if (attackAnimationTick > 0) --attackAnimationTick; + if (offerFlowerTick > 0) --offerFlowerTick; + + if (xd * xd + zd * zd > MoveControl::MIN_SPEED_SQR && random->nextInt(5) == 0) + { + int xt = Mth::floor(x); + int yt = Mth::floor(y - 0.2f - this->heightOffset); + int zt = Mth::floor(z); + int t = level->getTile(xt, yt, zt); + int d = level->getData(xt, yt, zt); + if (t > 0) + { + level->addParticle(PARTICLE_TILECRACK(t,d), x + (random->nextFloat() - 0.5) * bbWidth, bb->y0 + 0.1, z + (random->nextFloat() - 0.5) * bbWidth, 4 * (random->nextFloat() - 0.5), .5, + (random->nextFloat() - 0.5) * 4); + } + } +} + +bool VillagerGolem::canAttackType(eINSTANCEOF targetType) +{ + if (isPlayerCreated() && (eTYPE_PLAYER & targetType) == eTYPE_PLAYER ) return false; + return Golem::canAttackType(targetType); +} + +void VillagerGolem::addAdditonalSaveData(CompoundTag *tag) +{ + Golem::addAdditonalSaveData(tag); + tag->putBoolean(L"PlayerCreated", isPlayerCreated()); +} + +void VillagerGolem::readAdditionalSaveData(CompoundTag *tag) +{ + Golem::readAdditionalSaveData(tag); + setPlayerCreated(tag->getBoolean(L"PlayerCreated")); +} + +bool VillagerGolem::doHurtTarget(shared_ptr target) +{ + attackAnimationTick = 10; + level->broadcastEntityEvent(shared_from_this(), EntityEvent::START_ATTACKING); + bool hurt = target->hurt(DamageSource::mobAttack(dynamic_pointer_cast(shared_from_this())), 7 + random->nextInt(15)); + if (hurt) target->yd += 0.4f; + level->playSound(shared_from_this(), eSoundType_MOB_IRONGOLEM_THROW, 1, 1); + return hurt; +} + +void VillagerGolem::handleEntityEvent(byte id) +{ + if (id == EntityEvent::START_ATTACKING) + { + attackAnimationTick = 10; + level->playSound(shared_from_this(), eSoundType_MOB_IRONGOLEM_THROW, 1, 1); + } + else if (id == EntityEvent::OFFER_FLOWER) + { + offerFlowerTick = OfferFlowerGoal::OFFER_TICKS; + } + else Golem::handleEntityEvent(id); +} + +shared_ptr VillagerGolem::getVillage() +{ + return village.lock(); +} + +int VillagerGolem::getAttackAnimationTick() +{ + return attackAnimationTick; +} + +void VillagerGolem::offerFlower(bool offer) +{ + offerFlowerTick = offer ? OfferFlowerGoal::OFFER_TICKS : 0; + level->broadcastEntityEvent(shared_from_this(), EntityEvent::OFFER_FLOWER); +} + +int VillagerGolem::getAmbientSound() +{ + return -1; +} + +int VillagerGolem::getHurtSound() +{ + return eSoundType_MOB_IRONGOLEM_HIT; +} + +int VillagerGolem::getDeathSound() +{ + return eSoundType_MOB_IRONGOLEM_DEATH; +} + +void VillagerGolem::playStepSound(int xt, int yt, int zt, int t) +{ + level->playSound(shared_from_this(), eSoundType_MOB_IRONGOLEM_WALK, 1, 1); +} + +void VillagerGolem::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + int roses = random->nextInt(3); + for (int i = 0; i < roses; i++) + { + spawnAtLocation(Tile::rose_Id, 1); + } + int iron = 3 + random->nextInt(3); + for (int i = 0; i < iron; i++) + { + spawnAtLocation(Item::ironIngot_Id, 1); + } +} + +int VillagerGolem::getOfferFlowerTick() +{ + return offerFlowerTick; +} + +bool VillagerGolem::isPlayerCreated() +{ + return (entityData->getByte(DATA_FLAGS_ID) & 0x01) != 0; +} + +void VillagerGolem::setPlayerCreated(bool value) +{ + byte current = entityData->getByte(DATA_FLAGS_ID); + if (value) + { + entityData->set(DATA_FLAGS_ID, (byte) (current | 0x01)); + } + else + { + entityData->set(DATA_FLAGS_ID, (byte) (current & ~0x01)); + } +} + +void VillagerGolem::die(DamageSource *source) +{ + if (!isPlayerCreated() && lastHurtByPlayer != NULL && village.lock() != NULL) + { + village.lock()->modifyStanding(lastHurtByPlayer->getName(), -5); + } + Golem::die(source); +} \ No newline at end of file diff --git a/Minecraft.World/VillagerGolem.h b/Minecraft.World/VillagerGolem.h new file mode 100644 index 00000000..dfcd3ac0 --- /dev/null +++ b/Minecraft.World/VillagerGolem.h @@ -0,0 +1,64 @@ +#pragma once + +#include "Golem.h" + +class Village; +class Level; + +class VillagerGolem : public Golem +{ +public: + eINSTANCEOF GetType() { return eTYPE_VILLAGERGOLEM; } + static Entity *create(Level *level) { return new VillagerGolem(level); } + +protected: + static const int DATA_FLAGS_ID = 16; + +private: + int villageUpdateInterval; + weak_ptr village; + int attackAnimationTick; + int offerFlowerTick; + +public: + VillagerGolem(Level *level); + +protected: + virtual void defineSynchedData(); + +public: + virtual bool useNewAi(); + +protected: + virtual void serverAiMobStep(); + +public: + virtual int getMaxHealth(); + +protected: + virtual int decreaseAirSupply(int currentSupply); + +public: + virtual void aiStep(); + virtual bool canAttackType(eINSTANCEOF targetType); + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + virtual bool doHurtTarget(shared_ptr target); + virtual void handleEntityEvent(byte id); + virtual shared_ptr getVillage(); + virtual int getAttackAnimationTick(); + virtual void offerFlower(bool offer); + +protected: + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual void playStepSound(int xt, int yt, int zt, int t); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + +public: + virtual int getOfferFlowerTick(); + virtual bool isPlayerCreated(); + virtual void setPlayerCreated(bool value); + virtual void die(DamageSource *source); +}; \ No newline at end of file diff --git a/Minecraft.World/Villages.cpp b/Minecraft.World/Villages.cpp new file mode 100644 index 00000000..995befe8 --- /dev/null +++ b/Minecraft.World/Villages.cpp @@ -0,0 +1,253 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.ai.village.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "BasicTypeContainers.h" +#include "Villages.h" + +const wstring Villages::VILLAGE_FILE_ID = L"villages"; + +Villages::Villages(const wstring &id) : SavedData(id) +{ + _tick = 0; + level = NULL; +} + +Villages::Villages(Level *level) : SavedData(VILLAGE_FILE_ID) +{ + _tick = 0; + this->level = level; +} + +Villages::~Villages() +{ + for(AUTO_VAR(it,queries.begin()); it != queries.end(); ++it) delete *it; +} + +void Villages::setLevel(Level *level) +{ + this->level = level; + + //for (Village village : villages) + for(AUTO_VAR(it, villages.begin()); it != villages.end(); ++it) + { + shared_ptr village = *it; + village->setLevel(level); + } +} + +void Villages::queryUpdateAround(int x, int y, int z) +{ + if (queries.size() > 64) return; + if (!hasQuery(x, y, z)) queries.push_back(new Pos(x, y, z)); +} + +void Villages::tick() +{ + ++_tick; + //for (Village village : villages) + for(AUTO_VAR(it, villages.begin()); it != villages.end(); ++it) + { + shared_ptr village = *it; + village->tick(_tick); + } + removeVillages(); + processNextQuery(); + cluster(); + + if ((_tick % 400) == 0) + { + setDirty(); + } +} + +void Villages::removeVillages() +{ + //for (Iterator it = villages.iterator(); it.hasNext();) + for(AUTO_VAR(it, villages.begin()); it != villages.end(); ) + { + shared_ptr village = *it; //it.next(); + if (village->canRemove()) + { + it = villages.erase(it); + //it.remove(); + setDirty(); + } + else + { + ++it; + } + } +} + +vector > *Villages::getVillages() +{ + return &villages; +} + +shared_ptr Villages::getClosestVillage(int x, int y, int z, int maxDist) +{ + shared_ptr closest = nullptr; + float closestDistSqr = Float::MAX_VALUE; + //for (Village village : villages) + for(AUTO_VAR(it, villages.begin()); it != villages.end(); ++it) + { + shared_ptr village = *it; + float distSqr = village->getCenter()->distSqr(x, y, z); + if (distSqr >= closestDistSqr) continue; + + float requiredDist = maxDist + village->getRadius(); + if (distSqr > requiredDist * requiredDist) continue; + + closest = village; + closestDistSqr = distSqr; + } + return closest; +} + +void Villages::processNextQuery() +{ + if (queries.empty()) return; + Pos *q = queries.front(); + queries.pop_front(); + addDoorInfos(q); + delete q; +} + +void Villages::cluster() +{ + // note doesn't merge or split existing villages + //for (int i = 0; i < unclustered.size(); ++i) + for(AUTO_VAR(it, unclustered.begin()); it != unclustered.end(); ++it) + { + shared_ptr di = *it; //unclustered.get(i); + + bool found = false; + //for (Village village : villages) + for(AUTO_VAR(itV, villages.begin()); itV != villages.end(); ++itV) + { + shared_ptr village = *itV; + int dist = (int) village->getCenter()->distSqr(di->x, di->y, di->z); + int radius = MaxDoorDist + village->getRadius(); + if (dist > radius * radius) continue; + village->addDoorInfo(di); + found = true; + break; + } + if (found) continue; + + // create new Village + shared_ptr village = shared_ptr(new Village(level)); + village->addDoorInfo(di); + villages.push_back(village); + setDirty(); + } + unclustered.clear(); +} + +void Villages::addDoorInfos(Pos *pos) +{ + int scanX = 16, scanY = 4, scanZ = 16; + for (int xx = pos->x - scanX; xx < pos->x + scanX; xx++) + { + for (int yy = pos->y - scanY; yy < pos->y + scanY; yy++) + { + for (int zz = pos->z - scanZ; zz < pos->z + scanZ; zz++) + { + if (isDoor(xx, yy, zz)) + { + shared_ptr currentDoor = getDoorInfo(xx, yy, zz); + if (currentDoor == NULL) createDoorInfo(xx, yy, zz); + else currentDoor->timeStamp = _tick; + } + } + } + } +} + +shared_ptr Villages::getDoorInfo(int x, int y, int z) +{ + //for (DoorInfo di : unclustered) + for(AUTO_VAR(it,unclustered.begin()); it != unclustered.end(); ++it) + { + shared_ptr di = *it; + if (di->x == x && di->z == z && abs(di->y - y) <= 1) return di; + } + //for (Village v : villages) + for(AUTO_VAR(it,villages.begin()); it != villages.end(); ++it) + { + shared_ptr v = *it; + shared_ptr di = v->getDoorInfo(x, y, z); + if (di != NULL) return di; + } + return nullptr; +} + +void Villages::createDoorInfo(int x, int y, int z) +{ + int dir = ((DoorTile *) Tile::door_wood)->getDir(level, x, y, z); + if (dir == 0 || dir == 2) + { + int canSeeX = 0; + for (int i = -5; i < 0; ++i) + if (level->canSeeSky(x + i, y, z)) canSeeX--; + for (int i = 1; i <= 5; ++i) + if (level->canSeeSky(x + i, y, z)) canSeeX++; + if (canSeeX != 0) unclustered.push_back(shared_ptr(new DoorInfo(x, y, z, canSeeX > 0 ? -2 : 2, 0, _tick))); + } + else + { + int canSeeZ = 0; + for (int i = -5; i < 0; ++i) + if (level->canSeeSky(x, y, z + i)) canSeeZ--; + for (int i = 1; i <= 5; ++i) + if (level->canSeeSky(x, y, z + i)) canSeeZ++; + if (canSeeZ != 0) unclustered.push_back(shared_ptr(new DoorInfo(x, y, z, 0, canSeeZ > 0 ? -2 : 2, _tick))); + } +} + +bool Villages::hasQuery(int x, int y, int z) +{ + //for (Pos pos : queries) + for(AUTO_VAR(it, queries.begin()); it != queries.end(); ++it) + { + Pos *pos = *it; + if (pos->x == x && pos->y == y && pos->z == z) return true; + } + return false; +} + +bool Villages::isDoor(int x, int y, int z) +{ + int tileId = level->getTile(x, y, z); + return tileId == Tile::door_wood_Id; +} + +void Villages::load(CompoundTag *tag) +{ + _tick = tag->getInt(L"Tick"); + ListTag *villageTags = (ListTag *) tag->getList(L"Villages"); + for (int i = 0; i < villageTags->size(); i++) + { + CompoundTag *compoundTag = villageTags->get(i); + shared_ptr village = shared_ptr(new Village()); + village->readAdditionalSaveData(compoundTag); + villages.push_back(village); + } +} + +void Villages::save(CompoundTag *tag) +{ + tag->putInt(L"Tick", _tick); + ListTag *villageTags = new ListTag(L"Villages"); + //for (Village village : villages) + for(AUTO_VAR(it, villages.begin()); it != villages.end(); ++it) + { + shared_ptr village = *it; + CompoundTag *villageTag = new CompoundTag(L"Village"); + village->addAdditonalSaveData(villageTag); + villageTags->add(villageTag); + } + tag->put(L"Villages", villageTags); +} \ No newline at end of file diff --git a/Minecraft.World/Villages.h b/Minecraft.World/Villages.h new file mode 100644 index 00000000..030edc33 --- /dev/null +++ b/Minecraft.World/Villages.h @@ -0,0 +1,47 @@ +#pragma once + +#include "SavedData.h" + +class Villages : public SavedData +{ +public: + static const wstring VILLAGE_FILE_ID; + + static const int MaxDoorDist = 32; + +private: + Level *level; + deque queries; + vector > unclustered; + vector > villages; + int _tick; + +public: + Villages(const wstring &id); + Villages(Level *level); + ~Villages(); + + void setLevel(Level *level); + void queryUpdateAround(int x, int y, int z); + void tick(); + +private: + void removeVillages(); + +public: + vector > *getVillages(); + shared_ptr getClosestVillage(int x, int y, int z, int maxDist); + +private: + void processNextQuery(); + void cluster(); + void addDoorInfos(Pos *pos); + shared_ptrgetDoorInfo(int x, int y, int z); + void createDoorInfo(int x, int y, int z); + bool hasQuery(int x, int y, int z); + bool isDoor(int x, int y, int z); + +public: + void load(CompoundTag *tag); + void save(CompoundTag *tag); +}; \ No newline at end of file diff --git a/Minecraft.World/VineTile.cpp b/Minecraft.World/VineTile.cpp new file mode 100644 index 00000000..1170caf2 --- /dev/null +++ b/Minecraft.World/VineTile.cpp @@ -0,0 +1,387 @@ +#include "stdafx.h" +#include "VineTile.h" +#include "Material.h" +#include "JavaMath.h" +#include "Facing.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.level.biome.h" + +VineTile::VineTile(int id) : Tile(id, Material::replaceable_plant, isSolidRender() ) +{ + setTicking(true); +} + +void VineTile::updateDefaultShape() +{ + setShape(0, 0, 0, 1, 1, 1); +} + +int VineTile::getRenderShape() +{ + return SHAPE_VINE; +} + +bool VineTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool VineTile::isCubeShaped() +{ + return false; +} + +void VineTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) // 4J added forceData, forceEntity param +{ + const float thickness = 1.0f / 16.0f; + + int facings = level->getData(x, y, z); + + float minX = 1; + float minY = 1; + float minZ = 1; + float maxX = 0; + float maxY = 0; + float maxZ = 0; + bool hasWall = facings > 0; + + if ((facings & VINE_WEST) != 0) + { + maxX = Math::_max(maxX, thickness); + minX = 0; + minY = 0; + maxY = 1; + minZ = 0; + maxZ = 1; + hasWall = true; + } + if ((facings & VINE_EAST) != 0) + { + minX = Math::_min(minX, 1 - thickness); + maxX = 1; + minY = 0; + maxY = 1; + minZ = 0; + maxZ = 1; + hasWall = true; + } + if ((facings & VINE_NORTH) != 0) + { + maxZ = Math::_max(maxZ, thickness); + minZ = 0; + minX = 0; + maxX = 1; + minY = 0; + maxY = 1; + hasWall = true; + } + if ((facings & VINE_SOUTH) != 0) + { + minZ = Math::_min(minZ, 1 - thickness); + maxZ = 1; + minX = 0; + maxX = 1; + minY = 0; + maxY = 1; + hasWall = true; + } + if (!hasWall && isAcceptableNeighbor(level->getTile(x, y + 1, z))) + { + minY = Math::_min(minY, 1 - thickness); + maxY = 1; + minX = 0; + maxX = 1; + minZ = 0; + maxZ = 1; + } + setShape(minX, minY, minZ, maxX, maxY, maxZ); + +} + +AABB *VineTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + +bool VineTile::mayPlace(Level *level, int x, int y, int z, int face) +{ + switch (face) + { + default: + return false; + case Facing::UP: + return isAcceptableNeighbor(level->getTile(x, y + 1, z)); + case Facing::NORTH: + return isAcceptableNeighbor(level->getTile(x, y, z + 1)); + case Facing::SOUTH: + return isAcceptableNeighbor(level->getTile(x, y, z - 1)); + case Facing::EAST: + return isAcceptableNeighbor(level->getTile(x - 1, y, z)); + case Facing::WEST: + return isAcceptableNeighbor(level->getTile(x + 1, y, z)); + } +} + +bool VineTile::isAcceptableNeighbor(int id) +{ + if (id == 0) return false; + Tile *tile = Tile::tiles[id]; + if (tile->isCubeShaped() && tile->material->blocksMotion()) return true; + return false; +} + +bool VineTile::updateSurvival(Level *level, int x, int y, int z) +{ + int facings = level->getData(x, y, z); + int newFacings = facings; + + if (newFacings > 0) + { + for (int d = 0; d <= 3; d++) + { + int facing = 1 << d; + if ((facings & facing) != 0) + { + if (!isAcceptableNeighbor(level->getTile(x + Direction::STEP_X[d], y, z + Direction::STEP_Z[d]))) + { + // no attachment in this direction, + // verify that there is vines hanging above + if (level->getTile(x, y + 1, z) != id || (level->getData(x, y + 1, z) & facing) == 0) + { + newFacings &= ~facing; + } + } + } + } + } + + if (newFacings == 0) + { + // the block will die unless it has a roof + if (!isAcceptableNeighbor(level->getTile(x, y + 1, z))) + { + return false; + } + } + if (newFacings != facings) + { + level->setData(x, y, z, newFacings); + } + return true; + +} + +int VineTile::getColor() const +{ + return FoliageColor::getDefaultColor(); +} + +int VineTile::getColor(int auxData) +{ + return FoliageColor::getDefaultColor(); +} + +int VineTile::getColor(LevelSource *level, int x, int y, int z, int data) +{ + return getColor(level, x, y, z); +} + +int VineTile::getColor(LevelSource *level, int x, int y, int z) +{ + return level->getBiome(x, z)->getFolageColor(); +} + +void VineTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + if (!level->isClientSide && !updateSurvival(level, x, y, z)) + { + spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + } +} + +void VineTile::tick(Level *level, int x, int y, int z, Random *random) +{ + if (!level->isClientSide) + { + if (level->random->nextInt(4) == 0) + { + // 4J - Brought side spread check forward from 1.2.3 + int r = 4; + int max = 5; + bool noSideSpread = false; + for (int xx = x - r; xx <= x + r; xx++) + { + for (int zz = z - r; zz <= z + r; zz++) + for (int yy = y - 1; yy <= y + 1; yy++) + { + if (level->getTile(xx, yy, zz) == id && --max <= 0) + { + noSideSpread = true; + goto testLoop; + } + } +testLoop: if(noSideSpread) break; + } + + int currentFacings = level->getData(x, y, z); + int testFacing = level->random->nextInt(6); + int testDirection = Direction::FACING_DIRECTION[testFacing]; + + if (testFacing == Facing::UP && y < (Level::maxBuildHeight - 1) && level->isEmptyTile(x, y + 1, z)) + { + // 4J - Brought side spread check forward from 1.2.3 + if (noSideSpread) return; + + // grow upwards, but only if there is something to cling to + int spawnFacings = level->random->nextInt(16) & currentFacings; + if (spawnFacings > 0) + { + for (int d = 0; d <= 3; d++) + { + if (!isAcceptableNeighbor(level->getTile(x + Direction::STEP_X[d], y + 1, z + Direction::STEP_Z[d]))) + { + spawnFacings &= ~(1 << d); + } + } + if (spawnFacings > 0) + { + level->setTileAndData(x, y + 1, z, id, spawnFacings); + } + } + } + else if (testFacing >= Facing::NORTH && testFacing <= Facing::EAST && (currentFacings & (1 << testDirection)) == 0) + { + // 4J - Brought side spread check forward from 1.2.3 + if (noSideSpread) return; + + int edgeTile = level->getTile(x + Direction::STEP_X[testDirection], y, z + Direction::STEP_Z[testDirection]); + + if (edgeTile == 0 || Tile::tiles[edgeTile] == NULL) + { + // if the edge tile is air, we could possibly cling + // to something + int left = (testDirection + 1) & 3; + int right = (testDirection + 3) & 3; + + // attempt to grow straight onto solid tiles + if ((currentFacings & (1 << left)) != 0 + && isAcceptableNeighbor(level->getTile(x + Direction::STEP_X[testDirection] + Direction::STEP_X[left], y, z + Direction::STEP_Z[testDirection] + Direction::STEP_Z[left]))) + { + level->setTileAndData(x + Direction::STEP_X[testDirection], y, z + Direction::STEP_Z[testDirection], id, 1 << left); + } + else if ((currentFacings & (1 << right)) != 0 + && isAcceptableNeighbor(level->getTile(x + Direction::STEP_X[testDirection] + Direction::STEP_X[right], y, z + Direction::STEP_Z[testDirection] + Direction::STEP_Z[right]))) + { + level->setTileAndData(x + Direction::STEP_X[testDirection], y, z + Direction::STEP_Z[testDirection], id, 1 << right); + } + // attempt to grow around corners, but only if the + // base tile is solid + else if ((currentFacings & (1 << left)) != 0 + && level->isEmptyTile(x + Direction::STEP_X[testDirection] + Direction::STEP_X[left], y, z + Direction::STEP_Z[testDirection] + Direction::STEP_Z[left]) + && isAcceptableNeighbor(level->getTile(x + Direction::STEP_X[left], y, z + Direction::STEP_Z[left]))) + { + level->setTileAndData(x + Direction::STEP_X[testDirection] + Direction::STEP_X[left], y, z + Direction::STEP_Z[testDirection] + Direction::STEP_Z[left], id, + 1 << ((testDirection + 2) & 3)); + } + else if ((currentFacings & (1 << right)) != 0 + && level->isEmptyTile(x + Direction::STEP_X[testDirection] + Direction::STEP_X[right], y, z + Direction::STEP_Z[testDirection] + Direction::STEP_Z[right]) + && isAcceptableNeighbor(level->getTile(x + Direction::STEP_X[right], y, z + Direction::STEP_Z[right]))) + { + level->setTileAndData(x + Direction::STEP_X[testDirection] + Direction::STEP_X[right], y, z + Direction::STEP_Z[testDirection] + Direction::STEP_Z[right], id, + 1 << ((testDirection + 2) & 3)); + } + // attempt to grow onto the ceiling + else if (isAcceptableNeighbor(level->getTile(x + Direction::STEP_X[testDirection], y + 1, z + Direction::STEP_Z[testDirection]))) + { + level->setTileAndData(x + Direction::STEP_X[testDirection], y, z + Direction::STEP_Z[testDirection], id, 0); + } + + } + else if (Tile::tiles[edgeTile]->material->isSolidBlocking() && Tile::tiles[edgeTile]->isCubeShaped()) + { + // we have a wall that we can cling to + level->setData(x, y, z, currentFacings | (1 << testDirection)); + } + } + // growing downwards happens more often than the other + // directions + else if (y > 1) + { + int belowTile = level->getTile(x, y - 1, z); + // grow downwards into air + if (belowTile == 0) + { + int spawnFacings = level->random->nextInt(16) & currentFacings; + if (spawnFacings > 0) + { + level->setTileAndData(x, y - 1, z, id, spawnFacings); + } + } + else if (belowTile == id) + { + int spawnFacings = level->random->nextInt(16) & currentFacings; + int belowData = level->getData(x, y - 1, z); + if (belowData != (belowData | spawnFacings)) { + level->setData(x, y - 1, z, belowData | spawnFacings); + } + } + } + } + } +} + +int VineTile::getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue) +{ + int facings = 0; + switch (face) + { + case Facing::NORTH: + facings = VINE_SOUTH; + break; + case Facing::SOUTH: + facings = VINE_NORTH; + break; + case Facing::WEST: + facings = VINE_EAST; + break; + case Facing::EAST: + facings = VINE_WEST; + break; + } + if (facings != 0) + { + return facings; + } + return itemValue; +} + +int VineTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return 0; +} + +int VineTile::getResourceCount(Random *random) +{ + return 0; +} + +void VineTile::playerDestroy(Level *level, shared_ptrplayer, int x, int y, int z, int data) +{ + if (!level->isClientSide && player->getSelectedItem() != NULL && player->getSelectedItem()->id == Item::shears->id) + { + player->awardStat( + GenericStats::blocksMined(id), + GenericStats::param_blocksMined(id,data,1) + ); + + // drop leaf block instead of sapling + popResource(level, x, y, z, shared_ptr(new ItemInstance(Tile::vine, 1, 0))); + } + else + { + Tile::playerDestroy(level, player, x, y, z, data); + } +} diff --git a/Minecraft.World/VineTile.h b/Minecraft.World/VineTile.h new file mode 100644 index 00000000..bdea2cdc --- /dev/null +++ b/Minecraft.World/VineTile.h @@ -0,0 +1,38 @@ +#pragma once +#include "Tile.h" +#include "Direction.h" +class Level; +class LevelSource; + +class VineTile : public Tile +{ +public: + static const int VINE_SOUTH = 1 << Direction::SOUTH; + static const int VINE_NORTH = 1 << Direction::NORTH; + static const int VINE_EAST = 1 << Direction::EAST; + static const int VINE_WEST = 1 << Direction::WEST; + +public: + VineTile(int id); + virtual void updateDefaultShape(); + virtual int getRenderShape(); + virtual bool isSolidRender(bool isServerLevel = false); + virtual bool isCubeShaped(); + virtual void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); // 4J added forceData, forceEntity param + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual bool mayPlace(Level *level, int x, int y, int z, int face); +private: + bool isAcceptableNeighbor(int id); + bool updateSurvival(Level *level, int x, int y, int z); +public: + virtual int getColor() const; + virtual int getColor(int auxData); + virtual int getColor(LevelSource *level, int x, int y, int z, int data); // 4J added + virtual int getColor(LevelSource *level, int x, int y, int z); + virtual void neighborChanged(Level *level, int x, int y, int z, int type); + virtual void tick(Level *level, int x, int y, int z, Random *random); + virtual int getPlacedOnFaceDataValue(Level *level, int x, int y, int z, int face, float clickX, float clickY, float clickZ, int itemValue); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int getResourceCount(Random *random); + virtual void playerDestroy(Level *level, shared_ptr player, int x, int y, int z, int data); +}; \ No newline at end of file diff --git a/Minecraft.World/VinesFeature.cpp b/Minecraft.World/VinesFeature.cpp new file mode 100644 index 00000000..314b978e --- /dev/null +++ b/Minecraft.World/VinesFeature.cpp @@ -0,0 +1,39 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.h" +#include "VinesFeature.h" + + +VinesFeature::VinesFeature() +{ +} + +bool VinesFeature::place(Level *level, Random *random, int x, int y, int z) +{ + int ox = x; + int oz = z; + + while (y < 128) + { + if (level->isEmptyTile(x, y, z)) + { + for (int face = Facing::NORTH; face <= Facing::EAST; face++) + { + if (Tile::vine->mayPlace(level, x, y, z, face)) + { + level->setTileAndDataNoUpdate(x, y, z, Tile::vine_Id, 1 << Direction::FACING_DIRECTION[Facing::OPPOSITE_FACING[face]]); + break; + } + } + } + else + { + x = ox + random->nextInt(4) - random->nextInt(4); + z = oz + random->nextInt(4) - random->nextInt(4); + } + y++; + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/VinesFeature.h b/Minecraft.World/VinesFeature.h new file mode 100644 index 00000000..8dea3df7 --- /dev/null +++ b/Minecraft.World/VinesFeature.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Feature.h" + +class VinesFeature : public Feature +{ + +public: + VinesFeature(); + + bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/VoronoiZoom.cpp b/Minecraft.World/VoronoiZoom.cpp new file mode 100644 index 00000000..78323dfa --- /dev/null +++ b/Minecraft.World/VoronoiZoom.cpp @@ -0,0 +1,122 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" +#include "System.h" + +VoronoiZoom::VoronoiZoom(__int64 seedMixup, shared_ptrparent) : Layer(seedMixup) +{ + this->parent = parent; +} + +intArray VoronoiZoom::getArea(int xo, int yo, int w, int h) +{ + xo -= 2; + yo -= 2; + int bits = 2; + int ss = 1 << bits; + int px = xo >> bits; + int py = yo >> bits; + int pw = (w >> bits) + 3; + int ph = (h >> bits) + 3; + intArray p = parent->getArea(px, py, pw, ph); + + int ww = pw << bits; + int hh = ph << bits; + intArray tmp = IntCache::allocate(ww * hh); + for (int y = 0; y < ph - 1; y++) + { + int ul = p[(0 + 0) + (y + 0) * pw]; + int dl = p[(0 + 0) + (y + 1) * pw]; + for (int x = 0; x < pw - 1; x++) + { + double s = ss * 0.9; + initRandom((x + px) << bits, (y + py) << bits); + double x0 = (nextRandom(1024) / 1024.0 - 0.5) * s; + double y0 = (nextRandom(1024) / 1024.0 - 0.5) * s; + initRandom((x + px + 1) << bits, (y + py) << bits); + double x1 = (nextRandom(1024) / 1024.0 - 0.5) * s + ss; + double y1 = (nextRandom(1024) / 1024.0 - 0.5) * s; + initRandom((x + px) << bits, (y + py + 1) << bits); + double x2 = (nextRandom(1024) / 1024.0 - 0.5) * s; + double y2 = (nextRandom(1024) / 1024.0 - 0.5) * s + ss; + initRandom((x + px + 1) << bits, (y + py + 1) << bits); + double x3 = (nextRandom(1024) / 1024.0 - 0.5) * s + ss; + double y3 = (nextRandom(1024) / 1024.0 - 0.5) * s + ss; + + int ur = p[(x + 1) + (y + 0) * pw]; + int dr = p[(x + 1) + (y + 1) * pw]; + + for (int yy = 0; yy < ss; yy++) + { + int pp = ((y << bits) + yy) * ww + ((x << bits)); + for (int xx = 0; xx < ss; xx++) + { + double d0 = ((yy - y0) * (yy - y0) + (xx - x0) * (xx - x0)); + double d1 = ((yy - y1) * (yy - y1) + (xx - x1) * (xx - x1)); + double d2 = ((yy - y2) * (yy - y2) + (xx - x2) * (xx - x2)); + double d3 = ((yy - y3) * (yy - y3) + (xx - x3) * (xx - x3)); + + if (d0 < d1 && d0 < d2 && d0 < d3) + { + tmp[pp++] = ul; + } + else if (d1 < d0 && d1 < d2 && d1 < d3) + { + tmp[pp++] = ur; + } + else if (d2 < d0 && d2 < d1 && d2 < d3) + { + tmp[pp++] = dl; + } + else + { + tmp[pp++] = dr; + } + } + } + + ul = ur; + dl = dr; + } + } + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + System::arraycopy(tmp, (y + (yo & (ss - 1))) * (pw << bits) + (xo & (ss - 1)), &result, y * w, w); + } + return result; +} + +int VoronoiZoom::random(int a, int b) +{ + return nextRandom(2) == 0 ? a : b; +} + +int VoronoiZoom::random(int a, int b, int c, int d) +{ + if (b == c && c == d) return b; + if (a == b && a == c) return a; + if (a == b && a == d) return a; + if (a == c && a == d) return a; + + if (a == b && c != d) return a; + if (a == c && b != d) return a; + if (a == d && b != c) return a; + + if (b == a && c != d) return b; + if (b == c && a != d) return b; + if (b == d && a != c) return b; + + if (c == a && b != d) return c; + if (c == b && a != d) return c; + if (c == d && a != b) return c; + + if (d == a && b != c) return c; + if (d == b && a != c) return c; + if (d == c && a != b) return c; + + int s = nextRandom(4); + if (s == 0) return a; + if (s == 1) return b; + if (s == 2) return c; + return d; +} \ No newline at end of file diff --git a/Minecraft.World/VoronoiZoom.h b/Minecraft.World/VoronoiZoom.h new file mode 100644 index 00000000..c0738c90 --- /dev/null +++ b/Minecraft.World/VoronoiZoom.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Layer.h" + +class VoronoiZoom : public Layer +{ +public: + VoronoiZoom(__int64 seedMixup, shared_ptrparent); + + virtual intArray getArea(int xo, int yo, int w, int h); + +protected: + int random(int a, int b); + int random(int a, int b, int c, int d); +}; \ No newline at end of file diff --git a/Minecraft.World/WallTile.cpp b/Minecraft.World/WallTile.cpp new file mode 100644 index 00000000..6275eef7 --- /dev/null +++ b/Minecraft.World/WallTile.cpp @@ -0,0 +1,189 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.h" +#include "net.minecraft.h" +#include "WallTile.h" + +const float WallTile::WALL_WIDTH = 3.0f / 16.0f; +const float WallTile::WALL_HEIGHT = 13.0f / 16.0f; +const float WallTile::POST_WIDTH = 4.0f / 16.0f; +const float WallTile::POST_HEIGHT = 16.0f / 16.0f; + +const unsigned int WallTile::COBBLE_NAMES[2] = { IDS_TILE_COBBLESTONE_WALL, + IDS_TILE_COBBLESTONE_WALL_MOSSY, +}; + +WallTile::WallTile(int id, Tile *baseTile) : Tile(id, baseTile->material, isSolidRender()) +{ + setDestroyTime(baseTile->destroySpeed); + setExplodeable(baseTile->explosionResistance / 3); + setSoundType(baseTile->soundType); +} + +Icon *WallTile::getTexture(int face, int data) +{ + if (data == TYPE_MOSSY) + { + return Tile::mossStone->getTexture(face); + } + return Tile::stoneBrick->getTexture(face); +} + +int WallTile::getRenderShape() +{ + return SHAPE_WALL; +} + +bool WallTile::isCubeShaped() +{ + return false; +} + +bool WallTile::isPathfindable(LevelSource *level, int x, int y, int z) +{ + return false; +} + +bool WallTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +void WallTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) +{ + bool n = connectsTo(level, x, y, z - 1); + bool s = connectsTo(level, x, y, z + 1); + bool w = connectsTo(level, x - 1, y, z); + bool e = connectsTo(level, x + 1, y, z); + + float west = .5f - POST_WIDTH; + float east = .5f + POST_WIDTH; + float north = .5f - POST_WIDTH; + float south = .5f + POST_WIDTH; + float up = POST_HEIGHT; + + if (n) + { + north = 0; + } + if (s) + { + south = 1; + } + if (w) + { + west = 0; + } + if (e) + { + east = 1; + } + + if (n && s && !w && !e) + { + up = WALL_HEIGHT; + west = .5f - WALL_WIDTH; + east = .5f + WALL_WIDTH; + } + else if (!n && !s && w && e) + { + up = WALL_HEIGHT; + north = .5f - WALL_WIDTH; + south = .5f + WALL_WIDTH; + } + + setShape(west, 0, north, east, up, south); +} + +AABB *WallTile::getAABB(Level *level, int x, int y, int z) +{ + // 4J-JEV: Changed to avoid race conditions associated with calling update shape. + + bool n = connectsTo(level, x, y, z - 1); + bool s = connectsTo(level, x, y, z + 1); + bool w = connectsTo(level, x - 1, y, z); + bool e = connectsTo(level, x + 1, y, z); + + float west = .5f - POST_WIDTH; + float east = .5f + POST_WIDTH; + float north = .5f - POST_WIDTH; + float south = .5f + POST_WIDTH; + float up = POST_HEIGHT; + + if (n) + { + north = 0; + } + if (s) + { + south = 1; + } + if (w) + { + west = 0; + } + if (e) + { + east = 1; + } + + /* 4J-JEV: + Stopping the width changing here, it's causing cows/mobs/passers-by to 'jump' up when they are pressed against the + wall and then the wall section is upgraded to a wall post expanding the bounding box. + It's only a 1/16 of a block difference, it shouldn't matter if we leave it a little larger. + */ + if (n && s && !w && !e) + { + up = WALL_HEIGHT; + //west = .5f - WALL_WIDTH; + //east = .5f + WALL_WIDTH; + } + else if (!n && !s && w && e) + { + up = WALL_HEIGHT; + //north = .5f - WALL_WIDTH; + //south = .5f + WALL_WIDTH; + } + + return AABB::newTemp(x+west, y, z+north, x+east, y+1.5f, z+south); +} + + +bool WallTile::connectsTo(LevelSource *level, int x, int y, int z) +{ + int tile = level->getTile(x, y, z); + if (tile == id || tile == Tile::fenceGate_Id) + { + return true; + } + Tile *tileInstance = Tile::tiles[tile]; + if (tileInstance != NULL) + { + if (tileInstance->material->isSolidBlocking() && tileInstance->isCubeShaped()) + { + return tileInstance->material != Material::vegetable; + } + } + return false; +} + +int WallTile::getSpawnResourcesAuxValue(int data) +{ + return data; +} + +bool WallTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + if (face == Facing::DOWN) + { + return Tile::shouldRenderFace(level, x, y, z, face); + } + return true; +} + +void WallTile::registerIcons(IconRegister *iconRegister) +{ + // None +} diff --git a/Minecraft.World/WallTile.h b/Minecraft.World/WallTile.h new file mode 100644 index 00000000..a9a383d1 --- /dev/null +++ b/Minecraft.World/WallTile.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Tile.h" + +class WallTile : public Tile +{ +public: + static const float WALL_WIDTH; + static const float WALL_HEIGHT; + static const float POST_WIDTH; + static const float POST_HEIGHT; + + static const int TYPE_NORMAL = 0; + static const int TYPE_MOSSY = 1; + + static const unsigned int COBBLE_NAMES[2]; + + WallTile(int id, Tile *baseTile); + + Icon *getTexture(int face, int data); + int getRenderShape(); + bool isCubeShaped(); + bool isPathfindable(LevelSource *level, int x, int y, int z); + bool isSolidRender(bool isServerLevel = false); + void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); + AABB *getAABB(Level *level, int x, int y, int z); + bool connectsTo(LevelSource *level, int x, int y, int z); + int getSpawnResourcesAuxValue(int data); + bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/WaterAnimal.cpp b/Minecraft.World/WaterAnimal.cpp new file mode 100644 index 00000000..0a7313d1 --- /dev/null +++ b/Minecraft.World/WaterAnimal.cpp @@ -0,0 +1,42 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "WaterAnimal.h" + + + +WaterAnimal::WaterAnimal(Level *level) : PathfinderMob( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + + // This should only be called for the most derive classes + //this->defineSynchedData(); +} + +bool WaterAnimal::isWaterMob() +{ + return true; //prevent drowning +} + +bool WaterAnimal::canSpawn() +{ + return level->isUnobstructed(bb); +} + +int WaterAnimal::getAmbientSoundInterval() +{ + return 20 * 6; +} + +bool WaterAnimal::removeWhenFarAway() +{ + return true; +} + +int WaterAnimal::getExperienceReward(shared_ptr killedBy) +{ + return 1 + level->random->nextInt(3); +} \ No newline at end of file diff --git a/Minecraft.World/WaterAnimal.h b/Minecraft.World/WaterAnimal.h new file mode 100644 index 00000000..23b984f5 --- /dev/null +++ b/Minecraft.World/WaterAnimal.h @@ -0,0 +1,18 @@ +#pragma once +#include "PathfinderMob.h" +#include "Creature.h" + +class Player; + +class WaterAnimal : public PathfinderMob, public Creature +{ +public: + WaterAnimal(Level *level); + virtual bool isWaterMob(); + virtual bool canSpawn(); + virtual int getAmbientSoundInterval(); + +protected: + virtual bool removeWhenFarAway(); + virtual int getExperienceReward(shared_ptr killedBy); +}; diff --git a/Minecraft.World/WaterColor.cpp b/Minecraft.World/WaterColor.cpp new file mode 100644 index 00000000..dfa8c2dc --- /dev/null +++ b/Minecraft.World/WaterColor.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" + +#include "WaterColor.h" + +// 4J Stu - Not using this any more +//intArray WaterColor::pixels; +// +//void WaterColor::init(intArray pixels) +//{ +// int *oldData = WaterColor::pixels.data; +// WaterColor::pixels = pixels; +// delete [] oldData; +//} +// +//int WaterColor::get(double temp, double rain) +//{ +// rain *= temp; +// int x = (int) ((1 - temp) * 255); +// int y = (int) ((1 - rain) * 255); +// int returnVal = pixels[y << 8 | x]; +// +// return returnVal; +//} \ No newline at end of file diff --git a/Minecraft.World/WaterColor.h b/Minecraft.World/WaterColor.h new file mode 100644 index 00000000..c4473091 --- /dev/null +++ b/Minecraft.World/WaterColor.h @@ -0,0 +1,14 @@ +#pragma once + +class WaterColor +{ + // 4J Stu - Not using this (and it wasn't ever used anyway...) +//private: +// static intArray pixels; +// +//public: +// static void init(intArray pixels); +// +//public: +// static int get(double temp, double rain); +}; diff --git a/Minecraft.World/WaterLevelChunk.cpp b/Minecraft.World/WaterLevelChunk.cpp new file mode 100644 index 00000000..b6dbd05d --- /dev/null +++ b/Minecraft.World/WaterLevelChunk.cpp @@ -0,0 +1,159 @@ +#include "stdafx.h" +#include "Arrays.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.phys.h" +#include "WaterLevelChunk.h" +#include "net.minecraft.world.level.biome.h" + +WaterLevelChunk::WaterLevelChunk(Level *level, byteArray blocks, int x, int z): LevelChunk(level,blocks,x,z) +{ + dontSave = true; + // Set this as fully post-processed, so we don't try and run post-processing on any edge chunks that will overlap into real chunks + terrainPopulated = LevelChunk::sTerrainPopulatedAllNeighbours | LevelChunk::sTerrainPostPostProcessed; +} + +bool WaterLevelChunk::isAt(int x, int z) +{ + return x == this->x && z == this->z; +} + +void WaterLevelChunk::recalcBlockLights() +{ +} + +void WaterLevelChunk::recalcHeightmapOnly() +{ +} + +void WaterLevelChunk::recalcHeightmap() +{ +} + +void WaterLevelChunk::lightLava() +{ +} + +bool WaterLevelChunk::setTileAndData(int x, int y, int z, int _tile, int _data) +{ + return true; +} + +bool WaterLevelChunk::setTile(int x, int y, int z, int _tile) +{ + return true; +} + +bool WaterLevelChunk::setData(int x, int y, int z, int val, int mask, bool *maskedBitsChanged) +{ + *maskedBitsChanged = true; + return true; +} + +void WaterLevelChunk::setBrightness(LightLayer::variety layer, int x, int y, int z, int brightness) +{ +} + +void WaterLevelChunk::addEntity(shared_ptr e) +{ +} + +void WaterLevelChunk::removeEntity(shared_ptr e) +{ +} + +void WaterLevelChunk::removeEntity(shared_ptr e, int yc) +{ +} + +void WaterLevelChunk::skyBrightnessChanged() +{ +} + +shared_ptr WaterLevelChunk::getTileEntity(int x, int y, int z) +{ + return shared_ptr(); +} + +void WaterLevelChunk::addTileEntity(shared_ptr te) +{ +} + +void WaterLevelChunk::setTileEntity(int x, int y, int z, shared_ptr tileEntity) +{ +} + +void WaterLevelChunk::removeTileEntity(int x, int y, int z) +{ +} + +void WaterLevelChunk::load() +{ +} + +void WaterLevelChunk::unload(bool unloadTileEntities) // 4J - added parameter +{ +} + +void WaterLevelChunk::markUnsaved() +{ +} + +void WaterLevelChunk::getEntities(shared_ptr except, AABB bb, vector > &es) +{ +} + +void WaterLevelChunk::getEntitiesOfClass(const type_info& ec, AABB bb, vector > &es) +{ +} + +int WaterLevelChunk::countEntities() +{ + return 0; +} + +bool WaterLevelChunk::shouldSave(bool force) +{ + return false; +} + +void WaterLevelChunk::setBlocks(byteArray newBlocks, int sub) +{ +} + +int WaterLevelChunk::setBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p, bool includeLighting/* = true*/) +{ + int xs = x1 - x0; + int ys = y1 - y0; + int zs = z1 - z0; + + int s = xs * ys * zs; + + if( includeLighting ) + { + return s + s / 2 * 3; + } + else + { + return s + s / 2; + } +} + +bool WaterLevelChunk::testSetBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p) +{ + return false; +} + +Random *WaterLevelChunk::getRandom(__int64 l) +{ + return new Random((level->getSeed() + x * x * 4987142 + x * 5947611 + z * z * 4392871l + z * 389711) ^ l); +} + +void WaterLevelChunk::setLevelChunkBrightness(LightLayer::variety layer, int x, int y, int z, int brightness) +{ + LevelChunk::setBrightness(layer, x, y, z, brightness); +} + +Biome *WaterLevelChunk::getBiome(int x, int z, BiomeSource *biomeSource) +{ + return NULL; +} diff --git a/Minecraft.World/WaterLevelChunk.h b/Minecraft.World/WaterLevelChunk.h new file mode 100644 index 00000000..18dc8a05 --- /dev/null +++ b/Minecraft.World/WaterLevelChunk.h @@ -0,0 +1,45 @@ +#pragma once +#include "LevelChunk.h" +#include "Definitions.h" + +class Level; + +class WaterLevelChunk: public LevelChunk +{ +public: + using LevelChunk::getEntities; + using LevelChunk::getEntitiesOfClass; + + WaterLevelChunk(Level *level, byteArray blocks, int x, int z); + bool isAt(int x, int z); + void recalcBlockLights(); + void recalcHeightmapOnly(); + void recalcHeightmap(); + void lightLava(); + bool setTileAndData(int x, int y, int z, int _tile, int _data); + bool setTile(int x, int y, int z, int _tile); + bool setData(int x, int y, int z, int val, int mask, bool *maskedBitsChanged); // 4J added mask + void setBrightness(LightLayer::variety layer, int x, int y, int z, int brightness); + void setLevelChunkBrightness(LightLayer::variety layer, int x, int y, int z, int brightness); // 4J added - calls the setBrightness method of the parent class + void addEntity(shared_ptr e); + void removeEntity(shared_ptr e); + void removeEntity(shared_ptr e, int yc); + void skyBrightnessChanged(); + shared_ptr getTileEntity(int x, int y, int z); + void addTileEntity(shared_ptr te); + void setTileEntity(int x, int y, int z, shared_ptr tileEntity); + void removeTileEntity(int x, int y, int z); + void load(); + void unload(bool unloadTileEntities) ; // 4J - added parameter + void markUnsaved(); + void getEntities(shared_ptr except, AABB bb, vector > &es); + void getEntitiesOfClass(const type_info& ec, AABB bb, vector > &es); + int countEntities(); + bool shouldSave(bool force); + void setBlocks(byteArray newBlocks, int sub); + int setBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p, bool includeLighting = true); // 4J - added includeLighting parameter; + bool testSetBlocksAndData(byteArray data, int x0, int y0, int z0, int x1, int y1, int z1, int p); + Random *getRandom(__int64 l); + virtual Biome *getBiome(int x, int z, BiomeSource *biomeSource); + virtual void reSyncLighting() {}; // 4J added +}; diff --git a/Minecraft.World/WaterLilyTile.cpp b/Minecraft.World/WaterLilyTile.cpp new file mode 100644 index 00000000..91203922 --- /dev/null +++ b/Minecraft.World/WaterLilyTile.cpp @@ -0,0 +1,76 @@ +#include "stdafx.h" +#include "WaterLilyTile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.entity.item.h" +#include "..\Minecraft.Client\Minecraft.h" +#include "AABB.h" + +WaterlilyTile::WaterlilyTile(int id) : Bush(id) +{ + this->updateDefaultShape(); +} + +// 4J Added override +void WaterlilyTile::updateDefaultShape() +{ + float ss = 0.5f; + float hh = 0.25f / 16.0f; + this->setShape(0.5f - ss, 0, 0.5f - ss, 0.5f + ss, hh, 0.5f + ss); +} + +int WaterlilyTile::getRenderShape() +{ + return Tile::SHAPE_LILYPAD; +} + +void WaterlilyTile::addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source) +{ + if (source == NULL || !(dynamic_pointer_cast(source))) + { + Bush::addAABBs(level, x, y, z, box, boxes, source); + } +} + +AABB *WaterlilyTile::getAABB(Level *level, int x, int y, int z) +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return AABB::newTemp(x + tls->xx0, y + tls->yy0, z + tls->zz0, x + tls->xx1, y + tls->yy1, z + tls->zz1); +} + +int WaterlilyTile::getColor() const +{ + return Minecraft::GetInstance()->getColourTable()->getColor(eMinecraftColour_Tile_WaterLily); //0x208030 +} + +int WaterlilyTile::getColor(int auxData) +{ + return Minecraft::GetInstance()->getColourTable()->getColor(eMinecraftColour_Tile_WaterLily); //0x208030 +} + +int WaterlilyTile::getColor(LevelSource *level, int x, int y, int z) +{ + return Minecraft::GetInstance()->getColourTable()->getColor(eMinecraftColour_Tile_WaterLily); //0x208030 +} + +int WaterlilyTile::getColor(LevelSource *level, int x, int y, int z, int data) //0x208030 +{ + return getColor(level, x, y, z); +} + +bool WaterlilyTile::mayPlaceOn(int tile) +{ + return tile == Tile::calmWater_Id; +} + +bool WaterlilyTile::canSurvive(Level *level, int x, int y, int z) +{ + if (y < 0 || y >= Level::maxBuildHeight) return false; + return level->getMaterial(x, y - 1, z) == Material::water && level->getData(x, y - 1, z) == 0; +} + +bool WaterlilyTile::growTree(Level *level, int x, int y, int z, Random *random) +{ + return false; +} diff --git a/Minecraft.World/WaterLilyTile.h b/Minecraft.World/WaterLilyTile.h new file mode 100644 index 00000000..af07269c --- /dev/null +++ b/Minecraft.World/WaterLilyTile.h @@ -0,0 +1,24 @@ +#pragma once +#include "Bush.h" + +class WaterlilyTile : public Bush +{ +private: + // static const int col = 0x208030; + +public: + WaterlilyTile(int id); + virtual void updateDefaultShape(); // 4J Added override + + virtual int getRenderShape(); + virtual void addAABBs(Level *level, int x, int y, int z, AABB *box, AABBList *boxes, shared_ptr source); + virtual AABB *getAABB(Level *level, int x, int y, int z); + virtual int getColor() const; + virtual int getColor(int auxData); + virtual int getColor(LevelSource *level, int x, int y, int z); + virtual int getColor(LevelSource *level, int x, int y, int z, int data); // 4J added +protected: + virtual bool mayPlaceOn(int tile); + virtual bool canSurvive(Level *level, int x, int y, int z); + bool growTree(Level *level, int x, int y, int z, Random *random); +}; diff --git a/Minecraft.World/WaterLilyTileItem.cpp b/Minecraft.World/WaterLilyTileItem.cpp new file mode 100644 index 00000000..13814e5c --- /dev/null +++ b/Minecraft.World/WaterLilyTileItem.cpp @@ -0,0 +1,78 @@ +#include "stdafx.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.material.h" +#include "net.minecraft.world.entity.player.h" +#include "WaterLilyTileItem.h" + +WaterLilyTileItem::WaterLilyTileItem(int id) : ColoredTileItem(id, false) +{ +} + +bool WaterLilyTileItem::TestUse(Level *level, shared_ptr player) +{ + HitResult *hr = getPlayerPOVHitResult(level, player, true); + if (hr == NULL) return false; + + if (hr->type == HitResult::TILE) + { + int xt = hr->x; + int yt = hr->y; + int zt = hr->z; + delete hr; + if (!level->mayInteract(player, xt, yt, zt, 0)) + { + return false; + } + if (!player->mayBuild(xt, yt, zt)) return false; + + if (level->getMaterial(xt, yt, zt) == Material::water && level->getData(xt, yt, zt) == 0 && level->isEmptyTile(xt, yt + 1, zt)) + { + return true; + } + } + else + { + delete hr; + } + return false; +} + +shared_ptr WaterLilyTileItem::use(shared_ptr itemInstance, Level *level, shared_ptr player) +{ + HitResult *hr = getPlayerPOVHitResult(level, player, true); + if (hr == NULL) return itemInstance; + + if (hr->type == HitResult::TILE) + { + int xt = hr->x; + int yt = hr->y; + int zt = hr->z; + delete hr; + if (!level->mayInteract(player, xt, yt, zt, 0)) + { + return itemInstance; + } + if (!player->mayBuild(xt, yt, zt)) return itemInstance; + + if (level->getMaterial(xt, yt, zt) == Material::water && level->getData(xt, yt, zt) == 0 && level->isEmptyTile(xt, yt + 1, zt)) + { + level->setTile(xt, yt + 1, zt, Tile::waterLily->id); + if (!player->abilities.instabuild) + { + itemInstance->count--; + } + } + } + else + { + delete hr; + } + return itemInstance; +} + +int WaterLilyTileItem::getColor(int data, int spriteLayer) +{ + return Tile::waterLily->getColor(data); +} \ No newline at end of file diff --git a/Minecraft.World/WaterLilyTileItem.h b/Minecraft.World/WaterLilyTileItem.h new file mode 100644 index 00000000..c0c5db99 --- /dev/null +++ b/Minecraft.World/WaterLilyTileItem.h @@ -0,0 +1,14 @@ +#pragma once + +#include "ColoredTileItem.h" + +class WaterLilyTileItem : public ColoredTileItem +{ +public: + using ColoredTileItem::getColor; + WaterLilyTileItem(int id); + + virtual shared_ptr use(shared_ptr itemInstance, Level *level, shared_ptr player); + virtual bool TestUse(Level *level, shared_ptr player); + virtual int getColor(int data, int spriteLayer); +}; diff --git a/Minecraft.World/WaterWorkerEnchantment.cpp b/Minecraft.World/WaterWorkerEnchantment.cpp new file mode 100644 index 00000000..591e360a --- /dev/null +++ b/Minecraft.World/WaterWorkerEnchantment.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" +#include "WaterWorkerEnchantment.h" + +WaterWorkerEnchantment::WaterWorkerEnchantment(int id, int frequency) : Enchantment(id, frequency, EnchantmentCategory::armor_head) +{ + setDescriptionId(IDS_ENCHANTMENT_WATER_WORKER); +} + +int WaterWorkerEnchantment::getMinCost(int level) +{ + return 1; +} + +int WaterWorkerEnchantment::getMaxCost(int level) +{ + return getMinCost(level) + 40; +} + +int WaterWorkerEnchantment::getMaxLevel() +{ + return 1; +} \ No newline at end of file diff --git a/Minecraft.World/WaterWorkerEnchantment.h b/Minecraft.World/WaterWorkerEnchantment.h new file mode 100644 index 00000000..29a5a53a --- /dev/null +++ b/Minecraft.World/WaterWorkerEnchantment.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Enchantment.h" + +class WaterWorkerEnchantment : public Enchantment +{ +public: + WaterWorkerEnchantment(int id, int frequency); + + virtual int getMinCost(int level); + virtual int getMaxCost(int level); + virtual int getMaxLevel(); +}; \ No newline at end of file diff --git a/Minecraft.World/WaterlilyFeature.cpp b/Minecraft.World/WaterlilyFeature.cpp new file mode 100644 index 00000000..c47a667c --- /dev/null +++ b/Minecraft.World/WaterlilyFeature.cpp @@ -0,0 +1,23 @@ +#include "stdafx.h" +#include "WaterlilyFeature.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.level.h" + +bool WaterlilyFeature::place(Level *level, Random *random, int x, int y, int z) +{ + for (int i = 0; i < 10; i++) + { + int x2 = x + random->nextInt(8) - random->nextInt(8); + int y2 = y + random->nextInt(4) - random->nextInt(4); + int z2 = z + random->nextInt(8) - random->nextInt(8); + if (level->isEmptyTile(x2, y2, z2)) + { + if (Tile::waterLily->mayPlace(level, x2, y2, z2)) + { + level->setTileNoUpdate(x2, y2, z2, Tile::waterLily_Id); + } + } + } + + return true; +} \ No newline at end of file diff --git a/Minecraft.World/WaterlilyFeature.h b/Minecraft.World/WaterlilyFeature.h new file mode 100644 index 00000000..ddf2330f --- /dev/null +++ b/Minecraft.World/WaterlilyFeature.h @@ -0,0 +1,7 @@ +#pragma once +#include "Feature.h" + +class WaterlilyFeature : public Feature +{ + virtual bool place(Level *level, Random *random, int x, int y, int z); +}; \ No newline at end of file diff --git a/Minecraft.World/WeaponItem.cpp b/Minecraft.World/WeaponItem.cpp new file mode 100644 index 00000000..82b16bde --- /dev/null +++ b/Minecraft.World/WeaponItem.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.tile.h" +#include "WeaponItem.h" + +WeaponItem::WeaponItem(int id, const Tier *tier) : Item(id), tier( tier ) +{ + maxStackSize = 1; + setMaxDamage(tier->getUses()); + + damage = 4 + tier->getAttackDamageBonus(); +} + +float WeaponItem::getDestroySpeed(shared_ptr itemInstance, Tile *tile) +{ + if (tile->id == Tile::web_Id) + { + // swords can quickly cut web + return 15; + } + return 1.5f; +} + +bool WeaponItem::hurtEnemy(shared_ptr itemInstance, shared_ptr mob, shared_ptr attacker) +{ + itemInstance->hurt(1, attacker); + return true; +} + +bool WeaponItem::mineBlock(shared_ptr itemInstance, Level *level, int tile, int x, int y, int z, shared_ptr owner) +{ + // Don't damage weapons if the tile can be destroyed in one hit. + if (Tile::tiles[tile]->getDestroySpeed(level, x, y, z) != 0.0) itemInstance->hurt(2, owner); + return true; +} + +int WeaponItem::getAttackDamage(shared_ptr entity) +{ + return damage; +} + +bool WeaponItem::isHandEquipped() +{ + return true; +} + +UseAnim WeaponItem::getUseAnimation(shared_ptr itemInstance) +{ + return UseAnim_block; +} + +int WeaponItem::getUseDuration(shared_ptr itemInstance) +{ + return 20 * 60 * 60; // Block for a maximum of one hour! +} + +shared_ptr WeaponItem::use(shared_ptr instance, Level *level, shared_ptr player) +{ + player->startUsingItem(instance, getUseDuration(instance)); + return instance; +} + +bool WeaponItem::canDestroySpecial(Tile *tile) +{ + return tile->id == Tile::web_Id; +} + +int WeaponItem::getEnchantmentValue() +{ + return tier->getEnchantmentValue(); +} + +const Item::Tier *WeaponItem::getTier() +{ + return tier; +} + +bool WeaponItem::isValidRepairItem(shared_ptr source, shared_ptr repairItem) +{ + if (tier->getTierItemId() == repairItem->id) + { + return true; + } + return Item::isValidRepairItem(source, repairItem); +} \ No newline at end of file diff --git a/Minecraft.World/WeaponItem.h b/Minecraft.World/WeaponItem.h new file mode 100644 index 00000000..2150f6e9 --- /dev/null +++ b/Minecraft.World/WeaponItem.h @@ -0,0 +1,28 @@ +#pragma once +using namespace std; + +#include "Item.h" + +class WeaponItem : public Item +{ +private: + int damage; + const Tier *tier; + +public: + WeaponItem(int id, const Tier *tier); + + virtual float getDestroySpeed(shared_ptr itemInstance, Tile *tile); + virtual bool hurtEnemy(shared_ptr itemInstance, shared_ptr mob, shared_ptr attacker); + virtual bool mineBlock(shared_ptr itemInstance, Level *level, int tile, int x, int y, int z, shared_ptr owner); + virtual int getAttackDamage(shared_ptr entity); + virtual bool isHandEquipped(); + virtual UseAnim getUseAnimation(shared_ptr itemInstance); + virtual int getUseDuration(shared_ptr itemInstance); + virtual shared_ptr use(shared_ptr instance, Level *level, shared_ptr player); + virtual bool canDestroySpecial(Tile *tile); + virtual int getEnchantmentValue(); + + const Tier *getTier(); + bool isValidRepairItem(shared_ptr source, shared_ptr repairItem); +}; \ No newline at end of file diff --git a/Minecraft.World/WeaponRecipies.cpp b/Minecraft.World/WeaponRecipies.cpp new file mode 100644 index 00000000..e90ebea6 --- /dev/null +++ b/Minecraft.World/WeaponRecipies.cpp @@ -0,0 +1,107 @@ +#include "stdafx.h" + +#include "net.minecraft.world.item.h" +#include "Tile.h" +#include "Recipy.h" +#include "Recipes.h" +#include "WeaponRecipies.h" + +// 4J-PB - adding "" on the end of these so we can detect it +wstring WeaponRecipies::shapes[][4] = +{ + {L"X", // + L"X",// + L"#",L""},// +}; + +/* + private Object[][] map = { + {Tile.wood, Tile.stoneBrick, Item.ironIngot, Item.diamond, Item.goldIngot}, + {Item.sword_wood, Item.sword_stone, Item.sword_iron, Item.sword_diamond, Item.sword_gold}, + }; +*/ + +void WeaponRecipies::_init() +{ + map = new vector [MAX_WEAPON_RECIPES]; + + ADD_OBJECT(map[0],Tile::wood); + ADD_OBJECT(map[0],Tile::stoneBrick); + ADD_OBJECT(map[0],Item::ironIngot); + ADD_OBJECT(map[0],Item::diamond); + ADD_OBJECT(map[0],Item::goldIngot); + + ADD_OBJECT(map[1],Item::sword_wood); + ADD_OBJECT(map[1],Item::sword_stone); + ADD_OBJECT(map[1],Item::sword_iron); + ADD_OBJECT(map[1],Item::sword_diamond); + ADD_OBJECT(map[1],Item::sword_gold); +} + +void WeaponRecipies::addRecipes(Recipes *r) +{ + wchar_t wchTypes[7]; + wchTypes[6]=0; + + for (unsigned int m = 0; m < map[0].size(); m++) + { + Object *pObjMaterial = map[0].at(m); + + for (int t=0; titem; + + wchTypes[0]=L'w'; + wchTypes[1]=L'c'; + wchTypes[2]=L'i'; + wchTypes[3]=L'c'; + wchTypes[5]=L'g'; + if(pObjMaterial->GetType()==eType_TILE) + { + wchTypes[4]=L't'; + r->addShapedRecipy(new ItemInstance(target), + wchTypes, + shapes[t], + + L'#', Item::stick, + L'X', pObjMaterial->tile, + L'T'); + } + else + { + // must be Item + wchTypes[4]=L'i'; + r->addShapedRecipy(new ItemInstance(target), + wchTypes, + shapes[t], + + L'#', Item::stick, + L'X', pObjMaterial->item, + L'T'); + } + } + } + + /* 4J-PB - moved out to main recipes so we can avoid them stacking on the group display name + r->addShapedRecipy(new ItemInstance(Item::bow, 1), // + L"ssscicig", + L" #X", // + L"# X", // + L" #X", // + + L'X', Item::string,// + L'#', Item::stick, + L'T'); + + r->addShapedRecipy(new ItemInstance(Item::arrow, 4), // + L"ssscicicig", + L"X", // + L"#", // + L"Y", // + + L'Y', Item::feather,// + L'X', Item::flint,// + L'#', Item::stick, + L'T'); + */ +} \ No newline at end of file diff --git a/Minecraft.World/WeaponRecipies.h b/Minecraft.World/WeaponRecipies.h new file mode 100644 index 00000000..105fba45 --- /dev/null +++ b/Minecraft.World/WeaponRecipies.h @@ -0,0 +1,22 @@ +//package net.minecraft.world.item.crafting; + +//import net.minecraft.world.item.*; +//import net.minecraft.world.level.tile.Tile; + +#pragma once + +#define MAX_WEAPON_RECIPES 2 +class WeaponRecipies +{ +public: + // 4J - added for common ctor code + void _init(); + WeaponRecipies() {_init();} + +private: + static wstring shapes[][4]; + vector *map; + +public: + void addRecipes(Recipes *r); +}; diff --git a/Minecraft.World/WebMaterial.h b/Minecraft.World/WebMaterial.h new file mode 100644 index 00000000..8a8bebf8 --- /dev/null +++ b/Minecraft.World/WebMaterial.h @@ -0,0 +1,9 @@ +#include "Material.h" + +// 4J added, Java version just does a local alteration when instantiating the Material for webs to get the same thing +class WebMaterial : public Material +{ +public: + WebMaterial(MaterialColor *color) : Material(color) {} + virtual bool blocksMotion() { return false; } +}; \ No newline at end of file diff --git a/Minecraft.World/WebTile.cpp b/Minecraft.World/WebTile.cpp new file mode 100644 index 00000000..a724c36d --- /dev/null +++ b/Minecraft.World/WebTile.cpp @@ -0,0 +1,53 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.item.h" +#include "WebTile.h" + +WebTile::WebTile(int id) : Tile(id, Material::web) +{ +} + + +void WebTile::entityInside(Level *level, int x, int y, int z, shared_ptr entity) +{ + entity->makeStuckInWeb(); +} + + +bool WebTile::isSolidRender(bool isServerLevel) +{ + return false; +} + + +AABB *WebTile::getAABB(Level *level, int x, int y, int z) +{ + return NULL; +} + + +int WebTile::getRenderShape() +{ + return Tile::SHAPE_CROSS_TEXTURE; +} + +bool WebTile::blocksLight() +{ + return false; +} + +bool WebTile::isCubeShaped() +{ + return false; +} + +int WebTile::getResource(int data, Random *random, int playerBonusLevel) +{ + // @TODO: Explosives currently also give string back. Fix? + return Item::string->id; +} + +bool WebTile::isSilkTouchable() +{ + return true; +} \ No newline at end of file diff --git a/Minecraft.World/WebTile.h b/Minecraft.World/WebTile.h new file mode 100644 index 00000000..ca1f4dae --- /dev/null +++ b/Minecraft.World/WebTile.h @@ -0,0 +1,31 @@ +#pragma once + +#include "Tile.h" + +class WebTile : public Tile +{ + +public: + WebTile(int id); + + +public: + void entityInside(Level *level, int x, int y, int z, shared_ptr entity); + + +public: + bool isSolidRender(bool isServerLevel = false); + +public: + AABB *getAABB(Level *level, int x, int y, int z); + +public: + int getRenderShape(); + + bool blocksLight(); + bool isCubeShaped(); + virtual int getResource(int data, Random *random, int playerBonusLevel); + +protected: + bool isSilkTouchable(); +}; \ No newline at end of file diff --git a/Minecraft.World/WeighedRandom.cpp b/Minecraft.World/WeighedRandom.cpp new file mode 100644 index 00000000..8839b0c5 --- /dev/null +++ b/Minecraft.World/WeighedRandom.cpp @@ -0,0 +1,72 @@ +#include "stdafx.h" +#include "WeighedRandom.h" + +int WeighedRandom::getTotalWeight(vector *items) +{ + int totalWeight = 0; + for( AUTO_VAR(it, items->begin()); it != items->end(); it++ ) + { + totalWeight += (*it)->randomWeight; + } + return totalWeight; +} + +WeighedRandomItem *WeighedRandom::getRandomItem(Random *random, vector *items, int totalWeight) +{ + if (totalWeight <= 0) + { + __debugbreak(); + } + + int selection = random->nextInt(totalWeight); + + for( AUTO_VAR(it, items->begin()); it != items->end(); it++ ) + { + selection -= (*it)->randomWeight; + if (selection < 0) + { + return *it; + } + } + return NULL; +} + +WeighedRandomItem *WeighedRandom::getRandomItem(Random *random, vector *items) +{ + return getRandomItem(random, items, getTotalWeight(items)); +} + +int WeighedRandom::getTotalWeight(WeighedRandomItemArray items) +{ + int totalWeight = 0; + for( unsigned int i = 0; i < items.length; i++ ) + { + totalWeight += items[i]->randomWeight; + } + return totalWeight; +} + +WeighedRandomItem *WeighedRandom::getRandomItem(Random *random, WeighedRandomItemArray items, int totalWeight) +{ + if (totalWeight <= 0) + { + __debugbreak(); + } + + int selection = random->nextInt(totalWeight); + for( unsigned int i = 0; i < items.length; i++ ) + { + selection -= items[i]->randomWeight; + if (selection < 0) + { + return items[i]; + } + } + return NULL; +} + + +WeighedRandomItem *WeighedRandom::getRandomItem(Random *random, WeighedRandomItemArray items) +{ + return getRandomItem(random, items, getTotalWeight(items)); +} \ No newline at end of file diff --git a/Minecraft.World/WeighedRandom.h b/Minecraft.World/WeighedRandom.h new file mode 100644 index 00000000..71066c22 --- /dev/null +++ b/Minecraft.World/WeighedRandom.h @@ -0,0 +1,28 @@ +#pragma once +// 4J - this WeighedRandomItem class was a nested static class within WeighedRandom, but we need to be able to refer to it externally + +class WeighedRandomItem +{ + friend class WeighedRandom; +protected: + int randomWeight; + +public: + WeighedRandomItem(int randomWeight) + { + this->randomWeight = randomWeight; + } +}; + +class WeighedRandom +{ +public: + // 4J - vectors here were Collection + static int getTotalWeight(vector *items); + static WeighedRandomItem *getRandomItem(Random *random, vector *items, int totalWeight); + static WeighedRandomItem *getRandomItem(Random *random, vector *items); + static int getTotalWeight(WeighedRandomItemArray items); + static WeighedRandomItem *getRandomItem(Random *random, WeighedRandomItemArray items, int totalWeight); + static WeighedRandomItem *getRandomItem(Random *random, WeighedRandomItemArray items); + +}; diff --git a/Minecraft.World/WeighedTreasure.cpp b/Minecraft.World/WeighedTreasure.cpp new file mode 100644 index 00000000..d7044644 --- /dev/null +++ b/Minecraft.World/WeighedTreasure.cpp @@ -0,0 +1,86 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.item.h" +#include "WeighedRandom.h" +#include "WeighedTreasure.h" + +WeighedTreasure::WeighedTreasure(int itemId, int auxValue, int minCount, int maxCount, int weight) : WeighedRandomItem(weight) +{ + this->item = shared_ptr( new ItemInstance(itemId, 1, auxValue) ); + this->minCount = minCount; + this->maxCount = maxCount; +} + +WeighedTreasure::WeighedTreasure(shared_ptr item, int minCount, int maxCount, int weight) : WeighedRandomItem(weight) +{ + this->item = item; + this->minCount = minCount; + this->maxCount = maxCount; +} + +void WeighedTreasure::addChestItems(Random *random, WeighedTreasureArray items, shared_ptr dest, int numRolls) +{ + for (int r = 0; r < numRolls; r++) + { + WeighedTreasure *treasure = (WeighedTreasure *) WeighedRandom::getRandomItem(random, *((WeighedRandomItemArray *)&items)); + + int count = treasure->minCount + random->nextInt(treasure->maxCount - treasure->minCount + 1); + if (treasure->item->getMaxStackSize() >= count) + { + shared_ptr copy = treasure->item->copy(); + copy->count = count; + dest->setItem(random->nextInt(dest->getContainerSize()), copy); + } + else + { + // use multiple slots + for (int c = 0; c < count; c++) + { + shared_ptr copy = treasure->item->copy(); + copy->count = 1; + dest->setItem(random->nextInt(dest->getContainerSize()), copy); + } + } + } +} + +void WeighedTreasure::addDispenserItems(Random *random, WeighedTreasureArray items, shared_ptr dest, int numRolls) +{ + for (int r = 0; r < numRolls; r++) + { + WeighedTreasure *treasure = (WeighedTreasure *) WeighedRandom::getRandomItem(random, *((WeighedRandomItemArray *)&items)); + + int count = treasure->minCount + random->nextInt(treasure->maxCount - treasure->minCount + 1); + if (treasure->item->getMaxStackSize() >= count) + { + shared_ptr copy = treasure->item->copy(); + copy->count = count; + dest->setItem(random->nextInt(dest->getContainerSize()), copy); + } + else + { + // use multiple slots + for (int c = 0; c < count; c++) + { + shared_ptr copy = treasure->item->copy(); + copy->count = 1; + dest->setItem(random->nextInt(dest->getContainerSize()), copy); + } + } + } +} + +WeighedTreasureArray WeighedTreasure::addToTreasure(WeighedTreasureArray items, WeighedTreasure *extra) +{ + WeighedTreasureArray result(items.length + 1); + int i = 0; + + for (int j = 0; j < items.length; j++) + { + result[i++] = items[j]; + } + + result[i++] = extra; + + return result; +} \ No newline at end of file diff --git a/Minecraft.World/WeighedTreasure.h b/Minecraft.World/WeighedTreasure.h new file mode 100644 index 00000000..a37bc6ae --- /dev/null +++ b/Minecraft.World/WeighedTreasure.h @@ -0,0 +1,19 @@ +#pragma once + +#include "WeighedRandom.h" + +class WeighedTreasure : public WeighedRandomItem +{ +private: + shared_ptr item; + int minCount; + int maxCount; + +public: + WeighedTreasure(int itemId, int auxValue, int minCount, int maxCount, int weight); + WeighedTreasure(shared_ptr item, int minCount, int maxCount, int weight); + + static void addChestItems(Random *random, WeighedTreasureArray items, shared_ptr dest, int numRolls); + static void addDispenserItems(Random *random, WeighedTreasureArray items, shared_ptr dest, int numRolls); + static WeighedTreasureArray addToTreasure(WeighedTreasureArray items, WeighedTreasure *extra); +}; \ No newline at end of file diff --git a/Minecraft.World/Wolf.cpp b/Minecraft.World/Wolf.cpp new file mode 100644 index 00000000..8ffc45c6 --- /dev/null +++ b/Minecraft.World/Wolf.cpp @@ -0,0 +1,550 @@ +#include "stdafx.h" +#include "com.mojang.nbt.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.ai.goal.target.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.entity.projectile.h" +#include "net.minecraft.world.level.pathfinder.h" +#include "net.minecraft.world.damagesource.h" +#include "net.minecraft.stats.h" +#include "Sheep.h" +#include "Wolf.h" +#include "..\Minecraft.Client\Textures.h" +#include "SoundTypes.h" + + + +Wolf::Wolf(Level *level) : TamableAnimal( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + interestedAngle = interestedAngleO = 0.0f; + m_isWet = isShaking = false; + shakeAnim = shakeAnimO = 0.0f; + + this->textureIdx = TN_MOB_WOLF; // 4J - was L"/mob/wolf.png"; + this->setSize(0.60f, 0.8f); + runSpeed = 0.3f; + + getNavigation()->setAvoidWater(true); + goalSelector.addGoal(1, new FloatGoal(this)); + goalSelector.addGoal(2, sitGoal, false); + goalSelector.addGoal(3, new LeapAtTargetGoal(this, 0.4f)); + goalSelector.addGoal(4, new MeleeAttackGoal(this, runSpeed, true)); + goalSelector.addGoal(5, new FollowOwnerGoal(this, runSpeed, 10, 2)); + goalSelector.addGoal(6, new BreedGoal(this, runSpeed)); + goalSelector.addGoal(7, new RandomStrollGoal(this, runSpeed)); + goalSelector.addGoal(8, new BegGoal(this, 8)); + goalSelector.addGoal(9, new LookAtPlayerGoal(this, typeid(Player), 8)); + goalSelector.addGoal(9, new RandomLookAroundGoal(this)); + + targetSelector.addGoal(1, new OwnerHurtByTargetGoal(this)); + targetSelector.addGoal(2, new OwnerHurtTargetGoal(this)); + targetSelector.addGoal(3, new HurtByTargetGoal(this, true)); + targetSelector.addGoal(4, new NonTameRandomTargetGoal(this, typeid(Sheep), 16, 200, false)); +} + +bool Wolf::useNewAi() +{ + return true; +} + +void Wolf::setTarget(shared_ptr target) +{ + TamableAnimal::setTarget(target); + if ( dynamic_pointer_cast(target) == NULL ) + { + setAngry(false); + } + else if(!isTame()) + { + setAngry(true); + } +} + +void Wolf::serverAiMobStep() +{ + entityData->set(DATA_HEALTH_ID, getHealth()); +} + +int Wolf::getMaxHealth() +{ + if (isTame()) + { + return TAME_HEALTH; + } + return START_HEALTH; +} + +void Wolf::defineSynchedData() +{ + TamableAnimal::defineSynchedData(); + entityData->define(DATA_HEALTH_ID, getHealth()); + entityData->define(DATA_INTERESTED_ID, (byte)0); + entityData->define(DATA_COLLAR_COLOR, (byte) ClothTile::getTileDataForItemAuxValue(DyePowderItem::RED)); +} + +bool Wolf::makeStepSound() +{ + return false; +} + +int Wolf::getTexture() +{ + if (isTame()) + { + return TN_MOB_WOLF_TAME; // 4J was L"/mob/wolf_tame.png"; + } + if (isAngry()) + { + return TN_MOB_WOLF_ANGRY; // 4J was L"/mob/wolf_angry.png"; + } + return TamableAnimal::getTexture(); +} + +void Wolf::addAdditonalSaveData(CompoundTag *tag) +{ + TamableAnimal::addAdditonalSaveData(tag); + + tag->putBoolean(L"Angry", isAngry()); + tag->putByte(L"CollarColor", (byte) getCollarColor()); +} + +void Wolf::readAdditionalSaveData(CompoundTag *tag) +{ + TamableAnimal::readAdditionalSaveData(tag); + + setAngry(tag->getBoolean(L"Angry")); + if (tag->contains(L"CollarColor")) setCollarColor(tag->getByte(L"CollarColor")); +} + +bool Wolf::removeWhenFarAway() +{ + return !isTame(); +} + +int Wolf::getAmbientSound() +{ + if (isAngry()) + { + return eSoundType_MOB_WOLF_GROWL; + } + if (random->nextInt(3) == 0) + { + if (isTame() && entityData->getInteger(DATA_HEALTH_ID) < 10) + { + return eSoundType_MOB_WOLF_WHINE; + } + return eSoundType_MOB_WOLF_PANTING; + } + return eSoundType_MOB_WOLF_BARK; +} + +int Wolf::getHurtSound() +{ + return eSoundType_MOB_WOLF_HURT; +} + +int Wolf::getDeathSound() +{ + return eSoundType_MOB_WOLF_DEATH; +} + +float Wolf::getSoundVolume() +{ + return 0.4f; +} + +int Wolf::getDeathLoot() +{ + return -1; +} + +void Wolf::aiStep() +{ + TamableAnimal::aiStep(); + + if (!level->isClientSide && m_isWet && !isShaking && !isPathFinding() && onGround) + { + isShaking = true; + shakeAnim = 0; + shakeAnimO = 0; + + level->broadcastEntityEvent(shared_from_this(), EntityEvent::SHAKE_WETNESS); + } +} + +void Wolf::tick() +{ + TamableAnimal::tick(); + + interestedAngleO = interestedAngle; + if (isInterested()) + { + interestedAngle = interestedAngle + (1 - interestedAngle) * 0.4f; + } + else + { + interestedAngle = interestedAngle + (0 - interestedAngle) * 0.4f; + } + if (isInterested()) + { + lookTime = 10; + } + + if (isInWaterOrRain()) + { + m_isWet = true; + isShaking = false; + shakeAnim = 0; + shakeAnimO = 0; + } + else if (m_isWet || isShaking) + { + if (isShaking) + { + + if (shakeAnim == 0) + { + level->playSound(shared_from_this(), eSoundType_MOB_WOLF_SHAKE, getSoundVolume(), (random->nextFloat() - random->nextFloat()) * 0.2f + 1.0f); + } + + shakeAnimO = shakeAnim; + shakeAnim += 0.05f; + + if (shakeAnimO >= 2) + { + m_isWet = false; + isShaking = false; + shakeAnimO = 0; + shakeAnim = 0; + } + + if (shakeAnim > 0.4f) + { + float yt = (float) bb->y0; + int shakeCount = (int) (Mth::sin((shakeAnim - 0.4f) * PI) * 7.0f); + for (int i = 0; i < shakeCount; i++) + { + float xo = (random->nextFloat() * 2 - 1) * bbWidth * 0.5f; + float zo = (random->nextFloat() * 2 - 1) * bbWidth * 0.5f; + level->addParticle(eParticleType_splash, x + xo, yt + 0.8f, z + zo, xd, yd, zd); + } + } + } + } +} + +bool Wolf::isWet() +{ + return m_isWet; +} + +float Wolf::getWetShade(float a) +{ + return 0.75f + ((shakeAnimO + (shakeAnim - shakeAnimO) * a) / 2.0f) * 0.25f; +} + +float Wolf::getBodyRollAngle(float a, float offset) +{ + float progress = ((shakeAnimO + (shakeAnim - shakeAnimO) * a) + offset) / 1.8f; + if (progress < 0) + { + progress = 0; + } + else if (progress > 1) + { + progress = 1; + } + return Mth::sin(progress * PI) * Mth::sin(progress * PI * 11.0f) * 0.15f * PI; +} + +float Wolf::getHeadRollAngle(float a) +{ + return (interestedAngleO + (interestedAngle - interestedAngleO) * a) * 0.15f * PI; +} + +float Wolf::getHeadHeight() +{ + return bbHeight * 0.8f; +} + +int Wolf::getMaxHeadXRot() +{ + if (isSitting()) + { + return 20; + } + return TamableAnimal::getMaxHeadXRot(); +} + +bool Wolf::hurt(DamageSource *source, int dmg) +{ + if (isInvulnerable()) return false; + shared_ptr sourceEntity = source->getEntity(); + sitGoal->wantToSit(false); + if (sourceEntity != NULL && !(dynamic_pointer_cast(sourceEntity) != NULL || dynamic_pointer_cast(sourceEntity) != NULL)) + { + // take half damage from non-players and arrows + dmg = (dmg + 1) / 2; + } + return TamableAnimal::hurt(source, dmg); +} + +bool Wolf::doHurtTarget(shared_ptr target) +{ + int damage = isTame() ? 4 : 2; + return target->hurt(DamageSource::mobAttack(dynamic_pointer_cast(shared_from_this())), damage); +} + +void Wolf::tame(const wstring &wsOwnerUUID, bool bDisplayTamingParticles, bool bSetSitting) +{ + setTame(true); + setPath(NULL); + setTarget(nullptr); + sitGoal->wantToSit(bSetSitting); + setHealth(TAME_HEALTH); + + setOwnerUUID(wsOwnerUUID); + + // We'll not show the taming particles if this is a baby wolf + spawnTamingParticles(bDisplayTamingParticles); +} + +bool Wolf::interact(shared_ptr player) +{ + shared_ptr item = player->inventory->getSelected(); + + if (isTame()) + { + if (item != NULL) + { + if(dynamic_cast(Item::items[item->id]) != NULL) + { + FoodItem *food = dynamic_cast( Item::items[item->id] ); + + if (food->isMeat()) + { + if(entityData->getInteger(DATA_HEALTH_ID) < MAX_HEALTH) + { + heal(food->getNutrition()); + // 4J-PB - don't lose the bone in creative mode + if (player->abilities.instabuild==false) + { + item->count--; + if (item->count <= 0) + { + player->inventory->setItem(player->inventory->selected, nullptr); + } + } + return true; + } + else return TamableAnimal::interact(player); + } + } + else if (item->id == Item::dye_powder_Id) + { + int color = ClothTile::getTileDataForItemAuxValue(item->getAuxValue()); + if (color != getCollarColor()) + { + setCollarColor(color); + + if (!player->abilities.instabuild && --item->count <= 0) + { + player->inventory->setItem(player->inventory->selected, nullptr); + } + + return true; + } + } + } + if (equalsIgnoreCase(player->getUUID(), getOwnerUUID())) + { + if (!level->isClientSide && !isFood(item)) + { + sitGoal->wantToSit(!isSitting()); + jumping = false; + setPath(NULL); + } + } + } + else + { + if (item != NULL && item->id == Item::bone->id && !isAngry()) + { + // 4J-PB - don't lose the bone in creative mode + if (player->abilities.instabuild==false) + { + item->count--; + if (item->count <= 0) + { + player->inventory->setItem(player->inventory->selected, nullptr); + } + } + + if (!level->isClientSide) + { + if (random->nextInt(3) == 0) + { + // 4J : WESTY: Added for new acheivements. + player->awardStat(GenericStats::tamedEntity(eTYPE_WOLF),GenericStats::param_tamedEntity(eTYPE_WOLF)); + + // 4J Changed to this + tame(player->getUUID(),true,true); + + level->broadcastEntityEvent(shared_from_this(), EntityEvent::TAMING_SUCCEEDED); + } + else + { + spawnTamingParticles(false); + level->broadcastEntityEvent(shared_from_this(), EntityEvent::TAMING_FAILED); + } + } + + return true; + } + + // 4J-PB - stop wild wolves going in to Love Mode (even though they do on Java, but don't breed) + if((item != NULL) && isFood(item)) + { + return false; + } + } + return TamableAnimal::interact(player); +} + +void Wolf::handleEntityEvent(byte id) +{ + if (id == EntityEvent::SHAKE_WETNESS) + { + isShaking = true; + shakeAnim = 0; + shakeAnimO = 0; + } + else + { + TamableAnimal::handleEntityEvent(id); + } +} + +float Wolf::getTailAngle() +{ + if (isAngry()) + { + return 0.49f * PI; + } + else if (isTame()) + { + return (0.55f - (MAX_HEALTH - entityData->getInteger(DATA_HEALTH_ID)) * 0.02f) * PI; + } + return 0.20f * PI; +} + +bool Wolf::isFood(shared_ptr item) +{ + if (item == NULL) return false; + if (dynamic_cast(Item::items[item->id]) == NULL) return false; + return ((FoodItem *) Item::items[item->id])->isMeat(); +} + +int Wolf::getMaxSpawnClusterSize() +{ + // 4J - changed - was 8 but we have a limit of only 8 wolves in the world so doesn't seem right potentially spawning them all in once cluster + return 4; +} + +bool Wolf::isAngry() +{ + return (entityData->getByte(DATA_FLAGS_ID) & 0x02) != 0; +} + +void Wolf::setAngry(bool value) +{ + byte current = entityData->getByte(DATA_FLAGS_ID); + if (value) + { + entityData->set(DATA_FLAGS_ID, (byte) (current | 0x02)); + } + else + { + entityData->set(DATA_FLAGS_ID, (byte) (current & ~0x02)); + } +} + +int Wolf::getCollarColor() +{ + return entityData->getByte(DATA_COLLAR_COLOR) & 0xF; +} + +void Wolf::setCollarColor(int color) +{ + entityData->set(DATA_COLLAR_COLOR, (byte) (color & 0xF)); +} + +// 4J-PB added for tooltips +int Wolf::GetSynchedHealth() +{ + return getEntityData()->getInteger(DATA_HEALTH_ID); +} + +shared_ptr Wolf::getBreedOffspring(shared_ptr target) +{ + // 4J - added limit to wolves that can be bred + if( level->canCreateMore( GetType(), Level::eSpawnType_Breed) ) + { + shared_ptr pBabyWolf = shared_ptr( new Wolf(level) ); + + if(!getOwnerUUID().empty()) + { + // set the baby wolf to be tame, and assign the owner + pBabyWolf->tame(getOwnerUUID(),false,false); + } + return pBabyWolf; + } + else + { + return nullptr; + } +} + +void Wolf::setIsInterested(bool value) +{ + //byte current = entityData->getByte(DATA_INTERESTED_ID); + + if (value) + { + entityData->set(DATA_INTERESTED_ID, (byte) 1); + } + else + { + entityData->set(DATA_INTERESTED_ID, (byte) 0); + } +} + +bool Wolf::canMate(shared_ptr animal) +{ + if (animal == shared_from_this()) return false; + if (!isTame()) return false; + shared_ptr partner = dynamic_pointer_cast(animal); + if (partner == NULL) return false; + if (!partner->isTame()) return false; + if (partner->isSitting()) return false; + + return isInLove() && partner->isInLove(); +} + +bool Wolf::isInterested() +{ + return entityData->getByte(DATA_INTERESTED_ID) == 1; +} diff --git a/Minecraft.World/Wolf.h b/Minecraft.World/Wolf.h new file mode 100644 index 00000000..52f2a23b --- /dev/null +++ b/Minecraft.World/Wolf.h @@ -0,0 +1,86 @@ +#pragma once + +using namespace std; + +#include "TamableAnimal.h" +class DamageSource; + +class Wolf : public TamableAnimal +{ +public: + eINSTANCEOF GetType() { return eTYPE_WOLF; } + static Entity *create(Level *level) { return new Wolf(level); } +private: + // synch health in a separate field to show tame wolves' health + static const int DATA_HEALTH_ID = 18; + static const int DATA_INTERESTED_ID = 19; + static const int DATA_COLLAR_COLOR = 20; + static const int START_HEALTH = 8; + static const int MAX_HEALTH = 20; + static const int TAME_HEALTH = 20; + + float interestedAngle, interestedAngleO; + bool m_isWet, isShaking; + float shakeAnim, shakeAnimO; + +public: + Wolf(Level *level); + virtual bool useNewAi(); + virtual void setTarget(shared_ptr target); + +protected: + virtual void serverAiMobStep(); + +public: + virtual int getMaxHealth(); + +protected: + virtual void defineSynchedData(); + virtual bool makeStepSound(); + +public: + virtual int getTexture(); // 4J - changed from wstring to ing + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + +protected: + virtual bool removeWhenFarAway(); + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual float getSoundVolume(); + virtual int getDeathLoot(); + +public: + virtual void aiStep(); + virtual void tick(); + bool isWet(); + float getWetShade(float a); + float getBodyRollAngle(float a, float offset); + float getHeadRollAngle(float a); + float getHeadHeight(); + int getMaxHeadXRot(); + virtual bool hurt(DamageSource *source, int dmg); + virtual bool doHurtTarget(shared_ptr target); + virtual bool interact(shared_ptr player); + virtual void handleEntityEvent(byte id); + float getTailAngle(); + virtual bool isFood(shared_ptr item); + virtual int getMaxSpawnClusterSize(); + bool isAngry(); + void setAngry(bool value); + int getCollarColor(); + void setCollarColor(int color); + void tame(const wstring &wsOwnerUUID, bool bDisplayTamingParticles, bool bSetSitting); + + // For tooltips + int GetSynchedHealth(); + +protected: + virtual shared_ptr getBreedOffspring(shared_ptr target); + +public: + virtual void setIsInterested(bool isInterested); + virtual bool canMate(shared_ptr animal); + bool isInterested(); +}; diff --git a/Minecraft.World/WoodSlabTile.cpp b/Minecraft.World/WoodSlabTile.cpp new file mode 100644 index 00000000..ba3a98e5 --- /dev/null +++ b/Minecraft.World/WoodSlabTile.cpp @@ -0,0 +1,60 @@ +#include "stdafx.h" +#include "WoodSlabTile.h" +#include "woodtile.h" +#include "treetile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.stats.h" + +const unsigned int WoodSlabTile::SLAB_NAMES[SLAB_NAMES_LENGTH] = { IDS_TILE_STONESLAB_OAK, + IDS_TILE_STONESLAB_SPRUCE, + IDS_TILE_STONESLAB_BIRCH, + IDS_TILE_STONESLAB_JUNGLE, +}; + +// public static final String[] WOOD_NAMES = { +// "oak", "spruce", "birch", "jungle" +// }; + +WoodSlabTile::WoodSlabTile(int id, bool fullSize) : HalfSlabTile(id, fullSize, Material::wood) +{ +} + +Icon *WoodSlabTile::getTexture(int face, int data) +{ + return Tile::wood->getTexture(face, data & TYPE_MASK); +} + +int WoodSlabTile::getResource(int data, Random *random, int playerBonusLevel) +{ + return Tile::woodSlabHalf_Id; +} + +shared_ptr WoodSlabTile::getSilkTouchItemInstance(int data) +{ + return shared_ptr(new ItemInstance(Tile::woodSlabHalf, 2, data & TYPE_MASK)); +} + +int WoodSlabTile::getAuxName(int auxValue) +{ + if (auxValue < 0 || auxValue >= SLAB_NAMES_LENGTH) + { + auxValue = 0; + } + return SLAB_NAMES[auxValue];//super.getDescriptionId() + "." + SLAB_NAMES[auxValue]; +} + +void WoodSlabTile::registerIcons(IconRegister *iconRegister) +{ + // None +} + +unsigned int WoodSlabTile::getDescriptionId(int iData) +{ + if (iData < 0 || iData >= SLAB_NAMES_LENGTH) + { + iData = 0; + } + return SLAB_NAMES[iData]; +} \ No newline at end of file diff --git a/Minecraft.World/WoodSlabTile.h b/Minecraft.World/WoodSlabTile.h new file mode 100644 index 00000000..b91f7136 --- /dev/null +++ b/Minecraft.World/WoodSlabTile.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Tile.h" +#include "HalfSlabTile.h" + +class Player; + +class WoodSlabTile : HalfSlabTile +{ + + friend class Tile; +public: + static const int TYPE_MASK = 7; + static const int TOP_SLOT_BIT = 8; + static const int SLAB_NAMES_LENGTH = 4; + static const unsigned int SLAB_NAMES[SLAB_NAMES_LENGTH]; + + WoodSlabTile(int id, bool fullSize); + virtual Icon *getTexture(int face, int data); + virtual int getResource(int data, Random *random, int playerBonusLevel); + virtual int getAuxName(int auxValue); + + virtual shared_ptr getSilkTouchItemInstance(int data); + void registerIcons(IconRegister *iconRegister); + + // 4J added + virtual unsigned int getDescriptionId(int iData = -1); +}; \ No newline at end of file diff --git a/Minecraft.World/WoodTile.cpp b/Minecraft.World/WoodTile.cpp new file mode 100644 index 00000000..43101d58 --- /dev/null +++ b/Minecraft.World/WoodTile.cpp @@ -0,0 +1,56 @@ +#include "stdafx.h" +#include "WoodTile.h" +#include "TreeTile.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.biome.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.stats.h" +#include "net.minecraft.world.h" + +const unsigned int WoodTile::WOOD_NAMES[WOOD_NAMES_LENGTH] = { IDS_TILE_OAKWOOD_PLANKS, + IDS_TILE_SPRUCEWOOD_PLANKS, + IDS_TILE_BIRCHWOOD_PLANKS, + IDS_TILE_JUNGLE_PLANKS, +}; + +const wstring WoodTile::TEXTURE_NAMES[] = {L"wood", L"wood_spruce", L"wood_birch", L"wood_jungle"}; + +// public static final String[] WOOD_NAMES = { +// "oak", "spruce", "birch", "jungle" +// }; + +WoodTile::WoodTile(int id) : Tile(id, Material::wood) +{ + icons = NULL; +} + +unsigned int WoodTile::getDescriptionId(int iData) +{ + if(iData < 0 || iData >= WOOD_NAMES_LENGTH) iData = 0; + + return WOOD_NAMES[iData]; +} + +Icon *WoodTile::getTexture(int face, int data) +{ + if (data < 0 || data >= WOOD_NAMES_LENGTH) + { + data = 0; + } + return icons[data]; +} + +int WoodTile::getSpawnResourcesAuxValue(int data) +{ + return data; +} + +void WoodTile::registerIcons(IconRegister *iconRegister) +{ + icons = new Icon*[WOOD_NAMES_LENGTH]; + + for (int i = 0; i < WOOD_NAMES_LENGTH; i++) + { + icons[i] = iconRegister->registerIcon(TEXTURE_NAMES[i]); + } +} \ No newline at end of file diff --git a/Minecraft.World/WoodTile.h b/Minecraft.World/WoodTile.h new file mode 100644 index 00000000..23bbdfb1 --- /dev/null +++ b/Minecraft.World/WoodTile.h @@ -0,0 +1,26 @@ +#pragma once +#include "Tile.h" + +class ChunkRebuildData; + +class WoodTile : public Tile +{ +friend class ChunkRebuildData; +public: + static const int WOOD_NAMES_LENGTH = 4; + + + static const unsigned int WOOD_NAMES[WOOD_NAMES_LENGTH]; + static const wstring TEXTURE_NAMES[]; + +private: + Icon **icons; + +public: + WoodTile(int id); + virtual unsigned int getDescriptionId(int iData = -1); + virtual Icon *getTexture(int face, int data) ; + virtual int getSpawnResourcesAuxValue(int data); + + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/WoolCarpetTile.cpp b/Minecraft.World/WoolCarpetTile.cpp new file mode 100644 index 00000000..16a3d0aa --- /dev/null +++ b/Minecraft.World/WoolCarpetTile.cpp @@ -0,0 +1,111 @@ +#include "stdafx.h" +#include "net.minecraft.world.phys.h" +#include "net.minecraft.world.level.h" +#include "SharedConstants.h" +#include "WoolCarpetTile.h" + +WoolCarpetTile::WoolCarpetTile(int id) : Tile(id, Material::clothDecoration, isSolidRender() ) +{ + setShape(0, 0, 0, 1, 1 / 16.0f, 1); + setTicking(true); + updateShape(0); +} + +Icon *WoolCarpetTile::getTexture(int face, int data) +{ + return Tile::cloth->getTexture(face, data); +} + +AABB *WoolCarpetTile::getAABB(Level *level, int x, int y, int z) +{ + int height = 0; + float offset = 1.0f / SharedConstants::WORLD_RESOLUTION; + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(Tile::tlsIdxShape); + // 4J Stu - Added this so that the TLS shape is correct for this tile + if(tls->tileId != this->id) updateDefaultShape(); + return AABB::newTemp(x + tls->xx0, y + tls->yy0, z + tls->zz0, x + tls->xx1, y + (height * offset), z + tls->zz1); +} + +bool WoolCarpetTile::blocksLight() +{ + return false; +} + +bool WoolCarpetTile::isSolidRender(bool isServerLevel) +{ + return false; +} + +bool WoolCarpetTile::isCubeShaped() +{ + return false; +} + +void WoolCarpetTile::updateDefaultShape() +{ + updateShape(0); +} + +void WoolCarpetTile::updateShape(LevelSource *level, int x, int y, int z, int forceData, shared_ptr forceEntity) +{ + updateShape(level->getData(x, y, z)); +} + +void WoolCarpetTile::updateShape(int data) +{ + int height = 0; + float o = 1 * (1 + height) / 16.0f; + setShape(0, 0, 0, 1, o, 1); +} + +bool WoolCarpetTile::mayPlace(Level *level, int x, int y, int z) +{ + return Tile::mayPlace(level, x, y, z) && canSurvive(level, x, y, z); +} + +void WoolCarpetTile::neighborChanged(Level *level, int x, int y, int z, int type) +{ + checkCanSurvive(level, x, y, z); +} + +bool WoolCarpetTile::checkCanSurvive(Level *level, int x, int y, int z) +{ + if (!canSurvive(level, x, y, z)) + { + spawnResources(level, x, y, z, level->getData(x, y, z), 0); + level->setTile(x, y, z, 0); + return false; + } + return true; +} + +bool WoolCarpetTile::canSurvive(Level *level, int x, int y, int z) +{ + return !level->isEmptyTile(x, y - 1, z); +} + +bool WoolCarpetTile::shouldRenderFace(LevelSource *level, int x, int y, int z, int face) +{ + if (face == 1) return true; + return Tile::shouldRenderFace(level, x, y, z, face); +} + +int WoolCarpetTile::getSpawnResourcesAuxValue(int data) +{ + return data; +} + +int WoolCarpetTile::getTileDataForItemAuxValue(int auxValue) +{ + return (~auxValue & 0xf); +} + +int WoolCarpetTile::getItemAuxValueForTileData(int data) +{ + return (~data & 0xf); +} + +void WoolCarpetTile::registerIcons(IconRegister *iconRegister) +{ + // None, delegates to cloth tile +} diff --git a/Minecraft.World/WoolCarpetTile.h b/Minecraft.World/WoolCarpetTile.h new file mode 100644 index 00000000..af0cd28a --- /dev/null +++ b/Minecraft.World/WoolCarpetTile.h @@ -0,0 +1,37 @@ +#pragma once + +#include "Tile.h" + +class WoolCarpetTile : public Tile +{ + friend class Tile; +protected: + WoolCarpetTile(int id); + +public: + Icon *getTexture(int face, int data); + AABB *getAABB(Level *level, int x, int y, int z); + bool blocksLight(); + bool isSolidRender(bool isServerLevel = false); + bool isCubeShaped(); + void updateDefaultShape(); + void updateShape(LevelSource *level, int x, int y, int z, int forceData = -1, shared_ptr forceEntity = shared_ptr()); + +protected: + void updateShape(int data); + +public: + bool mayPlace(Level *level, int x, int y, int z); + void neighborChanged(Level *level, int x, int y, int z, int type); + +private: + bool checkCanSurvive(Level *level, int x, int y, int z); + +public: + bool canSurvive(Level *level, int x, int y, int z); + bool shouldRenderFace(LevelSource *level, int x, int y, int z, int face); + int getSpawnResourcesAuxValue(int data); + static int getTileDataForItemAuxValue(int auxValue); + static int getItemAuxValueForTileData(int data); + void registerIcons(IconRegister *iconRegister); +}; \ No newline at end of file diff --git a/Minecraft.World/WorkbenchTile.cpp b/Minecraft.World/WorkbenchTile.cpp new file mode 100644 index 00000000..7af19755 --- /dev/null +++ b/Minecraft.World/WorkbenchTile.cpp @@ -0,0 +1,44 @@ +#include "stdafx.h" +#include "net.minecraft.world.entity.player.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.h" +#include "net.minecraft.h" +#include "WorkbenchTile.h" + +WorkbenchTile::WorkbenchTile(int id) : Tile(id, Material::wood) +{ + iconTop = NULL; + iconFront = NULL; +} + +Icon *WorkbenchTile::getTexture(int face, int data) +{ + if (face == Facing::UP) return iconTop; + if (face == Facing::DOWN) return Tile::wood->getTexture(face); + if (face == Facing::NORTH || face == Facing::WEST) return iconFront; + return icon; +} + +void WorkbenchTile::registerIcons(IconRegister *iconRegister) +{ + icon = iconRegister->registerIcon(L"workbench_side"); + iconTop = iconRegister->registerIcon(L"workbench_top"); + iconFront = iconRegister->registerIcon(L"workbench_front"); +} + +// 4J-PB - Adding a TestUse for tooltip display +bool WorkbenchTile::TestUse() +{ + return true; +} + +bool WorkbenchTile::use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly/*=false*/) // 4J added soundOnly param +{ + if( soundOnly ) return false; + if (level->isClientSide) + { + return true; + } + player->startCrafting(x, y, z); + return true; +} \ No newline at end of file diff --git a/Minecraft.World/WorkbenchTile.h b/Minecraft.World/WorkbenchTile.h new file mode 100644 index 00000000..1e7fd4ec --- /dev/null +++ b/Minecraft.World/WorkbenchTile.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Tile.h" + +class Player; +class ChunkRebuildData; + +class WorkbenchTile : public Tile +{ + friend class Tile; + friend class ChunkRebuildData; +private: + Icon *iconTop; + Icon *iconFront; + +protected: + WorkbenchTile(int id); + +public: + Icon *getTexture(int face, int data); + void registerIcons(IconRegister *iconRegister); + +public: + virtual bool TestUse(); + bool use(Level *level, int x, int y, int z, shared_ptr player, int clickedFace, float clickX, float clickY, float clickZ, bool soundOnly = false); // 4J added soundOnly param +}; \ No newline at end of file diff --git a/Minecraft.World/XZPacket.cpp b/Minecraft.World/XZPacket.cpp new file mode 100644 index 00000000..92a934f4 --- /dev/null +++ b/Minecraft.World/XZPacket.cpp @@ -0,0 +1,52 @@ +#include "stdafx.h" +#include +#include "InputOutputStream.h" +#include "net.minecraft.world.item.h" +#include "PacketListener.h" +#include "XZPacket.h" + + + +const int XZPacket::STRONGHOLD = 0; + +XZPacket::~XZPacket() +{ +} + +XZPacket::XZPacket() +{ + action = STRONGHOLD; + x = 0; + z = 0; +} + +XZPacket::XZPacket(char action, int x, int z) +{ + this->action = action; + this->x = x; + this->z = z; +} + +void XZPacket::handle(PacketListener *listener) +{ + listener->handleXZ(shared_from_this()); +} + +void XZPacket::read(DataInputStream *dis) //throws IOException +{ + action = dis->read(); + x = dis->readInt(); + z = dis->readInt(); +} + +void XZPacket::write(DataOutputStream *dos) // throws IOException +{ + dos->write(action); + dos->writeInt(x); + dos->writeInt(z); +} + +int XZPacket::getEstimatedSize() +{ + return 10; +} diff --git a/Minecraft.World/XZPacket.h b/Minecraft.World/XZPacket.h new file mode 100644 index 00000000..0c4115e0 --- /dev/null +++ b/Minecraft.World/XZPacket.h @@ -0,0 +1,30 @@ +#pragma once + +// 4J ADDED THIS PACKET + +using namespace std; + +#include "Packet.h" + +class XZPacket : public Packet, public enable_shared_from_this +{ +public: + static const int STRONGHOLD; + + char action; + int x; + int z; + + XZPacket(); + ~XZPacket(); + XZPacket(char action, int x, int z); + + virtual void handle(PacketListener *listener); + virtual void read(DataInputStream *dis); + virtual void write(DataOutputStream *dos); + virtual int getEstimatedSize(); + +public: + static shared_ptr create() { return shared_ptr(new XZPacket()); } + virtual int getId() { return 166; } +}; \ No newline at end of file diff --git a/Minecraft.World/Zombie.cpp b/Minecraft.World/Zombie.cpp new file mode 100644 index 00000000..b167635f --- /dev/null +++ b/Minecraft.World/Zombie.cpp @@ -0,0 +1,383 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.h" +#include "net.minecraft.world.h" +#include "net.minecraft.world.item.h" +#include "net.minecraft.world.effect.h" +#include "net.minecraft.world.entity.ai.goal.h" +#include "net.minecraft.world.entity.ai.goal.target.h" +#include "net.minecraft.world.entity.ai.navigation.h" +#include "net.minecraft.world.entity.npc.h" +#include "net.minecraft.world.entity.player.h" +#include "Zombie.h" +#include "GenericStats.h" +#include "..\Minecraft.Client\Textures.h" +#include "net.minecraft.world.entity.h" +#include "SoundTypes.h" + +Zombie::Zombie(Level *level) : Monster( level ) +{ + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that + // the derived version of the function is called + this->defineSynchedData(); + + // 4J Stu - This function call had to be moved here from the Entity ctor to ensure that the derived version of the function is called + health = getMaxHealth(); + + this->textureIdx = TN_MOB_ZOMBIE; // 4J was L"/mob/zombie.png"; + runSpeed = 0.23f; + attackDamage = 4; + + villagerConversionTime = 0; + + registeredBBWidth = -1; + registeredBBHeight = 0; + + setSize(bbWidth, bbHeight); + + getNavigation()->setCanOpenDoors(true); + goalSelector.addGoal(0, new FloatGoal(this)); + goalSelector.addGoal(1, new BreakDoorGoal(this)); + goalSelector.addGoal(2, new MeleeAttackGoal(this, eTYPE_PLAYER, runSpeed, false)); + goalSelector.addGoal(3, new MeleeAttackGoal(this, eTYPE_VILLAGER, runSpeed, true)); + goalSelector.addGoal(4, new MoveTowardsRestrictionGoal(this, runSpeed)); + goalSelector.addGoal(5, new MoveThroughVillageGoal(this, runSpeed, false)); + goalSelector.addGoal(6, new RandomStrollGoal(this, runSpeed)); + goalSelector.addGoal(7, new LookAtPlayerGoal(this, typeid(Player), 8)); + goalSelector.addGoal(7, new RandomLookAroundGoal(this)); + + targetSelector.addGoal(1, new HurtByTargetGoal(this, false)); + targetSelector.addGoal(2, new NearestAttackableTargetGoal(this, typeid(Player), 16, 0, true)); + targetSelector.addGoal(2, new NearestAttackableTargetGoal(this, typeid(Villager), 16, 0, false)); +} + +void Zombie::defineSynchedData() +{ + Monster::defineSynchedData(); + + getEntityData()->define(DATA_BABY_ID, (byte) 0); + getEntityData()->define(DATA_VILLAGER_ID, (byte) 0); + getEntityData()->define(DATA_CONVERTING_ID, (byte) 0); +} + +float Zombie::getWalkingSpeedModifier() +{ + return Monster::getWalkingSpeedModifier() * (isBaby() ? 1.5f : 1.0f); +} + +int Zombie::getTexture() +{ + return isVillager() ? TN_MOB_ZOMBIE_VILLAGER : TN_MOB_ZOMBIE; +} + +int Zombie::getMaxHealth() +{ + return 20; +} + +int Zombie::getArmorValue() +{ + return 2; +} + +bool Zombie::useNewAi() +{ + return true; +} + +bool Zombie::isBaby() +{ + return getEntityData()->getByte(DATA_BABY_ID) == (byte) 1; +} + +void Zombie::setBaby(bool baby) +{ + getEntityData()->set(DATA_BABY_ID, (byte) 1); + updateSize(isBaby()); +} + +bool Zombie::isVillager() +{ + return getEntityData()->getByte(DATA_VILLAGER_ID) == (byte) 1; +} + +void Zombie::setVillager(bool villager) +{ + getEntityData()->set(DATA_VILLAGER_ID, (byte) (villager ? 1 : 0)); +} + +void Zombie::aiStep() +{ + if(level->isClientSide) + { + updateSize(isBaby()); + } + else if (level->isDay() && !level->isClientSide && !isBaby()) + { + float br = getBrightness(1); + if (br > 0.5f && random->nextFloat() * 30 < (br - 0.4f) * 2 && level->canSeeSky( Mth::floor(x), Mth::floor(y), Mth::floor(z))) + { + setOnFire(8); + } + } + Monster::aiStep(); +} + +void Zombie::tick() +{ + if (!level->isClientSide && isConverting()) + { + int amount = getConversionProgress(); + + villagerConversionTime -= amount; + + if (villagerConversionTime <= 0) + { + finishConversion(); + } + } + + Monster::tick(); +} + +int Zombie::getAmbientSound() +{ + return eSoundType_MOB_ZOMBIE_AMBIENT; +} + +int Zombie::getHurtSound() +{ + return eSoundType_MOB_ZOMBIE_HURT; +} + +int Zombie::getDeathSound() +{ + return eSoundType_MOB_ZOMBIE_DEATH; +} + +int Zombie::getDeathLoot() +{ + return Item::rotten_flesh_Id; +} + +MobType Zombie::getMobType() +{ + return UNDEAD; +} + +void Zombie::dropRareDeathLoot(int rareLootLevel) +{ + switch (random->nextInt(3)) + { +/* case 0: + spawnAtLocation(Item::sword_iron_Id, 1); + break; + case 1: + spawnAtLocation(Item::helmet_iron_Id, 1); + break; + case 2: + spawnAtLocation(Item::ironIngot_Id, 1); + break; + case 3: + spawnAtLocation(Item::shovel_iron_Id, 1); + break;*/ + case 0: + spawnAtLocation(Item::ironIngot_Id, 1); + break; + case 1: + spawnAtLocation(Item::carrots_Id, 1); + break; + case 2: + spawnAtLocation(Item::potato_Id, 1); + break; + } +} + +void Zombie::addAdditonalSaveData(CompoundTag *tag) +{ + Monster::addAdditonalSaveData(tag); + + if (isBaby()) tag->putBoolean(L"IsBaby", true); + if (isVillager()) tag->putBoolean(L"IsVillager", true); + tag->putInt(L"ConversionTime", isConverting() ? villagerConversionTime : -1); +} + +void Zombie::readAdditionalSaveData(CompoundTag *tag) +{ + Monster::readAdditionalSaveData(tag); + + if (tag->getBoolean(L"IsBaby")) setBaby(true); + if (tag->getBoolean(L"IsVillager")) setVillager(true); + if (tag->contains(L"ConversionTime") && tag->getInt(L"ConversionTime") > -1) startConverting(tag->getInt(L"ConversionTime")); +} + +void Zombie::killed(shared_ptr mob) +{ + Monster::killed(mob); + + if (level->difficulty >= Difficulty::NORMAL && ((mob->GetType() & eTYPE_VILLAGER) == eTYPE_VILLAGER)) + { + if( !level->canCreateMore( GetType(), Level::eSpawnType_Egg) ) return; + if (level->difficulty == Difficulty::NORMAL && random->nextBoolean()) return; + + shared_ptr zombie = shared_ptr(new Zombie(level)); + zombie->copyPosition(mob); + level->removeEntity(mob); + zombie->finalizeMobSpawn(); + zombie->setVillager(true); + if (mob->isBaby()) zombie->setBaby(true); + level->addEntity(zombie); + + level->levelEvent(nullptr, LevelEvent::SOUND_ZOMBIE_INFECTED, (int) x, (int) y, (int) z, 0); + } +} + +void Zombie::finalizeMobSpawn() +{ + // 4J Stu - TODO TU15 +#if 0 + canPickUpLoot = random->nextFloat() < CAN_PICK_UP_LOOT_CHANCES[level->difficulty]; +#endif + + if (level->random->nextFloat() < 0.05f) + { + setVillager(true); + } + + // 4J Stu - TODO TU15 +#if 0 + populateDefaultEquipmentSlots(); + populateDefaultEquipmentEnchantments(); + + if (getCarried(SLOT_HELM) == null) + { + Calendar cal = level.getCalendar(); + + if (cal.get(Calendar.MONTH) + 1 == 10 && cal.get(Calendar.DAY_OF_MONTH) == 31 && random.nextFloat() < 0.25f) { + // Halloween! OooOOo! 25% of all skeletons/zombies can wear + // pumpkins on their heads. + setEquippedSlot(SLOT_HELM, new ItemInstance(random.nextFloat() < 0.1f ? Tile.litPumpkin : Tile.pumpkin)); + dropChances[SLOT_HELM] = 0; + } + } +#endif +} + +bool Zombie::interact(shared_ptr player) +{ + shared_ptr item = player->getSelectedItem(); + + if (item != NULL && item->getItem() == Item::apple_gold && item->getAuxValue() == 0 && isVillager() && hasEffect(MobEffect::weakness)) + { + if (!player->abilities.instabuild) item->count--; + if (item->count <= 0) + { + player->inventory->setItem(player->inventory->selected, nullptr); + } + + if (!level->isClientSide) + { + startConverting(random->nextInt(VILLAGER_CONVERSION_WAIT_MAX - VILLAGER_CONVERSION_WAIT_MIN + 1) + VILLAGER_CONVERSION_WAIT_MIN); + + // 4J-JEV, award achievement here, as it is impractical to award when the zombie is actually cured. + player->awardStat(GenericStats::zombieDoctor(),GenericStats::param_zombieDoctor()); + } + + return true; + } + + return false; +} + +void Zombie::startConverting(int time) +{ + villagerConversionTime = time; + getEntityData()->set(DATA_CONVERTING_ID, (byte) 1); + + removeEffect(MobEffect::weakness->id); + addEffect(new MobEffectInstance(MobEffect::damageBoost->id, time, min(level->difficulty - 1, 0))); + + level->broadcastEntityEvent(shared_from_this(), EntityEvent::ZOMBIE_CONVERTING); +} + +void Zombie::handleEntityEvent(byte id) +{ + if (id == EntityEvent::ZOMBIE_CONVERTING) + { + level->playLocalSound(x + 0.5f, y + 0.5f, z + 0.5f, eSoundType_MOB_ZOMBIE_REMEDY, 1 + random->nextFloat(), random->nextFloat() * 0.7f + 0.3f);//, false); + } + else + { + Monster::handleEntityEvent(id); + } +} + +bool Zombie::isConverting() +{ + return getEntityData()->getByte(DATA_CONVERTING_ID) == (byte) 1; +} + +void Zombie::finishConversion() +{ + shared_ptr villager = shared_ptr(new Villager(level)); + villager->copyPosition(shared_from_this()); + villager->finalizeMobSpawn(); + villager->setRewardPlayersInVillage(); + if (isBaby()) villager->setAge(-20 * 60 * 20); + level->removeEntity(shared_from_this()); + level->addEntity(villager); + + villager->addEffect(new MobEffectInstance(MobEffect::confusion->id, SharedConstants::TICKS_PER_SECOND * 10, 0)); + level->levelEvent(nullptr, LevelEvent::SOUND_ZOMBIE_CONVERTED, (int) x, (int) y, (int) z, 0); +} + +int Zombie::getConversionProgress() +{ + int amount = 1; + + if (random->nextFloat() < 0.01f) + { + int specialBlocksCount = 0; + + for (int xx = (int) x - SPECIAL_BLOCK_RADIUS; xx < (int) x + SPECIAL_BLOCK_RADIUS && specialBlocksCount < MAX_SPECIAL_BLOCKS_COUNT; xx++) + { + for (int yy = (int) y - SPECIAL_BLOCK_RADIUS; yy < (int) y + SPECIAL_BLOCK_RADIUS && specialBlocksCount < MAX_SPECIAL_BLOCKS_COUNT; yy++) + { + for (int zz = (int) z - SPECIAL_BLOCK_RADIUS; zz < (int) z + SPECIAL_BLOCK_RADIUS && specialBlocksCount < MAX_SPECIAL_BLOCKS_COUNT; zz++) + { + int tile = level->getTile(xx, yy, zz); + + if (tile == Tile::ironFence_Id || tile == Tile::bed_Id) + { + if (random->nextFloat() < 0.3f) amount++; + specialBlocksCount++; + } + } + } + } + } + return amount; +} + +void Zombie::updateSize(bool isBaby) +{ + internalSetSize(isBaby ? .5f : 1.0f); +} + +void Zombie::setSize(float w, float h) +{ + bool inited = registeredBBWidth > 0; + + registeredBBWidth = w; + registeredBBHeight = h; + + if (!inited) + { + internalSetSize(1.0f); + } +} + +void Zombie::internalSetSize(float scale) +{ + PathfinderMob::setSize(registeredBBWidth * scale, registeredBBHeight * scale); +} diff --git a/Minecraft.World/Zombie.h b/Minecraft.World/Zombie.h new file mode 100644 index 00000000..237db15b --- /dev/null +++ b/Minecraft.World/Zombie.h @@ -0,0 +1,88 @@ +#pragma once +using namespace std; + +#include "Monster.h" +#include "SharedConstants.h" + +class Zombie : public Monster +{ +private: + static const int VILLAGER_CONVERSION_WAIT_MIN = SharedConstants::TICKS_PER_SECOND * 60 * 3; + static const int VILLAGER_CONVERSION_WAIT_MAX = SharedConstants::TICKS_PER_SECOND * 60 * 5; + + static const int DATA_BABY_ID = 12; + static const int DATA_VILLAGER_ID = 13; + static const int DATA_CONVERTING_ID = 14; + + int villagerConversionTime; + + float registeredBBWidth; + float registeredBBHeight; + +public: + static const int MAX_SPECIAL_BLOCKS_COUNT = 14; + static const int SPECIAL_BLOCK_RADIUS = 4; + +public: + eINSTANCEOF GetType() { return eTYPE_ZOMBIE; } + static Entity *create(Level *level) { return new Zombie(level); } + + Zombie(Level *level); + virtual float getWalkingSpeedModifier(); + +protected: + virtual void defineSynchedData(); + +public: + virtual int getTexture(); + virtual int getMaxHealth(); + int getArmorValue(); + +protected: + virtual bool useNewAi(); + +public: + bool isBaby(); + void setBaby(bool baby); + bool isVillager(); + void setVillager(bool villager); + virtual void aiStep(); + virtual void tick(); + +protected: + virtual int getAmbientSound(); + virtual int getHurtSound(); + virtual int getDeathSound(); + virtual int getDeathLoot(); + +public: + MobType getMobType(); + +protected: + virtual void dropRareDeathLoot(int rareLootLevel); + +public: + virtual void addAdditonalSaveData(CompoundTag *tag); + virtual void readAdditionalSaveData(CompoundTag *tag); + void killed(shared_ptr mob); + virtual void finalizeMobSpawn(); + bool interact(shared_ptr player); + +protected: + void startConverting(int time); + +public: + void handleEntityEvent(byte id); + bool isConverting(); + +protected: + void finishConversion(); + int getConversionProgress(); + +public: + virtual void updateSize(bool isBaby); + +protected: + virtual void setSize(float w, float h); + void internalSetSize(float scale); +}; diff --git a/Minecraft.World/ZoneFile.cpp b/Minecraft.World/ZoneFile.cpp new file mode 100644 index 00000000..fc1f36bf --- /dev/null +++ b/Minecraft.World/ZoneFile.cpp @@ -0,0 +1,92 @@ +#include "stdafx.h" +#include "ByteBuffer.h" +#include "File.h" +#include "ZoneFile.h" + + +const int ZoneFile::slotsLength = ZonedChunkStorage::CHUNKS_PER_ZONE * ZonedChunkStorage::CHUNKS_PER_ZONE; + +ZoneFile::ZoneFile(__int64 key, File file, File entityFile) : slots(slotsLength) +{ + lastUse = 0; + + this->key = key; + this->file = file; + + // 4J - try/catch removed +// try { + this->entityFile = new NbtSlotFile(entityFile); +// } catch (Exception e) { +// System.out.println("Broken entity file: " + entityFile + " (" + e.toString() + "), replacing.."); +// entityFile.delete(); +// entityFile.createNewFile(); +// this.entityFile = new NbtSlotFile(entityFile); +// } + + channel = CreateFile(wstringtofilename(file.getPath()), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + // 4J - try/catch removed +// try { + readHeader(); +// } catch (Exception e) { +// e.printStackTrace(); +// throw new IOException("Broken zone file: " + file + ": " + e); +// } +} + +ZoneFile::~ZoneFile() +{ + delete [] slots.data; +} + +void ZoneFile::readHeader() +{ + ZoneIo *zoneIo = new ZoneIo(channel, 0); + ByteBuffer *bb = zoneIo->read(FILE_HEADER_SIZE); + bb->flip(); + if (bb->remaining() < 5) return; + int magic = bb->getInt(); +// if (magic != MAGIC_NUMBER) throw new IOException("Bad magic number: " + magic); // 4J - TODO + short version = bb->getShort(); +// if (version != 0) throw new IOException("Bad version number: " + version); // 4J - TODO + + slotCount = bb->getShort(); + bb->getShortArray(slots); + bb->position(bb->position() + slotsLength * 2); +} + +void ZoneFile::writeHeader() +{ + ZoneIo *zoneIo = new ZoneIo(channel, 0); + + ByteBuffer *bb = ByteBuffer::allocate(FILE_HEADER_SIZE); + bb->order(ZonedChunkStorage::BYTEORDER); + bb->putInt(MAGIC_NUMBER); + bb->putShort((short) 0); + bb->putShort((short) slotCount); + bb->putShortArray(slots); + bb->position(bb->position() + slots.length * 2); + bb->flip(); + zoneIo->write(bb, FILE_HEADER_SIZE); +} + +void ZoneFile::close() +{ + CloseHandle(channel); + entityFile->close(); +} + +ZoneIo *ZoneFile::getZoneIo(int slot) +{ + if (slots[slot] == 0) + { + slots[slot] = ++slotCount; + writeHeader(); + } + int byteOffs = (slots[slot] - 1) * ZonedChunkStorage::CHUNK_SIZE_BYTES + FILE_HEADER_SIZE; + return new ZoneIo(channel, byteOffs); +} + +bool ZoneFile::containsSlot(int slot) +{ + return slots[slot] > 0; +} \ No newline at end of file diff --git a/Minecraft.World/ZoneFile.h b/Minecraft.World/ZoneFile.h new file mode 100644 index 00000000..d7b8cc82 --- /dev/null +++ b/Minecraft.World/ZoneFile.h @@ -0,0 +1,42 @@ +#pragma once +#include "ZonedChunkStorage.h" +#include "NbtSlotFile.h" +#include "ZoneIo.h" + +class ZoneFile +{ +public: + static const int FILE_HEADER_SIZE = 1024 * 4; + +private: + static const int MAGIC_NUMBER = 0x13737000; + + static const int slotsLength; + + shortArray slots; + short slotCount; + +public: + __int64 lastUse; + +private: + HANDLE channel; + +public: + __int64 key; + File file; + + NbtSlotFile *entityFile; + + ZoneFile(__int64 key, File file, File entityFile); + ~ZoneFile(); + + void readHeader(); + + void writeHeader(); + +public: + void close(); + ZoneIo *getZoneIo(int slot); + bool containsSlot(int slot); +}; diff --git a/Minecraft.World/ZoneIo.cpp b/Minecraft.World/ZoneIo.cpp new file mode 100644 index 00000000..f1838c86 --- /dev/null +++ b/Minecraft.World/ZoneIo.cpp @@ -0,0 +1,48 @@ +#include "stdafx.h" +#include "ByteBuffer.h" +#include "ZoneIo.h" + +ZoneIo::ZoneIo(HANDLE channel, __int64 pos) +{ + this->channel = channel; + this->pos = pos; +} + +void ZoneIo::write(byteArray bb, int size) +{ + ByteBuffer *buff = ByteBuffer::wrap(bb); +// if (bb.length != size) throw new IllegalArgumentException("Expected " + size + " bytes, got " + bb.length); // 4J - TODO + buff->order(ZonedChunkStorage::BYTEORDER); + buff->position(bb.length); + buff->flip(); + write(buff, size); + delete buff; +} + +void ZoneIo::write(ByteBuffer *bb, int size) +{ + DWORD numberOfBytesWritten; + SetFilePointer(channel,(int)pos,NULL,NULL); + WriteFile(channel,bb->getBuffer(), bb->getSize(),&numberOfBytesWritten,NULL); + pos += size; +} + +ByteBuffer *ZoneIo::read(int size) +{ + DWORD numberOfBytesRead; + byteArray bb = byteArray(size); + SetFilePointer(channel,(int)pos,NULL,NULL); + ByteBuffer *buff = ByteBuffer::wrap(bb); + // 4J - to investigate - why is this buffer flipped before anything goes in it? + buff->order(ZonedChunkStorage::BYTEORDER); + buff->position(size); + buff->flip(); + ReadFile(channel, buff->getBuffer(), buff->getSize(), &numberOfBytesRead, NULL); + pos += size; + return buff; +} + +void ZoneIo::flush() +{ + // 4J - was channel.force(false); +} \ No newline at end of file diff --git a/Minecraft.World/ZoneIo.h b/Minecraft.World/ZoneIo.h new file mode 100644 index 00000000..22473583 --- /dev/null +++ b/Minecraft.World/ZoneIo.h @@ -0,0 +1,19 @@ +#pragma once +#include "ZonedChunkStorage.h" + +class ByteBuffer; + +class ZoneIo +{ +private: + HANDLE channel; + __int64 pos; + +public: + ZoneIo(HANDLE channel, __int64 pos); + void write(byteArray bb, int size); + void write(ByteBuffer *bb, int size); + ByteBuffer *read(int size); + + void flush(); +}; diff --git a/Minecraft.World/ZonedChunkStorage.cpp b/Minecraft.World/ZonedChunkStorage.cpp new file mode 100644 index 00000000..b4ee8585 --- /dev/null +++ b/Minecraft.World/ZonedChunkStorage.cpp @@ -0,0 +1,264 @@ +#include "stdafx.h" +#include "File.h" +#include "ByteBuffer.h" +#include "net.minecraft.world.entity.h" +#include "net.minecraft.world.level.h" +#include "net.minecraft.world.level.tile.entity.h" +#include "net.minecraft.world.level.chunk.h" +#include "ZonedChunkStorage.h" +#include "ZoneFile.h" + +// 4J Stu - There are changes to this class for 1.8.2, but since we never use it anyway lets not worry about it + +const int ZonedChunkStorage::BIT_TERRAIN_POPULATED = 0x0000001; + +const int ZonedChunkStorage::CHUNKS_PER_ZONE_BITS = 5; // = 32 +const int ZonedChunkStorage::CHUNKS_PER_ZONE = 1 << ZonedChunkStorage::CHUNKS_PER_ZONE_BITS; // ^2 + +const int ZonedChunkStorage::CHUNK_WIDTH = 16; + +const int ZonedChunkStorage::CHUNK_HEADER_SIZE = 256; +const int ZonedChunkStorage::CHUNK_SIZE = ZonedChunkStorage::CHUNK_WIDTH * ZonedChunkStorage::CHUNK_WIDTH * Level::DEPTH; +const int ZonedChunkStorage::CHUNK_LAYERS = 3; +const int ZonedChunkStorage::CHUNK_SIZE_BYTES = ZonedChunkStorage::CHUNK_SIZE * ZonedChunkStorage::CHUNK_LAYERS + ZonedChunkStorage::CHUNK_HEADER_SIZE; + +const ByteOrder ZonedChunkStorage::BYTEORDER = BIGENDIAN; + +ZonedChunkStorage::ZonedChunkStorage(File dir) +{ + tickCount = 0; + + //this->dir = dir; + this->dir = File( dir, wstring( L"data" ) ); + if( !this->dir.exists() ) this->dir.mkdirs(); +} + + +int ZonedChunkStorage::getSlot(int x, int z) +{ + int xZone = x >> CHUNKS_PER_ZONE_BITS; + int zZone = z >> CHUNKS_PER_ZONE_BITS; + int xOffs = x - (xZone << CHUNKS_PER_ZONE_BITS); + int zOffs = z - (zZone << CHUNKS_PER_ZONE_BITS); + int slot = xOffs + zOffs * CHUNKS_PER_ZONE; + return slot; +} + +ZoneFile *ZonedChunkStorage::getZoneFile(int x, int z, bool create) +{ + int slot = getSlot(x, z); + + int xZone = x >> CHUNKS_PER_ZONE_BITS; + int zZone = z >> CHUNKS_PER_ZONE_BITS; + __int64 key = xZone + (zZone << 20l); + // 4J - was !zoneFiles.containsKey(key) + if (zoneFiles.find(key) == zoneFiles.end()) + { + wchar_t xRadix36[64]; + wchar_t zRadix36[64]; + _itow(x,xRadix36,36); + _itow(z,zRadix36,36); + File file = File(dir, wstring( L"zone_") + _toString( xRadix36 ) + L"_" + _toString( zRadix36 ) + L".dat" ); + + if ( !file.exists() ) + { + if (!create) return NULL; + HANDLE ch = CreateFile(wstringtofilename(file.getPath()), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + CloseHandle(ch); + } + + File entityFile = File(dir, wstring( L"entities_") + _toString( xRadix36 ) + L"_" + _toString( zRadix36 ) + L".dat" ); + + zoneFiles[key] = new ZoneFile(key, file, entityFile); + } + + ZoneFile *zoneFile = zoneFiles[key]; + zoneFile->lastUse = tickCount; + if (!zoneFile->containsSlot(slot)) + { + if (!create) return NULL; + } + return zoneFile; + +} + +ZoneIo *ZonedChunkStorage::getBuffer(int x, int z, bool create) +{ + ZoneFile *zoneFile = getZoneFile(x, z, create); + if (zoneFile == NULL) return NULL; + return zoneFile->getZoneIo(getSlot(x, z)); +} + +LevelChunk *ZonedChunkStorage::load(Level *level, int x, int z) +{ + ZoneIo *zoneIo = getBuffer(x, z, false); + if (zoneIo == NULL) return NULL; + + LevelChunk *lc = new LevelChunk(level, x, z); + lc->unsaved = false; + + ByteBuffer *header = zoneIo->read(CHUNK_HEADER_SIZE); + lc->blocks = zoneIo->read(CHUNK_SIZE)->array(); + lc->data = new DataLayer(zoneIo->read(CHUNK_SIZE / 2)->array()); + lc->skyLight = new DataLayer(zoneIo->read(CHUNK_SIZE / 2)->array()); + lc->blockLight = new DataLayer(zoneIo->read(CHUNK_SIZE / 2)->array()); + lc->heightmap = zoneIo->read(CHUNK_WIDTH * CHUNK_WIDTH)->array(); + + header->flip(); + int xOrg = header->getInt(); + int zOrg = header->getInt(); + __int64 time = header->getLong(); + __int64 flags = header->getLong(); + + lc->terrainPopulated = (flags & BIT_TERRAIN_POPULATED) != 0; + + loadEntities(level, lc); + + lc->fixBlocks(); + return lc; + +} + +void ZonedChunkStorage::save(Level *level, LevelChunk *lc) +{ + __int64 flags = 0; + if (lc->terrainPopulated) flags |= BIT_TERRAIN_POPULATED; + + ByteBuffer *header = ByteBuffer::allocate(CHUNK_HEADER_SIZE); + header->order(ZonedChunkStorage::BYTEORDER); + header->putInt(lc->x); + header->putInt(lc->z); + header->putLong(level->getTime()); + header->putLong(flags); + header->flip(); + + ZoneIo *zoneIo = getBuffer(lc->x, lc->z, true); + zoneIo->write(header, CHUNK_HEADER_SIZE); + zoneIo->write(lc->blocks, CHUNK_SIZE); + zoneIo->write(lc->data->data, CHUNK_SIZE / 2); + zoneIo->write(lc->skyLight->data, CHUNK_SIZE / 2); + zoneIo->write(lc->blockLight->data, CHUNK_SIZE / 2); + zoneIo->write(lc->heightmap, CHUNK_WIDTH * CHUNK_WIDTH); + zoneIo->flush(); +} + +void ZonedChunkStorage::tick() +{ + tickCount++; + if (tickCount % (20 * 10) == 4) + { + vector<__int64> toClose; + + AUTO_VAR(itEndZF, zoneFiles.end()); + for( unordered_map<__int64, ZoneFile *>::iterator it = zoneFiles.begin(); it != itEndZF; it++ ) + { + ZoneFile *zoneFile = it->second; + if (tickCount - zoneFile->lastUse > 20 * 60) + { + toClose.push_back(zoneFile->key); + } + } + + AUTO_VAR(itEndTC, toClose.end()); + for (AUTO_VAR(it, toClose.begin()); it != itEndTC; it++) + { + __int64 key = *it ; //toClose[i]; + // 4J - removed try/catch +// try { + char buf[256]; + sprintf(buf,"Closing zone %I64d\n",key); + app.DebugPrintf(buf); + zoneFiles[key]->close(); + zoneFiles.erase(zoneFiles.find(key)); +// } catch (IOException e) { +// e.printStackTrace(); +// } + } + } +} + + +void ZonedChunkStorage::flush() +{ + AUTO_VAR(itEnd, zoneFiles.end()); + for( unordered_map<__int64, ZoneFile *>::iterator it = zoneFiles.begin(); it != itEnd; it++ ) + { + ZoneFile *zoneFile = it->second; + // 4J - removed try/catch +// try { + zoneFile->close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } + } + zoneFiles.clear(); +} + +void ZonedChunkStorage::loadEntities(Level *level, LevelChunk *lc) +{ + int slot = getSlot(lc->x, lc->z); + ZoneFile *zoneFile = getZoneFile(lc->x, lc->z, true); + vector *tags = zoneFile->entityFile->readAll(slot); + + AUTO_VAR(itEnd, tags->end()); + for (AUTO_VAR(it, tags->begin()); it != itEnd; it++) + { + CompoundTag *tag = *it; //tags->at(i); + int type = tag->getInt(L"_TYPE"); + if (type == 0) + { + shared_ptr e = EntityIO::loadStatic(tag, level); + if (e != NULL) lc->addEntity(e); + } + else if (type == 1) + { + shared_ptr te = TileEntity::loadStatic(tag); + if (te != NULL) lc->addTileEntity(te); + } + } +} + +void ZonedChunkStorage::saveEntities(Level *level, LevelChunk *lc) +{ + int slot = getSlot(lc->x, lc->z); + ZoneFile *zoneFile = getZoneFile(lc->x, lc->z, true); + + vector tags; + +#ifdef _ENTITIES_RW_SECTION + EnterCriticalRWSection(&lc->m_csEntities, true); +#else + EnterCriticalSection(&lc->m_csEntities); +#endif + for (int i = 0; i < LevelChunk::ENTITY_BLOCKS_LENGTH; i++) + { + vector > *entities = lc->entityBlocks[i]; + + AUTO_VAR(itEndTags, entities->end()); + for (AUTO_VAR(it, entities->begin()); it != itEndTags; it++) + { + shared_ptr e = *it; //entities->at(j); + CompoundTag *cp = new CompoundTag(); + cp->putInt(L"_TYPE", 0); + e->save(cp); + tags.push_back(cp); + } + } +#ifdef _ENTITIES_RW_SECTION + LeaveCriticalRWSection(&lc->m_csEntities, true); +#else + LeaveCriticalSection(&lc->m_csEntities); +#endif + + for( unordered_map , TilePosKeyHash, TilePosKeyEq>::iterator it = lc->tileEntities.begin(); + it != lc->tileEntities.end(); it++) + { + shared_ptr te = it->second; + CompoundTag *cp = new CompoundTag(); + cp->putInt(L"_TYPE", 1); + te->save(cp); + tags.push_back(cp); + } + + zoneFile->entityFile->replaceSlot(slot, &tags); +} diff --git a/Minecraft.World/ZonedChunkStorage.h b/Minecraft.World/ZonedChunkStorage.h new file mode 100644 index 00000000..ed7e3b65 --- /dev/null +++ b/Minecraft.World/ZonedChunkStorage.h @@ -0,0 +1,53 @@ +#pragma once +#include "File.h" +#include "ChunkStorage.h" +#include "LevelChunk.h" + +// 4J Stu - There are changes to this class for 1.8.2, but since we never use it anyway lets not worry about it + +using namespace std; + +class ZoneFile; +class ZoneIo; + +class ZonedChunkStorage : public ChunkStorage +{ +public: + static const int BIT_TERRAIN_POPULATED; + + static const int CHUNKS_PER_ZONE_BITS; // = 32 + static const int CHUNKS_PER_ZONE; // ^2 + + static const int CHUNK_WIDTH; + + static const int CHUNK_HEADER_SIZE; + static const int CHUNK_SIZE; + static const int CHUNK_LAYERS ; + static const int CHUNK_SIZE_BYTES; + + static const ByteOrder BYTEORDER; + + File dir; + +private: + unordered_map<__int64, ZoneFile *> zoneFiles; + __int64 tickCount; + +public: + ZonedChunkStorage(File dir); +private: + int getSlot(int x, int z); + ZoneFile *getZoneFile(int x, int z, bool create); + ZoneIo *getBuffer(int x, int z, bool create); + +public: + LevelChunk *load(Level *level, int x, int z); + void save(Level *level, LevelChunk *lc); + + void tick(); + + void flush(); + + void loadEntities(Level *level, LevelChunk *lc); + void saveEntities(Level *level, LevelChunk *lc); +}; \ No newline at end of file diff --git a/Minecraft.World/ZoomLayer.cpp b/Minecraft.World/ZoomLayer.cpp new file mode 100644 index 00000000..bef24709 --- /dev/null +++ b/Minecraft.World/ZoomLayer.cpp @@ -0,0 +1,92 @@ +#include "stdafx.h" +#include "net.minecraft.world.level.newbiome.layer.h" +#include "System.h" + +ZoomLayer::ZoomLayer(__int64 seedMixup, shared_ptrparent) : Layer(seedMixup) +{ + this->parent = parent; +} + +intArray ZoomLayer::getArea(int xo, int yo, int w, int h) +{ + int px = xo >> 1; + int py = yo >> 1; + int pw = (w >> 1) + 3; + int ph = (h >> 1) + 3; + intArray p = parent->getArea(px, py, pw, ph); + + intArray tmp = IntCache::allocate((pw * 2) * (ph * 2)); + int ww = (pw << 1); + for (int y = 0; y < ph - 1; y++) + { + int ry = y << 1; + int pp = ry * ww; + int ul = p[(0 + 0) + (y + 0) * pw]; + int dl = p[(0 + 0) + (y + 1) * pw]; + for (int x = 0; x < pw - 1; x++) + { + initRandom((x + px) << 1, (y + py) << 1); + int ur = p[(x + 1) + (y + 0) * pw]; + int dr = p[(x + 1) + (y + 1) * pw]; + + tmp[pp] = ul; + tmp[pp++ + ww] = random(ul, dl); + tmp[pp] = random(ul, ur); + tmp[pp++ + ww] = random(ul, ur, dl, dr); + + ul = ur; + dl = dr; + } + } + intArray result = IntCache::allocate(w * h); + for (int y = 0; y < h; y++) + { + System::arraycopy(tmp, (y + (yo & 1)) * (pw << 1) + (xo & 1), &result, y * w, w); + } + return result; +} + +int ZoomLayer::random(int a, int b) +{ + return nextRandom(2) == 0 ? a : b; +} + +int ZoomLayer::random(int a, int b, int c, int d) +{ + if (b == c && c == d) return b; + if (a == b && a == c) return a; + if (a == b && a == d) return a; + if (a == c && a == d) return a; + + if (a == b && c != d) return a; + if (a == c && b != d) return a; + if (a == d && b != c) return a; + + if (b == a && c != d) return b; + if (b == c && a != d) return b; + if (b == d && a != c) return b; + + if (c == a && b != d) return c; + if (c == b && a != d) return c; + if (c == d && a != b) return c; + + if (d == a && b != c) return c; + if (d == b && a != c) return c; + if (d == c && a != b) return c; + + int s = nextRandom(4); + if (s == 0) return a; + if (s == 1) return b; + if (s == 2) return c; + return d; +} + +shared_ptrZoomLayer::zoom(__int64 seed, shared_ptr sup, int count) +{ + shared_ptrresult = sup; + for (int i = 0; i < count; i++) + { + result = shared_ptr(new ZoomLayer(seed + i, result)); + } + return result; +} \ No newline at end of file diff --git a/Minecraft.World/ZoomLayer.h b/Minecraft.World/ZoomLayer.h new file mode 100644 index 00000000..f865f088 --- /dev/null +++ b/Minecraft.World/ZoomLayer.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Layer.h" + +class ZoomLayer : public Layer +{ +public: + ZoomLayer(__int64 seedMixup, shared_ptr parent); + + virtual intArray getArea(int xo, int yo, int w, int h); + +protected: + int random(int a, int b); + int random(int a, int b, int c, int d); + +public: + static shared_ptr zoom(__int64 seed, shared_ptrsup, int count); +}; \ No newline at end of file diff --git a/Minecraft.World/com.mojang.nbt.h b/Minecraft.World/com.mojang.nbt.h new file mode 100644 index 00000000..56f4a66c --- /dev/null +++ b/Minecraft.World/com.mojang.nbt.h @@ -0,0 +1 @@ +#include "NbtIo.h" \ No newline at end of file diff --git a/Minecraft.World/compression.cpp b/Minecraft.World/compression.cpp new file mode 100644 index 00000000..99c5228a --- /dev/null +++ b/Minecraft.World/compression.cpp @@ -0,0 +1,546 @@ +#include "stdafx.h" +#include "compression.h" +#if defined __ORBIS__ || defined __PS3__ || defined _DURANGO || defined _WIN64 +#include "..\Minecraft.Client\Common\zlib\zlib.h" +#endif + +#if defined __PSVITA__ +#include "..\Minecraft.Client\PSVita\PSVitaExtras\zlib.h" +#elif defined __PS3__ +#include "..\Minecraft.Client\PS3\PS3Extras\EdgeZLib.h" +#endif //__PS3__ + + +DWORD Compression::tlsIdx = 0; +Compression::ThreadStorage *Compression::tlsDefault = NULL; + +Compression::ThreadStorage::ThreadStorage() +{ + compression = new Compression(); +} + +Compression::ThreadStorage::~ThreadStorage() +{ + delete compression; +} + +void Compression::CreateNewThreadStorage() +{ + ThreadStorage *tls = new ThreadStorage(); + if(tlsDefault == NULL ) + { + tlsIdx = TlsAlloc(); + tlsDefault = tls; + } + TlsSetValue(tlsIdx, tls); +} + +void Compression::UseDefaultThreadStorage() +{ + TlsSetValue(tlsIdx, tlsDefault); +} + +void Compression::ReleaseThreadStorage() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); + if( tls == tlsDefault ) return; + + delete tls; +} + +Compression *Compression::getCompression() +{ + ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx); + return tls->compression; +} + +HRESULT Compression::CompressLZXRLE(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize) +{ + EnterCriticalSection(&rleCompressLock); + //static unsigned char rleBuf[1024*100]; + + unsigned char *pucIn = (unsigned char *)pSource; + unsigned char *pucEnd = pucIn + SrcSize; + unsigned char *pucOut = (unsigned char *)rleCompressBuf; + + // Compress with RLE first: + // 0 - 254 - encodes a single byte + // 255 followed by 0, 1, 2 - encodes a 1, 2, or 3 255s + // 255 followed by 3-255, followed by a byte - encodes a run of n + 1 bytes + PIXBeginNamedEvent(0,"RLE compression"); + do + { + unsigned char thisOne = *pucIn++; + + unsigned int count = 1; + while( ( pucIn != pucEnd ) && ( *pucIn == thisOne ) && ( count < 256 ) ) + { + pucIn++; + count++; + } + + if( count <= 3 ) + { + if( thisOne == 255 ) + { + *pucOut++ = 255; + *pucOut++ = count - 1; + } + else + { + for( unsigned int i = 0; i < count ; i++ ) + { + *pucOut++ = thisOne; + } + } + } + else + { + *pucOut++ = 255; + *pucOut++ = count - 1; + *pucOut++ = thisOne; + } + } while (pucIn != pucEnd); + unsigned int rleSize = (unsigned int)(pucOut - rleCompressBuf); + PIXEndNamedEvent(); + + PIXBeginNamedEvent(0,"Secondary compression"); + Compress(pDestination, pDestSize, rleCompressBuf, rleSize); + PIXEndNamedEvent(); + LeaveCriticalSection(&rleCompressLock); +// printf("Compressed from %d to %d to %d\n",SrcSize,rleSize,*pDestSize); + + return S_OK; +} + +HRESULT Compression::CompressRLE(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize) +{ + EnterCriticalSection(&rleCompressLock); + //static unsigned char rleBuf[1024*100]; + + unsigned char *pucIn = (unsigned char *)pSource; + unsigned char *pucEnd = pucIn + SrcSize; + unsigned char *pucOut = (unsigned char *)rleCompressBuf; + + // Compress with RLE first: + // 0 - 254 - encodes a single byte + // 255 followed by 0, 1, 2 - encodes a 1, 2, or 3 255s + // 255 followed by 3-255, followed by a byte - encodes a run of n + 1 bytes + PIXBeginNamedEvent(0,"RLE compression"); + do + { + unsigned char thisOne = *pucIn++; + + unsigned int count = 1; + while( ( pucIn != pucEnd ) && ( *pucIn == thisOne ) && ( count < 256 ) ) + { + pucIn++; + count++; + } + + if( count <= 3 ) + { + if( thisOne == 255 ) + { + *pucOut++ = 255; + *pucOut++ = count - 1; + } + else + { + for( unsigned int i = 0; i < count ; i++ ) + { + *pucOut++ = thisOne; + } + } + } + else + { + *pucOut++ = 255; + *pucOut++ = count - 1; + *pucOut++ = thisOne; + } + } while (pucIn != pucEnd); + unsigned int rleSize = (unsigned int)(pucOut - rleCompressBuf); + PIXEndNamedEvent(); + LeaveCriticalSection(&rleCompressLock); + + // Return + if (rleSize <= *pDestSize) + { + *pDestSize = rleSize; + memcpy(pDestination, rleCompressBuf, *pDestSize); + } + else + { +#ifndef _CONTENT_PACKAGE + assert(false); +#endif + } + + return S_OK; +} + +HRESULT Compression::DecompressLZXRLE(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize) +{ + EnterCriticalSection(&rleDecompressLock); + // 4J Stu - Fix for #13676 - Crash: Crash while attempting to load a world after updating TU + // Some saves can have chunks that decompress into very large sizes, so I have doubled the size of this buffer + // Ideally we should be able to dynamically allocate the buffer if it's going to be too big, as most chunks + // only use 5% of this buffer + + // 4J Stu - Changed this again to dynamically allocate a buffer if it's going to be too big + unsigned char *pucIn = NULL; + + //const unsigned int staticRleSize = 1024*200; + //static unsigned char rleBuf[staticRleSize]; + unsigned int rleSize = staticRleSize; + unsigned char *dynamicRleBuf = NULL; + + if(*pDestSize > rleSize) + { + rleSize = *pDestSize; + dynamicRleBuf = new unsigned char[rleSize]; + Decompress(dynamicRleBuf, &rleSize, pSource, SrcSize); + pucIn = (unsigned char *)dynamicRleBuf; + } + else + { + Decompress(rleDecompressBuf, &rleSize, pSource, SrcSize); + pucIn = (unsigned char *)rleDecompressBuf; + } + + //unsigned char *pucIn = (unsigned char *)rleDecompressBuf; + unsigned char *pucEnd = pucIn + rleSize; + unsigned char *pucOut = (unsigned char *)pDestination; + + while( pucIn != pucEnd ) + { + unsigned char thisOne = *pucIn++; + if( thisOne == 255 ) + { + unsigned int count = *pucIn++; + if( count < 3 ) + { + count++; + for( unsigned int i = 0; i < count; i++ ) + { + *pucOut++ = 255; + } + } + else + { + count++; + unsigned char data = *pucIn++; + for( unsigned int i = 0; i < count; i++ ) + { + *pucOut++ = data; + } + } + } + else + { + *pucOut++ = thisOne; + } + } + *pDestSize = (unsigned int)(pucOut - (unsigned char *)pDestination); + +// printf("Decompressed from %d to %d to %d\n",SrcSize,rleSize,*pDestSize); + + if(dynamicRleBuf != NULL) delete [] dynamicRleBuf; + + LeaveCriticalSection(&rleDecompressLock); + return S_OK; +} + +HRESULT Compression::DecompressRLE(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize) +{ + EnterCriticalSection(&rleDecompressLock); + + //unsigned char *pucIn = (unsigned char *)rleDecompressBuf; + unsigned char *pucIn = (unsigned char *)pSource; + unsigned char *pucEnd = pucIn + SrcSize; + unsigned char *pucOut = (unsigned char *)pDestination; + + while( pucIn != pucEnd ) + { + unsigned char thisOne = *pucIn++; + if( thisOne == 255 ) + { + unsigned int count = *pucIn++; + if( count < 3 ) + { + count++; + for( unsigned int i = 0; i < count; i++ ) + { + *pucOut++ = 255; + } + } + else + { + count++; + unsigned char data = *pucIn++; + for( unsigned int i = 0; i < count; i++ ) + { + *pucOut++ = data; + } + } + } + else + { + *pucOut++ = thisOne; + } + } + *pDestSize = (unsigned int)(pucOut - (unsigned char *)pDestination); + + LeaveCriticalSection(&rleDecompressLock); + return S_OK; +} + + +HRESULT Compression::Compress(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize) +{ + // Using zlib for x64 compression - 360 is using native 360 compression and PS3 a stubbed non-compressing version of this +#if defined __ORBIS__ || defined _DURANGO || defined _WIN64 || defined __PSVITA__ + SIZE_T destSize = (SIZE_T)(*pDestSize); + int res = ::compress((Bytef *)pDestination, (uLongf *)&destSize, (Bytef *)pSource, SrcSize); + *pDestSize = (unsigned int)destSize; + return ( ( res == Z_OK ) ? S_OK : -1 ); +#elif defined __PS3__ + uint32_t destSize = (uint32_t)(*pDestSize); + bool res = EdgeZLib::Compress(pDestination, &destSize, pSource, SrcSize); + *pDestSize = (unsigned int)destSize; + return ( ( res ) ? S_OK : -1 ); +#else + SIZE_T destSize = (SIZE_T)(*pDestSize); + HRESULT res = XMemCompress(compressionContext, pDestination, &destSize, pSource, SrcSize); + *pDestSize = (unsigned int)destSize; + return res; +#endif +} + +HRESULT Compression::Decompress(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize) +{ + + if(m_decompressType != m_localDecompressType) // check if we're decompressing data from a different platform + { + // only used for loading a save from a different platform (Sony cloud storage cross play) + return DecompressWithType(pDestination, pDestSize, pSource, SrcSize); + } + + // Using zlib for x64 compression - 360 is using native 360 compression and PS3 a stubbed non-compressing version of this +#if defined __ORBIS__ || defined _DURANGO || defined _WIN64 || defined __PSVITA__ + SIZE_T destSize = (SIZE_T)(*pDestSize); + int res = ::uncompress((Bytef *)pDestination, (uLongf *)&destSize, (Bytef *)pSource, SrcSize); + *pDestSize = (unsigned int)destSize; + return ( ( res == Z_OK ) ? S_OK : -1 ); +#elif defined __PS3__ + uint32_t destSize = (uint32_t)(*pDestSize); + bool res = EdgeZLib::Decompress(pDestination, &destSize, pSource, SrcSize); + *pDestSize = (unsigned int)destSize; + return ( ( res ) ? S_OK : -1 ); +#else + SIZE_T destSize = (SIZE_T)(*pDestSize); + HRESULT res = XMemDecompress(decompressionContext, pDestination, (SIZE_T *)&destSize, pSource, SrcSize); + *pDestSize = (unsigned int)destSize; + return res; +#endif +} + +// MGH - same as VirtualDecompress in PSVitaStubs, but for use on other platforms (so no virtual mem stuff) +#ifndef _XBOX +VOID Compression::VitaVirtualDecompress(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize) // (LPVOID buf, SIZE_T dwSize, LPVOID dst) +{ + uint8_t *pSrc = (uint8_t *)pSource; + int Offset = 0; + int Page = 0; + int Index = 0; + uint8_t* Data = (uint8_t*)pDestination; + while( Index != SrcSize ) + { + // is this a normal value + if( pSrc[Index] ) + { + // just copy it across + Data[Offset] = pSrc[Index]; + Offset += 1; + } + else + { + // how many zeros do we have + Index += 1; + int Count = pSrc[Index]; + // to do : this should really be a sequence of memsets + for( int i = 0;i < Count;i += 1 ) + { + Data[Offset] = 0; + Offset += 1; + } + } + Index += 1; + } + *pDestSize = Offset; +} +#endif + + +HRESULT Compression::DecompressWithType(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize) +{ + switch(m_decompressType) + { + case eCompressionType_RLE: // 4J-JEV, RLE is just that; don't want to break here though. + case eCompressionType_None: + memcpy(pDestination,pSource,SrcSize); + *pDestSize = SrcSize; + return S_OK; + case eCompressionType_LZXRLE: + { +#if (defined _XBOX || defined _DURANGO || defined _WIN64) + SIZE_T destSize = (SIZE_T)(*pDestSize); + HRESULT res = XMemDecompress(decompressionContext, pDestination, (SIZE_T *)&destSize, pSource, SrcSize); + *pDestSize = (unsigned int)destSize; + return res; +#else + assert(0); +#endif + } + break; + case eCompressionType_ZLIBRLE: +#if (defined __ORBIS__ || defined __PS3__ || defined _DURANGO || defined _WIN64) + if (pDestination != NULL) + return ::uncompress((PBYTE)pDestination, (unsigned long *) pDestSize, (PBYTE) pSource, SrcSize); // Decompress + else break; // Cannot decompress when destination is NULL +#else + assert(0); + break; +#endif + case eCompressionType_PS3ZLIB: +#if (defined __ORBIS__ || defined __PSVITA__ || defined _DURANGO || defined _WIN64) + // Note that we're missing the normal zlib header and footer so we'll use inflate to + // decompress the payload and skip all the CRC checking, etc + if (pDestination != NULL) + { + // Read big-endian srcize from array + PBYTE pbDestSize = (PBYTE) pDestSize; + PBYTE pbSource = (PBYTE) pSource; + for (int i = 3; i >= 0; i--) { + pbDestSize[3-i] = pbSource[i]; + } + + byteArray uncompr = byteArray(*pDestSize); + + // Build decompression stream + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.next_out = uncompr.data; + strm.avail_out = uncompr.length; + // Skip those first 4 bytes + strm.next_in = (PBYTE) pSource + 4; + strm.avail_in = SrcSize - 4; + + int hr = inflateInit2(&strm, -15); + + // Run inflate() on input until end of stream + do { + hr = inflate(&strm, Z_NO_FLUSH); + + // Check + switch (hr) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + case Z_STREAM_ERROR: + (void)inflateEnd(&strm); + assert(false); + } + } while (hr != Z_STREAM_END); + + inflateEnd(&strm); // MGH - added, to clean up zlib, was causing a leak on Vita when dowloading a PS3 save + + // Copy the uncompressed data to the destination + memcpy(pDestination, uncompr.data, uncompr.length); + *pDestSize = uncompr.length; + + // Delete uncompressed data + delete uncompr.data; + return S_OK; + } + else break; // Cannot decompress when destination is NULL +#else + assert(0); +#endif + } + + assert(false); + return -1; +} + + + +Compression::Compression() +{ + // Using zlib for x64 compression - 360 is using native 360 compression and PS3 a stubbed non-compressing version of this +#if !(defined __ORBIS__ || defined __PS3__) + // The default parameters for compression context allocated about 6.5MB, reducing the partition size here from the default 512KB to 128KB + // brings this down to about 3MB + XMEMCODEC_PARAMETERS_LZX params; + params.Flags = 0; + params.WindowSize = 128 * 1024; + params.CompressionPartitionSize = 128 * 1024; + + XMemCreateCompressionContext(XMEMCODEC_LZX,¶ms,0,&compressionContext); + XMemCreateDecompressionContext(XMEMCODEC_LZX,¶ms,0,&decompressionContext); +#endif + +#if defined _XBOX + m_localDecompressType = eCompressionType_LZXRLE; +#elif defined __PS3__ + m_localDecompressType = eCompressionType_PS3ZLIB; +#else + m_localDecompressType = eCompressionType_ZLIBRLE; +#endif + m_decompressType = m_localDecompressType; + + InitializeCriticalSection(&rleCompressLock); + InitializeCriticalSection(&rleDecompressLock); +} + +Compression::~Compression() +{ +#if !(defined __ORBIS__ || defined __PS3__ || defined __PSVITA__) + + XMemDestroyCompressionContext(compressionContext); + XMemDestroyDecompressionContext(decompressionContext); +#endif + DeleteCriticalSection(&rleCompressLock); + DeleteCriticalSection(&rleDecompressLock); +} + + + +void Compression::SetDecompressionType(ESavePlatform platform) +{ + switch(platform) + { + case SAVE_FILE_PLATFORM_X360: + Compression::getCompression()->SetDecompressionType(Compression::eCompressionType_LZXRLE); + break; + case SAVE_FILE_PLATFORM_PS3: + Compression::getCompression()->SetDecompressionType(Compression::eCompressionType_PS3ZLIB); + break; + case SAVE_FILE_PLATFORM_XBONE: + case SAVE_FILE_PLATFORM_PS4: + case SAVE_FILE_PLATFORM_PSVITA: + case SAVE_FILE_PLATFORM_WIN64: + Compression::getCompression()->SetDecompressionType(Compression::eCompressionType_ZLIBRLE); + break; + default: + assert(0); + break; + } +} + +/*Compression gCompression;*/ + + diff --git a/Minecraft.World/compression.h b/Minecraft.World/compression.h new file mode 100644 index 00000000..bceac78c --- /dev/null +++ b/Minecraft.World/compression.h @@ -0,0 +1,86 @@ +#pragma once +#include "FileHeader.h" + +#ifdef _XBOX_ONE +#include "..\Minecraft.Client\Durango\DurangoExtras\xcompress.h" +#endif + +class Compression +{ +public: + // Enum maps directly some external tools + enum ECompressionTypes + { + eCompressionType_None = 0, + eCompressionType_RLE = 1, + eCompressionType_LZXRLE = 2, + eCompressionType_ZLIBRLE = 3, + eCompressionType_PS3ZLIB = 4 + }; + +private: + // 4J added so we can have separate contexts and rleBuf for different threads + class ThreadStorage + { + public: + Compression *compression; + ThreadStorage(); + ~ThreadStorage(); + }; + static DWORD tlsIdx; + static ThreadStorage *tlsDefault; +public: + // Each new thread that needs to use Compression will need to call one of the following 2 functions, to either create its own + // local storage, or share the default storage already allocated by the main thread + static void CreateNewThreadStorage(); + static void UseDefaultThreadStorage(); + static void ReleaseThreadStorage(); + + static Compression *getCompression(); + +public: + HRESULT Compress(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize); + HRESULT Decompress(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize); + HRESULT CompressLZXRLE(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize); + HRESULT DecompressLZXRLE(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize); + HRESULT CompressRLE(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize); + HRESULT DecompressRLE(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize); +#ifndef _XBOX + static VOID VitaVirtualDecompress(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize); +#endif + + void SetDecompressionType(ECompressionTypes type) { m_decompressType = type; } // for loading a save from a different platform (Sony cloud storage cross play) + ECompressionTypes GetDecompressionType() { return m_decompressType; } + void SetDecompressionType(ESavePlatform platform); + + Compression(); + ~Compression(); +private: + + HRESULT DecompressWithType(void *pDestination, unsigned int *pDestSize, void *pSource, unsigned int SrcSize); + +#if defined __ORBIS__ || defined __PS3__ +#else + XMEMCOMPRESSION_CONTEXT compressionContext; + XMEMDECOMPRESSION_CONTEXT decompressionContext; +#endif + CRITICAL_SECTION rleCompressLock; + CRITICAL_SECTION rleDecompressLock; + + unsigned char rleCompressBuf[1024*100]; + static const unsigned int staticRleSize = 1024*200; + unsigned char rleDecompressBuf[staticRleSize]; + ECompressionTypes m_decompressType; + ECompressionTypes m_localDecompressType; + +}; + +//extern Compression gCompression; + +#if defined __ORBIS__ || defined _DURANGO || defined _WIN64 || defined __PSVITA__ +#define APPROPRIATE_COMPRESSION_TYPE Compression::eCompressionType_ZLIBRLE +#elif defined __PS3__ +#define APPROPRIATE_COMPRESSION_TYPE Compression::eCompressionType_PS3ZLIB +#else +#define APPROPRIATE_COMPRESSION_TYPE Compression::eCompressionType_LZXRLE +#endif diff --git a/Minecraft.World/net.minecraft.commands.common.h b/Minecraft.World/net.minecraft.commands.common.h new file mode 100644 index 00000000..aa2e2f6f --- /dev/null +++ b/Minecraft.World/net.minecraft.commands.common.h @@ -0,0 +1,10 @@ +#pragma once + +#include "DefaultGameModeCommand.h" +#include "EnchantItemCommand.h" +#include "ExperienceCommand.h" +#include "GameModeCommand.h" +#include "GiveItemCommand.h" +#include "KillCommand.h" +#include "TimeCommand.h" +#include "ToggleDownfallCommand.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.commands.h b/Minecraft.World/net.minecraft.commands.h new file mode 100644 index 00000000..616b2e07 --- /dev/null +++ b/Minecraft.World/net.minecraft.commands.h @@ -0,0 +1,7 @@ +#pragma once + +#include "AdminLogCommand.h" +#include "CommandsEnum.h" +#include "Command.h" +#include "CommandDispatcher.h" +#include "CommandSender.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.h b/Minecraft.World/net.minecraft.h new file mode 100644 index 00000000..203a8b93 --- /dev/null +++ b/Minecraft.World/net.minecraft.h @@ -0,0 +1,6 @@ +#pragma once + +#include "Facing.h" +#include "Direction.h" +#include "Pos.h" +#include "SharedConstants.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.locale.h b/Minecraft.World/net.minecraft.locale.h new file mode 100644 index 00000000..b6ed8a10 --- /dev/null +++ b/Minecraft.World/net.minecraft.locale.h @@ -0,0 +1,4 @@ +#pragma once + +#include "Language.h" +#include "I18n.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.network.h b/Minecraft.World/net.minecraft.network.h new file mode 100644 index 00000000..cbeae475 --- /dev/null +++ b/Minecraft.World/net.minecraft.network.h @@ -0,0 +1,3 @@ +#pragma once + +#include "Connection.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.network.packet.h b/Minecraft.World/net.minecraft.network.packet.h new file mode 100644 index 00000000..b7bfee5d --- /dev/null +++ b/Minecraft.World/net.minecraft.network.packet.h @@ -0,0 +1,102 @@ +#pragma once + +#include "AddEntityPacket.h" +#include "AddGlobalEntityPacket.h" +#include "AddMobPacket.h" +#include "AddPaintingPacket.h" +#include "AddPlayerPacket.h" +#include "AnimatePacket.h" +#include "AwardStatPacket.h" +#include "BlockRegionUpdatePacket.h" +#include "ChatPacket.h" +#include "ChunkTilesUpdatePacket.h" +#include "ChunkVisibilityPacket.h" +#include "ComplexItemDataPacket.h" +#include "ContainerAckPacket.h" +#include "ContainerClickPacket.h" +#include "ContainerClosePacket.h" +#include "ContainerOpenPacket.h" +#include "ContainerSetContentPacket.h" +#include "ContainerSetDataPacket.h" +#include "ContainerSetSlotPacket.h" +#include "DisconnectPacket.h" +#include "EntityActionAtPositionPacket.h" +#include "EntityEventPacket.h" +#include "ExplodePacket.h" +#include "GameEventPacket.h" +#include "InteractPacket.h" +#include "KeepAlivePacket.h" +#include "LevelEventPacket.h" +#include "LoginPacket.h" +#include "MoveEntityPacket.h" +#include "MoveEntityPacketSmall.h" +#include "MovePlayerPacket.h" +#include "Packet.h" +#include "PacketListener.h" +#include "PlayerActionPacket.h" +#include "PlayerCommandPacket.h" +#include "PlayerInputPacket.h" +#include "PreLoginPacket.h" +#include "RemoveEntitiesPacket.h" +#include "RespawnPacket.h" + +#include "SetCarriedItemPacket.h" +#include "SetEntityDataPacket.h" +#include "SetEntityMotionPacket.h" +#include "SetEquippedItemPacket.h" +#include "SetHealthPacket.h" +#include "SetRidingPacket.h" +#include "SetSpawnPositionPacket.h" +#include "SetTimePacket.h" +#include "SignUpdatePacket.h" +#include "TakeItemEntityPacket.h" +#include "TeleportEntityPacket.h" +#include "TileEventPacket.h" +#include "TileUpdatePacket.h" +#include "UseItemPacket.h" + +// 1.8.2 Added +#include "AddExperienceOrbPacket.h" +#include "GetInfoPacket.h" +#include "PlayerInfoPacket.h" +#include "RemoveMobEffectPacket.h" +#include "SetCreativeModeSlotPacket.h" +#include "SetExperiencePacket.h" +#include "UpdateMobEffectPacket.h" + +// 1.0.1 Added +#include "ContainerButtonClickPacket.h" +#include "TileEntityDataPacket.h" + +// 1.1 Added (TU9) +#include "CustomPayloadPacket.h" + +// 1.2.3 +#include "RotateHeadPacket.h" + +// 1.3.2 +#include "ChatAutoCompletePacket.h" +#include "ClientCommandPacket.h" +#include "ClientInformationPacket.h" +#include "ClientProtocolPacket.h" +#include "LevelSoundPacket.h" +#include "PlayerAbilitiesPacket.h" +#include "ServerAuthDataPacket.h" +#include "TileDestructionPacket.h" + +// 4J Added +#include "CraftItemPacket.h" +#include "TradeItemPacket.h" +#include "DebugOptionsPacket.h" +#include "ServerSettingsChangedPacket.h" +#include "TexturePacket.h" +#include "TextureAndGeometryPacket.h" +#include "ChunkVisibilityAreaPacket.h" +#include "UpdateProgressPacket.h" +#include "TextureChangePacket.h" +#include "TextureAndGeometryChangePacket.h" +#include "UpdateGameRuleProgressPacket.h" +#include "KickPlayerPacket.h" +#include "XZPacket.h" +#include "GameCommandPacket.h" + diff --git a/Minecraft.World/net.minecraft.stats.h b/Minecraft.World/net.minecraft.stats.h new file mode 100644 index 00000000..52cc4afc --- /dev/null +++ b/Minecraft.World/net.minecraft.stats.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Achievement.h" +#include "Achievements.h" +#include "DescFormatter.h" +#include "GeneralStat.h" +#include "ItemStat.h" +#include "Stat.h" +#include "StatFormatter.h" +#include "Stats.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.ContainerListener.h b/Minecraft.World/net.minecraft.world.ContainerListener.h new file mode 100644 index 00000000..1ac036a5 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.ContainerListener.h @@ -0,0 +1,18 @@ +#pragma once + +class SimpleContainer; + +// TODO 4J Stu +// There are 2 classes called ContainerListener. One in net.minecraft.world.inventory and +// another one in net.minecraft.world . To avoid clashes I have renamed both and put them in a namespace +// to avoid confusion. + +namespace net_minecraft_world +{ + class ContainerListener + { + friend class SimpleContainer; + private: + virtual void containerChanged(shared_ptr simpleContainer) = 0; + }; +} \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.damagesource.h b/Minecraft.World/net.minecraft.world.damagesource.h new file mode 100644 index 00000000..03f354ec --- /dev/null +++ b/Minecraft.World/net.minecraft.world.damagesource.h @@ -0,0 +1,5 @@ +#pragma once + +#include "DamageSource.h" +#include "EntityDamageSource.h" +#include "IndirectEntityDamageSource.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.effect.h b/Minecraft.World/net.minecraft.world.effect.h new file mode 100644 index 00000000..f5bd448f --- /dev/null +++ b/Minecraft.World/net.minecraft.world.effect.h @@ -0,0 +1,5 @@ +#pragma once + +#include "MobEffect.h" +#include "InstantenousMobEffect.h" +#include "MobEffectInstance.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.ai.control.h b/Minecraft.World/net.minecraft.world.entity.ai.control.h new file mode 100644 index 00000000..d0a3e783 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.ai.control.h @@ -0,0 +1,7 @@ +#pragma once + +#include "Control.h" +#include "BodyControl.h" +#include "JumpControl.h" +#include "LookControl.h" +#include "MoveControl.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.ai.goal.h b/Minecraft.World/net.minecraft.world.entity.ai.goal.h new file mode 100644 index 00000000..5944daf2 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.ai.goal.h @@ -0,0 +1,41 @@ +#pragma once + +#include "Goal.h" +#include "ArrowAttackGoal.h" +#include "AvoidPlayerGoal.h" +#include "BegGoal.h" +#include "BreakDoorGoal.h" +#include "BreedGoal.h" +#include "ControlledByPlayerGoal.h" +#include "DoorInteractGoal.h" +#include "EatTileGoal.h" +#include "FleeSunGoal.h" +#include "FollowOwnerGoal.h" +#include "FollowParentGoal.h" +#include "FloatGoal.h" +#include "GoalSelector.h" +#include "InteractGoal.h" +#include "LeapAtTargetGoal.h" +#include "LookAtPlayerGoal.h" +#include "LookAtTradingPlayerGoal.h" +#include "MakeLoveGoal.h" +#include "MeleeAttackGoal.h" +#include "MoveIndoorsGoal.h" +#include "MoveThroughVillageGoal.h" +#include "MoveTowardsRestrictionGoal.h" +#include "MoveTowardsTargetGoal.h" +#include "OcelotSitOnTileGoal.h" +#include "OfferFlowerGoal.h" +#include "OpenDoorGoal.h" +#include "OzelotAttackGoal.h" +#include "PanicGoal.h" +#include "PlayGoal.h" +#include "RandomLookAroundGoal.h" +#include "RandomStrollGoal.h" +#include "RestrictOpenDoorGoal.h" +#include "RestrictSunGoal.h" +#include "SitGoal.h" +#include "SwellGoal.h" +#include "TakeFlowerGoal.h" +#include "TemptGoal.h" +#include "TradeWithPlayerGoal.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.ai.goal.target.h b/Minecraft.World/net.minecraft.world.entity.ai.goal.target.h new file mode 100644 index 00000000..98b96a16 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.ai.goal.target.h @@ -0,0 +1,9 @@ +#pragma once + +#include "TargetGoal.h" +#include "DefendVillageTargetGoal.h" +#include "HurtByTargetGoal.h" +#include "NearestAttackableTargetGoal.h" +#include "NonTameRandomTargetGoal.h" +#include "OwnerHurtByTargetGoal.h" +#include "OwnerHurtTargetGoal.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.ai.navigation.h b/Minecraft.World/net.minecraft.world.entity.ai.navigation.h new file mode 100644 index 00000000..ca7e39ff --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.ai.navigation.h @@ -0,0 +1,3 @@ +#pragma once + +#include "PathNavigation.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.ai.sensing.h b/Minecraft.World/net.minecraft.world.entity.ai.sensing.h new file mode 100644 index 00000000..20410c95 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.ai.sensing.h @@ -0,0 +1,3 @@ +#pragma once + +#include "Sensing.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.ai.util.h b/Minecraft.World/net.minecraft.world.entity.ai.util.h new file mode 100644 index 00000000..de67b5be --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.ai.util.h @@ -0,0 +1,3 @@ +#pragma once + +#include "RandomPos.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.ai.village.h b/Minecraft.World/net.minecraft.world.entity.ai.village.h new file mode 100644 index 00000000..7aeb2d82 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.ai.village.h @@ -0,0 +1,6 @@ +#pragma once + +#include "DoorInfo.h" +#include "Village.h" +#include "Villages.h" +#include "VillageSiege.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.animal.h b/Minecraft.World/net.minecraft.world.entity.animal.h new file mode 100644 index 00000000..efeb2c96 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.animal.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Animal.h" +#include "Chicken.h" +#include "Cow.h" +#include "Pig.h" +#include "Sheep.h" +#include "Squid.h" +#include "WaterAnimal.h" +#include "Wolf.h" + +// 1.0.1 +#include "Golem.h" +#include "MushroomCow.h" +#include "SnowMan.h" + +// 1.2.3 +#include "TamableAnimal.h" +#include "Ozelot.h" +#include "VillagerGolem.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.boss.enderdragon.h b/Minecraft.World/net.minecraft.world.entity.boss.enderdragon.h new file mode 100644 index 00000000..106240bf --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.boss.enderdragon.h @@ -0,0 +1,5 @@ +#pragma once + +#include "EnderCrystal.h" +#include "EnderDragon.h" +#include "NetherSphere.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.boss.h b/Minecraft.World/net.minecraft.world.entity.boss.h new file mode 100644 index 00000000..15aa271a --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.boss.h @@ -0,0 +1,4 @@ +#pragma once + +#include "BossMob.h" +#include "BossMobPart.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.global.h b/Minecraft.World/net.minecraft.world.entity.global.h new file mode 100644 index 00000000..51123d90 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.global.h @@ -0,0 +1,4 @@ +#pragma once + +#include "GlobalEntity.h" +#include "LightningBolt.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.h b/Minecraft.World/net.minecraft.world.entity.h new file mode 100644 index 00000000..9aa62632 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Creature.h" +#include "SynchedEntityData.h" +#include "Entity.h" +#include "EntityEvent.h" +#include "EntityIO.h" +#include "EntityPos.h" +#include "FlyingMob.h" +#include "Mob.h" +#include "MobCategory.h" +#include "Painting.h" +#include "PathfinderMob.h" + +// 1.8.2 +#include "DelayedRelease.h" +#include "ExperienceOrb.h" + +// 1.0.1 +#include "MobType.h" + +// TU9 +#include "ItemFrame.h" + +// 1.2.3 +#include "AgableMob.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.item.h b/Minecraft.World/net.minecraft.world.entity.item.h new file mode 100644 index 00000000..e35176bc --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.item.h @@ -0,0 +1,7 @@ +#pragma once + +#include "Boat.h" +#include "FallingTile.h" +#include "ItemEntity.h" +#include "Minecart.h" +#include "PrimedTnt.h" diff --git a/Minecraft.World/net.minecraft.world.entity.monster.h b/Minecraft.World/net.minecraft.world.entity.monster.h new file mode 100644 index 00000000..1d6b8495 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.monster.h @@ -0,0 +1,21 @@ +#pragma once + +#include "Enemy.h" +#include "Monster.h" +#include "Creeper.h" +#include "Ghast.h" +#include "Giant.h" +#include "Zombie.h" +#include "PigZombie.h" +#include "Skeleton.h" +#include "Slime.h" +#include "Spider.h" + +// 1.8.2 +#include "CaveSpider.h" +#include "EnderMan.h" +#include "Silverfish.h" + +// 1.0.1 +#include "Blaze.h" +#include "LavaSlime.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.npc.h b/Minecraft.World/net.minecraft.world.entity.npc.h new file mode 100644 index 00000000..2874bcd7 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.npc.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Npc.h" +#include "Villager.h" +#include "ClientSideMerchant.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.player.h b/Minecraft.World/net.minecraft.world.entity.player.h new file mode 100644 index 00000000..9394b89b --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.player.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Inventory.h" +#include "Player.h" +#include "Abilities.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.entity.projectile.h b/Minecraft.World/net.minecraft.world.entity.projectile.h new file mode 100644 index 00000000..3e56b04e --- /dev/null +++ b/Minecraft.World/net.minecraft.world.entity.projectile.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Arrow.h" +#include "Fireball.h" +#include "FishingHook.h" +#include "Snowball.h" +#include "ThrownEgg.h" + +// 1.0.1 +#include "EyeOfEnderSignal.h" +#include "SmallFireball.h" +#include "Throwable.h" +#include "ThrownEnderpearl.h" +#include "ThrownPotion.h" +#include "ThrownExpBottle.h" // Brought forward from 1.2 + +// Added TU 9 +#include "DragonFireball.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.food.h b/Minecraft.World/net.minecraft.world.food.h new file mode 100644 index 00000000..91b1916a --- /dev/null +++ b/Minecraft.World/net.minecraft.world.food.h @@ -0,0 +1,4 @@ +#pragma once + +#include "FoodConstants.h" +#include "FoodData.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.h b/Minecraft.World/net.minecraft.world.h new file mode 100644 index 00000000..4e600b33 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.h @@ -0,0 +1,13 @@ +#pragma once + +#include "CompoundContainer.h" +#include "Container.h" +#include "Difficulty.h" +#include "MouseInventoryClickHandler.h" +#include "net.minecraft.world.ContainerListener.h" +#include "SimpleContainer.h" + +// TU10 +#include "Icon.h" +#include "IconRegister.h" +#include "FlippedIcon.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.inventory.ContainerListener.h b/Minecraft.World/net.minecraft.world.inventory.ContainerListener.h new file mode 100644 index 00000000..ff9dfa2e --- /dev/null +++ b/Minecraft.World/net.minecraft.world.inventory.ContainerListener.h @@ -0,0 +1,21 @@ +#pragma once +using namespace std; +class AbstractContainerMenu; + +// 4J Stu +// There are 2 classes called ContainerListener. Once here in net.minecraft.world.inventory and +// another once in net.minecraft.world . To avoid clashes I have renamed both and put them in a namespace +// to avoid confusion. + +namespace net_minecraft_world_inventory +{ + class ContainerListener + { + public: + virtual void refreshContainer(AbstractContainerMenu *container, vector > *items) = 0; + + virtual void slotChanged(AbstractContainerMenu *container, int slotIndex, shared_ptr item) = 0; + + virtual void setContainerData(AbstractContainerMenu *container, int id, int value) = 0; + }; +} \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.inventory.h b/Minecraft.World/net.minecraft.world.inventory.h new file mode 100644 index 00000000..a2608dbd --- /dev/null +++ b/Minecraft.World/net.minecraft.world.inventory.h @@ -0,0 +1,27 @@ +#pragma once + +#include "AbstractContainerMenu.h" +#include "ArmorSlot.h" +#include "net.minecraft.world.inventory.ContainerListener.h" +#include "ContainerMenu.h" +#include "CraftingContainer.h" +#include "CraftingMenu.h" +#include "FurnaceMenu.h" +#include "FurnaceResultSlot.h" +#include "InventoryMenu.h" +#include "MenuBackup.h" +#include "ResultContainer.h" +#include "ResultSlot.h" +#include "Slot.h" +#include "TrapMenu.h" +#include "EnchantmentMenu.h" +#include "EnchantmentSlot.h" +#include "EnchantmentContainer.h" +#include "BrewingStandMenu.h" +#include "MerchantContainer.h" +#include "MerchantMenu.h" +#include "MerchantResultSlot.h" +#include "PlayerEnderChestContainer.h" +#include "RepairMenu.h" +#include "RepairContainer.h" +#include "RepairResultSlot.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.item.alchemy.h b/Minecraft.World/net.minecraft.world.item.alchemy.h new file mode 100644 index 00000000..fe69821f --- /dev/null +++ b/Minecraft.World/net.minecraft.world.item.alchemy.h @@ -0,0 +1,3 @@ +#pragma once + +#include "PotionBrewing.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.item.crafting.h b/Minecraft.World/net.minecraft.world.item.crafting.h new file mode 100644 index 00000000..ab6a32eb --- /dev/null +++ b/Minecraft.World/net.minecraft.world.item.crafting.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Recipes.h" +#include "Recipy.h" +#include "ArmorDyeRecipe.h" +#include "ArmorRecipes.h" +#include "ClothDyeRecipes.h" +#include "FoodRecipies.h" +#include "FurnaceRecipes.h" +#include "OreRecipies.h" +#include "ShapedRecipy.h" +#include "ShapelessRecipy.h" +#include "StructureRecipies.h" +#include "ToolRecipies.h" +#include "WeaponRecipies.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.item.enchantment.h b/Minecraft.World/net.minecraft.world.item.enchantment.h new file mode 100644 index 00000000..c0df5226 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.item.enchantment.h @@ -0,0 +1,24 @@ +#pragma once + +#include "DamageEnchantment.h" +#include "DigDurabilityEnchantment.h" +#include "DiggingEnchantment.h" +#include "Enchantment.h" +#include "EnchantmentCategory.h" +#include "EnchantmentHelper.h" +#include "EnchantmentInstance.h" +#include "FireAspectEnchantment.h" +#include "KnockbackEnchantment.h" +#include "LootBonusEnchantment.h" +#include "OxygenEnchantment.h" +#include "ProtectionEnchantment.h" +#include "UntouchingEnchantment.h" +#include "WaterWorkerEnchantment.h" + +// 4J Stu - Brought forward +#include "ArrowDamageEnchantment.h" +#include "ArrowFireEnchantment.h" +#include "ArrowInfiniteEnchantment.h" +#include "ArrowKnockbackEnchantment.h" + +#include "ThornsEnchantment.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.item.h b/Minecraft.World/net.minecraft.world.item.h new file mode 100644 index 00000000..0300b954 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.item.h @@ -0,0 +1,80 @@ +#pragma once + +#include "ArmorItem.h" +#include "BedItem.h" +#include "BoatItem.h" +#include "BowItem.h" +#include "BowlFoodItem.h" +#include "BucketItem.h" +#include "ClothTileItem.h" +#include "CoalItem.h" +#include "ComplexItem.h" +#include "DiggerItem.h" +#include "DoorItem.h" +#include "DyePowderItem.h" +#include "EggItem.h" +#include "FishingRodItem.h" +#include "FlintAndSteelItem.h" +#include "FoodItem.h" +#include "HatchetItem.h" +#include "HoeItem.h" +#include "Item.h" +#include "ItemInstance.h" +#include "LeafTileItem.h" +#include "MapItem.h" +#include "MinecartItem.h" +//#include "PaintingItem.h" +#include "PickaxeItem.h" +#include "PistonTileItem.h" +#include "RecordingItem.h" +#include "RedStoneItem.h" +#include "SaddleItem.h" +#include "SaplingTileItem.h" +#include "SeedItem.h" +#include "ShearsItem.h" +#include "ShovelItem.h" +#include "SignItem.h" +#include "SnowBallItem.h" +#include "StoneSlabTileItem.h" +#include "TileItem.h" +#include "TilePlanterItem.h" +#include "TreeTileItem.h" +#include "WeaponItem.h" + +// 1.8.2 +#include "AuxDataTileItem.h" +#include "ColoredTileItem.h" +#include "UseAnim.h" +#include "StoneMonsterTileItem.h" + +// 1.0.1 +#include "BottleItem.h" +#include "EnderEyeItem.h" +#include "EnderpearlItem.h" +#include "GoldenAppleItem.h" +#include "MilkBucketItem.h" +#include "PotionItem.h" +#include "Rarity.h" +#include "WaterLilyTileItem.h" +#include "ExperienceItem.h" // 4J Stu brought forward +#include "SmoothStoneBrickTileItem.h" // 4J Stu brought forward + +// TU9 +#include "FireChargeItem.h" +#include "ItemFrame.h" +#include "MonsterPlacerItem.h" +#include "MultiTextureTileItem.h" + +// TU12 +#include "SkullItem.h" + +// TU14 +#include "AnvilTileItem.h" +#include "BookItem.h" +#include "CarrotOnAStickItem.h" +#include "EnchantedBookItem.h" +#include "SeedFoodItem.h" + +// 4J Added +#include "ClockItem.h" +#include "CompassItem.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.item.trading.h b/Minecraft.World/net.minecraft.world.item.trading.h new file mode 100644 index 00000000..a705a1c0 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.item.trading.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Merchant.h" +#include "MerchantRecipe.h" +#include "MerchantRecipeList.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.biome.h b/Minecraft.World/net.minecraft.world.level.biome.h new file mode 100644 index 00000000..dc082d70 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.biome.h @@ -0,0 +1,32 @@ +#pragma once + + +#include "Biome.h" +#include "ForestBiome.h" +#include "HellBiome.h" +#include "RainforestBiome.h" +#include "IceBiome.h" +#include "MushroomIslandBiome.h" +#include "TheEndBiome.h" +#include "TheEndBiomeDecorator.h" +#include "WaterlilyFeature.h" +#include "SwampBiome.h" +#include "TaigaBiome.h" +#include "LevelSource.h" +#include "BiomeSource.h" +#include "FixedBiomeSource.h" + +// 1.8.2 +#include "BiomeCache.h" +#include "BiomeDecorator.h" +#include "DesertBiome.h" +#include "ExtremeHillsBiome.h" +#include "OceanBiome.h" +#include "PlainsBiome.h" +#include "RiverBiome.h" + +// 1.1 +#include "BeachBiome.h" + +//1.2.3 +#include "JungleBiome.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.chunk.h b/Minecraft.World/net.minecraft.world.level.chunk.h new file mode 100644 index 00000000..d98bbe5c --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.chunk.h @@ -0,0 +1,8 @@ +#pragma once + +#include "BlockReplacements.h" +#include "ChunkSource.h" +#include "DataLayer.h" +#include "EmptyLevelChunk.h" +#include "LevelChunk.h" +#include "ReadOnlyChunkCache.h" diff --git a/Minecraft.World/net.minecraft.world.level.chunk.storage.h b/Minecraft.World/net.minecraft.world.level.chunk.storage.h new file mode 100644 index 00000000..114264ec --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.chunk.storage.h @@ -0,0 +1,13 @@ +#pragma once + +#include "ChunkStorage.h" +#include "ChunkStorageProfileDecorator.h" +#include "MemoryChunkStorage.h" +#include "McRegionChunkStorage.h" +#include "NbtSlotFile.h" +#include "OldChunkStorage.h" +#include "RegionFile.h" +#include "RegionFileCache.h" +#include "ZonedChunkStorage.h" +#include "ZoneFile.h" +#include "ZoneIo.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.dimension.h b/Minecraft.World/net.minecraft.world.level.dimension.h new file mode 100644 index 00000000..0c9f41c2 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.dimension.h @@ -0,0 +1,6 @@ +#pragma once + +#include "Dimension.h" +#include "HellDimension.h" +#include "NormalDimension.h" +#include "TheEndDimension.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.h b/Minecraft.World/net.minecraft.world.level.h new file mode 100644 index 00000000..01d14fcb --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.h @@ -0,0 +1,25 @@ +#pragma once + +#include "ChunkPos.h" +#include "Coord.h" +#include "Explosion.h" +#include "FoliageColor.h" +#include "GrassColor.h" +#include "LevelConflictException.h" +#include "LevelListener.h" +#include "LevelSource.h" +#include "LightLayer.h" +#include "MobSpawner.h" +#include "PortalForcer.h" +#include "Region.h" +#include "TickNextTickData.h" +#include "TilePos.h" +#include "WaterColor.h" +#include "Level.h" +#include "LevelType.h" +#include "LevelSettings.h" + +// TU 10 +#include "BlockDestructionProgress.h" + +#include "TileEventData.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.levelgen.feature.h b/Minecraft.World/net.minecraft.world.level.levelgen.feature.h new file mode 100644 index 00000000..81ac8637 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.levelgen.feature.h @@ -0,0 +1,37 @@ +#pragma once + +#include "BasicTree.h" +#include "BirchFeature.h" +#include "CactusFeature.h" +#include "CaveFeature.h" +#include "ClayFeature.h" +#include "DeadBushFeature.h" +#include "Feature.h" +#include "FlowerFeature.h" +#include "HellFireFeature.h" +#include "HellPortalFeature.h" +#include "HellSpringFeature.h" +#include "HouseFeature.h" +#include "LakeFeature.h" +#include "LightGemFeature.h" +#include "MonsterRoomFeature.h" +#include "OreFeature.h" +#include "PineFeature.h" +#include "PumpkinFeature.h" +#include "ReedsFeature.h" +#include "SpringFeature.h" +#include "SpruceFeature.h" +#include "TallGrassFeature.h" +#include "TreeFeature.h" + +#include "HugeMushroomFeature.h" +#include "SandFeature.h" +#include "SwampTreeFeature.h" +#include "BonusChestFeature.h" +#include "SpikeFeature.h" +#include "EndPodiumFeature.h" + +#include "DesertWellFeature.h" +#include "MegaTreeFeature.h" +#include "VinesFeature.h" +#include "GroundBushFeature.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.levelgen.h b/Minecraft.World/net.minecraft.world.level.levelgen.h new file mode 100644 index 00000000..ea17a33c --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.levelgen.h @@ -0,0 +1,15 @@ +#pragma once + +#include "CanyonFeature.h" +#include "DungeonFeature.h" +#include "HellRandomLevelSource.h" +#include "HellFlatLevelSource.h" +#include "LargeCaveFeature.h" +#include "LargeFeature.h" +#include "LargeHellCaveFeature.h" +#include "RandomLevelSource.h" +#include "FlatLevelSource.h" +#include "TownFeature.h" +#include "TheEndLevelRandomLevelSource.h" + +#include "CustomLevelSource.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.levelgen.structure.h b/Minecraft.World/net.minecraft.world.level.levelgen.structure.h new file mode 100644 index 00000000..2bd9cfa2 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.levelgen.structure.h @@ -0,0 +1,16 @@ +#pragma once + +#include "BlockGenMethods.h" +#include "BoundingBox.h" +#include "MineShaftFeature.h" +#include "MineShaftPieces.h" +#include "MineShaftStart.h" +#include "StrongholdFeature.h" +#include "StrongholdPieces.h" +#include "StructureFeature.h" +#include "StructurePiece.h" +#include "StructureStart.h" +#include "VillageFeature.h" +#include "VillagePieces.h" +#include "RandomScatteredLargeFeature.h" +#include "ScatteredFeaturePieces.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.levelgen.synth.h b/Minecraft.World/net.minecraft.world.level.levelgen.synth.h new file mode 100644 index 00000000..a0f5bf1f --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.levelgen.synth.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Distort.h" +#include "Emboss.h" +#include "FastNoise.h" +#include "ImprovedNoise.h" +#include "PerlinNoise.h" +#include "PerlinSimplexNoise.h" +#include "Rotate.h" +#include "Scale.h" +#include "SimplexNoise.h" +#include "Synth.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.material.h b/Minecraft.World/net.minecraft.world.level.material.h new file mode 100644 index 00000000..173ac53a --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.material.h @@ -0,0 +1,8 @@ +#pragma once + +#include "DecorationMaterial.h" +#include "GasMaterial.h" +#include "LiquidMaterial.h" +#include "Material.h" +#include "MaterialColor.h" +#include "PortalMaterial.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.newbiome.layer.h b/Minecraft.World/net.minecraft.world.level.newbiome.layer.h new file mode 100644 index 00000000..c45659d4 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.newbiome.layer.h @@ -0,0 +1,28 @@ +#pragma once + +#include "AddIslandLayer.h" +#include "AddMushroomIslandLayer.h" +#include "AddSnowLayer.h" +#include "BiomeInitLayer.h" +#include "DownfallLayer.h" +#include "DownfallMixerLayer.h" +#include "FlatLayer.h" +#include "FuzzyZoomLayer.h" +#include "IntCache.h" +#include "IslandLayer.h" +#include "Layer.h" +#include "RiverInitLayer.h" +#include "RiverLayer.h" +#include "RiverMixerLayer.h" +#include "ShoreLayer.h" +#include "SmoothLayer.h" +#include "SmoothZoomLayer.h" +#include "TemperatureLayer.h" +#include "TemperatureMixerLayer.h" +#include "VoronoiZoom.h" +#include "ZoomLayer.h" +#include "GrowMushroomIslandLayer.h" // 4J added + +// 1.1. +#include "RegionHillsLayer.h" +#include "SwampRiversLayer.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.pathfinder.h b/Minecraft.World/net.minecraft.world.level.pathfinder.h new file mode 100644 index 00000000..acfbbea8 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.pathfinder.h @@ -0,0 +1,6 @@ +#pragma once + +#include "BinaryHeap.h" +#include "Node.h" +#include "Path.h" +#include "PathFinder.h" diff --git a/Minecraft.World/net.minecraft.world.level.saveddata.h b/Minecraft.World/net.minecraft.world.level.saveddata.h new file mode 100644 index 00000000..11878d9e --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.saveddata.h @@ -0,0 +1,4 @@ +#pragma once + +#include "SavedData.h" +#include "MapItemSavedData.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.level.storage.h b/Minecraft.World/net.minecraft.world.level.storage.h new file mode 100644 index 00000000..262e1005 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.storage.h @@ -0,0 +1,16 @@ +#pragma once + +#include "DirectoryLevelStorage.h" +#include "DirectoryLevelStorageSource.h" +#include "LevelData.h" +#include "LevelStorage.h" +#include "LevelStorageProfilerDecorator.h" +#include "LevelStorageSource.h" +#include "LevelSummary.h" +#include "McRegionLevelStorage.h" +#include "McRegionLevelStorageSource.h" +//#include "MemoryLevelStorage.h" // 4J removed as unused +//#include "MemoryLevelStorageSource.h" // 4J removed as unused +#include "MockedLevelStorage.h" +#include "PlayerIO.h" +#include "SavedDataStorage.h" diff --git a/Minecraft.World/net.minecraft.world.level.tile.entity.h b/Minecraft.World/net.minecraft.world.level.tile.entity.h new file mode 100644 index 00000000..0b6cb04a --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.tile.entity.h @@ -0,0 +1,15 @@ +#pragma once + +#include "BrewingStandTileEntity.h" +#include "ChestTileEntity.h" +#include "DispenserTileEntity.h" +#include "EnchantmentTableEntity.h" +#include "FurnaceTileEntity.h" +#include "MobSpawnerTileEntity.h" +#include "MusicTileEntity.h" +#include "SignTileEntity.h" +#include "TileEntity.h" +#include "PistonMovingPiece.h" +#include "TheEndPortalTileEntity.h" +#include "SkullTileEntity.h" +#include "EnderChestTileEntity.h" diff --git a/Minecraft.World/net.minecraft.world.level.tile.h b/Minecraft.World/net.minecraft.world.level.tile.h new file mode 100644 index 00000000..f039b000 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.tile.h @@ -0,0 +1,114 @@ +#pragma once + +#include "Tile.h" +#include "AirTile.h" +#include "AnvilTile.h" +#include "BedTile.h" +#include "BookshelfTile.h" +#include "BrewingStandTile.h" +#include "Bush.h" +#include "ButtonTile.h" +#include "CactusTile.h" +#include "CakeTile.h" +#include "CarrotTile.h" +#include "CauldronTile.h" +#include "ChestTile.h" +#include "ClayTile.h" +#include "ClothTile.h" +#include "CocoaTile.h" +#include "CoralTile.h" +#include "CropTile.h" +#include "DeadBushTile.h" +#include "DetectorRailTile.h" +#include "DiodeTile.h" +#include "DirectionalTile.h" +#include "DirtTile.h" +#include "DispenserTile.h" +#include "DoorTile.h" +#include "EggTile.h" +#include "EnchantmentTableTile.h" +#include "EnderChestTile.h" +#include "EntityTile.h" +#include "FarmTile.h" +#include "FenceGateTile.h" +#include "FenceTile.h" +#include "FireTile.h" +#include "FlowerPotTile.h" +#include "FurnaceTile.h" +#include "GlassTile.h" +#include "GrassTile.h" +#include "GravelTile.h" +#include "HalfTransparentTile.h" +#include "HeavyTile.h" +#include "HellSandTile.h" +#include "HellStoneTile.h" +#include "HugeMushroomTile.h" +#include "IceTile.h" +#include "LadderTile.h" +#include "LeafTile.h" +#include "LevelEvent.h" +#include "LeverTile.h" +#include "LightGemTile.h" +#include "LiquidTile.h" +#include "LiquidTileDynamic.h" +#include "LiquidTileStatic.h" +#include "LockedChestTile.h" +#include "MelonTile.h" +#include "MetalTile.h" +#include "MobSpawnerTile.h" +#include "Mushroom.h" +#include "MusicTile.h" +#include "MycelTile.h" +#include "NetherStalkTile.h" +#include "NotGateTile.h" +#include "ObsidianTile.h" +#include "OreTile.h" +#include "PistonBaseTile.h" +#include "PistonExtensionTile.h" +#include "PortalTile.h" +#include "PotatoTile.h" +#include "PressurePlateTile.h" +#include "PumpkinTile.h" +#include "QuartzBlockTile.h" +#include "RailTile.h" +#include "RecordPlayerTile.h" +#include "RedlightTile.h" +#include "RedStoneDustTile.h" +#include "RedStoneOreTile.h" +#include "ReedTile.h" +#include "SandStoneTile.h" +#include "Sapling.h" +#include "SignTile.h" +#include "SkullTile.h" +#include "SmoothStoneBrickTile.h" +#include "SnowTile.h" +#include "Sponge.h" +#include "SpringTile.h" +#include "StairTile.h" +#include "StemTile.h" +#include "StoneMonsterTile.h" +#include "StoneSlabTile.h" +#include "StoneTile.h" +#include "TallGrass.h" +#include "TheEndPortal.h" +#include "TheEndPortalFrameTile.h" +#include "ThinFenceTile.h" +#include "TntTile.h" +#include "TopSnowTile.h" +#include "TorchTile.h" +#include "TransparentTile.h" +#include "TrapDoorTile.h" +#include "TreeTile.h" +#include "TripWireSourceTile.h" +#include "TripWireTile.h" +#include "VineTile.h" +#include "WallTile.h" +#include "WaterLilyTile.h" +#include "WebTile.h" +#include "WorkbenchTile.h" +#include "WoodTile.h" +#include "HalfSlabTile.h" +#include "WoodSlabTile.h" +#include "WoolCarpetTile.h" + + diff --git a/Minecraft.World/net.minecraft.world.level.tile.piston.h b/Minecraft.World/net.minecraft.world.level.tile.piston.h new file mode 100644 index 00000000..b02e3428 --- /dev/null +++ b/Minecraft.World/net.minecraft.world.level.tile.piston.h @@ -0,0 +1,6 @@ +#pragma once + +#include "PistonBaseTile.h" +#include "PistonExtensionTile.h" +#include "PistonMovingPiece.h" +#include "PistonPieceEntity.h" \ No newline at end of file diff --git a/Minecraft.World/net.minecraft.world.phys.h b/Minecraft.World/net.minecraft.world.phys.h new file mode 100644 index 00000000..f5dfd29b --- /dev/null +++ b/Minecraft.World/net.minecraft.world.phys.h @@ -0,0 +1,3 @@ +#include "AABB.h" +#include "HitResult.h" +#include "Vec3.h" \ No newline at end of file diff --git a/Minecraft.World/stdafx.cpp b/Minecraft.World/stdafx.cpp new file mode 100644 index 00000000..8e08093d --- /dev/null +++ b/Minecraft.World/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// Minecraft.World.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/Minecraft.World/stdafx.h b/Minecraft.World/stdafx.h new file mode 100644 index 00000000..614f997b --- /dev/null +++ b/Minecraft.World/stdafx.h @@ -0,0 +1,247 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// +#pragma once + +#ifdef __PS3__ +#else +#define AUTO_VAR(_var, _val) auto _var = _val +#endif + +#if ( defined _XBOX || defined _WINDOWS64 || defined _DURANGO ) +typedef unsigned __int64 __uint64; +#endif + +#ifdef _WINDOWS64 +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include +#include +#include +// TODO: reference additional headers your program requires here +#include +#endif + +#ifdef _DURANGO +#include +#include +#include +#include +using namespace DirectX; +#include +#include "..\Minecraft.Client\Durango\DurangoExtras\DurangoStubs.h" +#endif + +#if (defined __PS3__ || defined _XBOX ) +// C RunTime Header Files +#include +#endif + +#ifdef __ORBIS__ +#include +#include +#include +#include +#include +#include +#include +#endif + +#ifdef _XBOX +#include +#include +#include +#include +typedef XINVITE_INFO INVITE_INFO; +typedef XUID PlayerUID; +typedef XNKID SessionID; +typedef XUID GameSessionUID; +#endif + +#ifdef __PS3__ +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "Ps3Types.h" +#include "Ps3Stubs.h" +#include "PS3Maths.h" + +#elif defined __ORBIS__ +#include +#include +#include +#include "OrbisTypes.h" +#include "OrbisStubs.h" +#include "OrbisMaths.h" +#elif defined __PSVITA__ +#include +#include +#include +#include +#include "PSVitaTypes.h" +#include "PSVitaStubs.h" +#include "PSVitaMaths.h" +#else +#include +#include +#include +#include +#endif //__PS3__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __PS3__ // the PS3 lib assert is rubbish, and aborts the code, we define our own in PS3Types.h +#include +#endif + +#ifndef _XBOX +#include "extraX64.h" +#else +#include "..\Minecraft.Client\xbox\network\extra.h" +#endif + +#include "Definitions.h" +#include "Class.h" +#include "Exceptions.h" +#include "Mth.h" +#include "StringHelpers.h" +#include "ArrayWithLength.h" +#include "Random.h" +#include "TilePos.h" +#include "ChunkPos.h" +#include "compression.h" +#include "PerformanceTimer.h" + + +#ifdef _FINAL_BUILD +#define printf BREAKTHECOMPILE +#define wprintf BREAKTHECOMPILE +#undef OutputDebugString +#define OutputDebugString BREAKTHECOMPILE +#define OutputDebugStringA BREAKTHECOMPILE +#define OutputDebugStringW BREAKTHECOMPILE +#endif + + +void MemSect(int sect); + +#ifdef _XBOX +#include "..\Minecraft.Client\xbox\4JLibs\inc\4J_Profile.h" +#include "..\Minecraft.Client\xbox\4JLibs\inc\4J_Render.h" +#include "..\Minecraft.Client\xbox\4JLibs\inc\4J_XTMS.h" +#include "..\Minecraft.Client\xbox\4JLibs\inc\4J_Storage.h" +#include "..\Minecraft.Client\xbox\4JLibs\inc\4J_Input.h" +#elif defined (__PS3__) +#include "..\Minecraft.Client\PS3\4JLibs\inc\4J_Profile.h" +#include "..\Minecraft.Client\PS3\4JLibs\inc\4J_Render.h" +#include "..\Minecraft.Client\PS3\4JLibs\inc\4J_Storage.h" +#include "..\Minecraft.Client\PS3\4JLibs\inc\4J_Input.h" +#elif defined _DURANGO +#include "..\Minecraft.Client\Durango\4JLibs\inc\4J_Profile.h" +#include "..\Minecraft.Client\Durango\4JLibs\inc\4J_Render.h" +#include "..\Minecraft.Client\Durango\4JLibs\inc\4J_Storage.h" +#include "..\Minecraft.Client\Durango\4JLibs\inc\4J_Input.h" +#elif defined _WINDOWS64 +#include "..\Minecraft.Client\Windows64\4JLibs\inc\4J_Profile.h" +#include "..\Minecraft.Client\Windows64\4JLibs\inc\4J_Render.h" +#include "..\Minecraft.Client\Windows64\4JLibs\inc\4J_Storage.h" +#include "..\Minecraft.Client\Windows64\4JLibs\inc\4J_Input.h" +#elif defined __PSVITA__ +#include "..\Minecraft.Client\PSVita\4JLibs\inc\4J_Profile.h" +#include "..\Minecraft.Client\PSVita\4JLibs\inc\4J_Render.h" +#include "..\Minecraft.Client\PSVita\4JLibs\inc\4J_Storage.h" +#include "..\Minecraft.Client\PSVita\4JLibs\inc\4J_Input.h" +#else +#include "..\Minecraft.Client\Orbis\4JLibs\inc\4J_Profile.h" +#include "..\Minecraft.Client\Orbis\4JLibs\inc\4J_Render.h" +#include "..\Minecraft.Client\Orbis\4JLibs\inc\4J_Storage.h" +#include "..\Minecraft.Client\Orbis\4JLibs\inc\4J_Input.h" +#endif + +#include "..\Minecraft.Client\Common\Network\GameNetworkManager.h" + +// #ifdef _XBOX +#include "..\Minecraft.Client\Common\UI\UIEnums.h" +#include "..\Minecraft.Client\Common\App_defines.h" +#include "..\Minecraft.Client\Common\App_enums.h" +#include "..\Minecraft.Client\Common\Tutorial\TutorialEnum.h" +#include "..\Minecraft.Client\Common\App_structs.h" +//#endif + +#ifdef _XBOX +#include "..\Minecraft.Client\Common\XUI\XUI_Helper.h" +#include "..\Minecraft.Client\Common\XUI\XUI_Scene_Base.h" +#endif +#include "..\Minecraft.Client\Common\Consoles_App.h" +#include "..\Minecraft.Client\Common\Minecraft_Macros.h" +#include "..\Minecraft.Client\Common\Colours\ColourTable.h" + +#include "..\Minecraft.Client\Common\BuildVer.h" + +#ifdef _XBOX +#include "..\Minecraft.Client\Xbox\Xbox_App.h" +#include "..\Minecraft.Client\XboxMedia\strings.h" +#include "..\Minecraft.Client\Xbox\Sentient\SentientTelemetryCommon.h" +#include "..\Minecraft.Client\Xbox\Sentient\MinecraftTelemetry.h" + +#elif defined (__PS3__) +#include "..\Minecraft.Client\PS3\PS3_App.h" +#include "..\Minecraft.Client\PS3Media\strings.h" +#include "..\Minecraft.Client\PS3\Sentient\SentientTelemetryCommon.h" +#include "..\Minecraft.Client\PS3\Sentient\MinecraftTelemetry.h" + +#elif defined _DURANGO +#include "..\Minecraft.Client\Durango\Durango_App.h" +#include "..\Minecraft.Client\DurangoMedia\strings.h" +//#include "..\Minecraft.Client\Durango\Sentient\SentientManager.h" +#include "..\Minecraft.Client\Durango\Sentient\SentientTelemetryCommon.h" +#include "..\Minecraft.Client\Durango\Sentient\MinecraftTelemetry.h" +#include "..\Minecraft.Client\Durango\Sentient\TelemetryEnum.h" + +#elif defined _WINDOWS64 +#include "..\Minecraft.Client\Windows64\Windows64_App.h" +#include "..\Minecraft.Client\Windows64Media\strings.h" +#include "..\Minecraft.Client\Windows64\Sentient\SentientTelemetryCommon.h" +#include "..\Minecraft.Client\Windows64\Sentient\MinecraftTelemetry.h" + +#elif defined __PSVITA__ +#include "..\Minecraft.Client\PSVita\PSVita_App.h" +#include "..\Minecraft.Client\PSVitaMedia\strings.h" // TODO - create PSVita-specific version of this +#include "..\Minecraft.Client\PSVita\Sentient\SentientManager.h" +#include "..\Minecraft.Client\PSVita\Sentient\MinecraftTelemetry.h" +#else +#include "..\Minecraft.Client\Orbis\Orbis_App.h" +#include "..\Minecraft.Client\OrbisMedia\strings.h" +#include "..\Minecraft.Client\Orbis\Sentient\SentientTelemetryCommon.h" +#include "..\Minecraft.Client\Orbis\Sentient\MinecraftTelemetry.h" +#endif + +#include "..\Minecraft.Client\Common\DLC\DLCSkinFile.h" +#include "..\Minecraft.Client\Common\Console_Awards_enum.h" +#include "..\Minecraft.Client\Common\Potion_Macros.h" +#include "..\Minecraft.Client\Common\Console_Debug_enum.h" +#include "..\Minecraft.Client\Common\GameRules\ConsoleGameRulesConstants.h" +#include "..\Minecraft.Client\Common\GameRules\ConsoleGameRules.h" +#include "..\Minecraft.Client\Common\Telemetry\TelemetryManager.h" diff --git a/Minecraft.World/system.cpp b/Minecraft.World/system.cpp new file mode 100644 index 00000000..72727e20 --- /dev/null +++ b/Minecraft.World/system.cpp @@ -0,0 +1,200 @@ +#include "stdafx.h" +#ifdef __PS3__ +#include +#endif +#include "System.h" + +template void System::arraycopy(arrayWithLength src, unsigned int srcPos, arrayWithLength *dst, unsigned int dstPos, unsigned int length) +{ + assert( srcPos >=0 && srcPos <= src.length); + assert( srcPos + length <= src.length ); + assert( dstPos + length <= dst->length ); + + std::copy( src.data + srcPos, src.data + srcPos + length, dst->data + dstPos ); +} + +ArrayCopyFunctionDefinition(Node *) +ArrayCopyFunctionDefinition(Biome *) + +void System::arraycopy(arrayWithLength src, unsigned int srcPos, arrayWithLength *dst, unsigned int dstPos, unsigned int length) +{ + assert( srcPos >=0 && srcPos <= src.length); + assert( srcPos + length <= src.length ); + assert( dstPos + length <= dst->length ); + + memcpy( dst->data + dstPos, src.data + srcPos, length); +} + +void System::arraycopy(arrayWithLength src, unsigned int srcPos, arrayWithLength *dst, unsigned int dstPos, unsigned int length) +{ + assert( srcPos >=0 && srcPos <= src.length); + assert( srcPos + length <= src.length ); + assert( dstPos + length <= dst->length ); + + memcpy( dst->data + dstPos, src.data + srcPos, length * sizeof(int) ); +} + +// TODO 4J Stu - These time functions may suffer from accuracy and we might have to use a high-resolution timer +//Returns the current value of the most precise available system timer, in nanoseconds. +//This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time. +//The value returned represents nanoseconds since some fixed but arbitrary time (perhaps in the future, so values may be negative). +//This method provides nanosecond precision, but not necessarily nanosecond accuracy. No guarantees are made about how +//frequently values change. Differences in successive calls that span greater than approximately 292 years (263 nanoseconds) +//will not accurately compute elapsed time due to numerical overflow. +// +//For example, to measure how long some code takes to execute: +// +// long startTime = System.nanoTime(); +// // ... the code being measured ... +// long estimatedTime = System.nanoTime() - startTime; +// +//Returns: +//The current value of the system timer, in nanoseconds. +__int64 System::nanoTime() +{ + return GetTickCount() * 1000000LL; +} + +//Returns the current time in milliseconds. Note that while the unit of time of the return value is a millisecond, +//the granularity of the value depends on the underlying operating system and may be larger. For example, +//many operating systems measure time in units of tens of milliseconds. +//See the description of the class Date for a discussion of slight discrepancies that may arise between "computer time" +//and coordinated universal time (UTC). +// +//Returns: +//the difference, measured in milliseconds, between the current time and midnight, January 1, 1970 UTC. +__int64 System::currentTimeMillis() +{ +#ifdef __PS3__ +// sys_time_get_current_time() obtains the elapsed time since Epoch (1970/01/01 00:00:00 UTC). +// The value is separated into two parts: sec stores the elapsed time in seconds, and nsec +// stores the value that is smaller than a second in nanoseconds. + sys_time_sec_t sec; + sys_time_nsec_t nsec; + sys_time_get_current_time(&sec, &nsec); + __int64 msec = (sec * 1000) + (nsec / (1000*1000)); + return msec; + +#elif defined __ORBIS__ + SceRtcTick tick; + int err = sceRtcGetCurrentTick(&tick); + + return (int64_t)(tick.tick / 1000); +#elif defined __PSVITA__ + // AP - TRC states we can't use the RTC for measuring elapsed game time + return sceKernelGetProcessTimeWide() / 1000; +/* SceDateTime Time; + sceRtcGetCurrentClockLocalTime(&Time); + __int64 systTime = (((((((Time.day * 24) + Time.hour) * 60) + Time.minute) * 60) + Time.second) * 1000) + (Time.microsecond / 1000); + return systTime;*/ + +#else + + SYSTEMTIME UTCSysTime; + GetSystemTime( &UTCSysTime ); + + //Represents as a 64-bit value the number of 100-nanosecond intervals since January 1, 1601 + FILETIME UTCFileTime; + SystemTimeToFileTime( &UTCSysTime, &UTCFileTime ); + + LARGE_INTEGER li; + li.HighPart = UTCFileTime.dwHighDateTime; + li.LowPart = UTCFileTime.dwLowDateTime; + + return li.QuadPart/10000; +#endif // __PS3__ +} + +// 4J Stu - Added this so that we can use real-world timestamps in PSVita saves. Particularly required for the save transfers to be smooth +__int64 System::currentRealTimeMillis() +{ +#ifdef __PSVITA__ + SceDateTime Time; + sceRtcGetCurrentClockLocalTime(&Time); + __int64 systTime = (((((((Time.day * 24) + Time.hour) * 60) + Time.minute) * 60) + Time.second) * 1000) + (Time.microsecond / 1000); + return systTime; +#else + return currentTimeMillis(); +#endif +} + + +void System::ReverseUSHORT(unsigned short *pusVal) +{ + unsigned short usValue=*pusVal; + unsigned char *pchVal1=(unsigned char *)pusVal; + unsigned char *pchVal2=(unsigned char *)&usValue; + + pchVal1[0]=pchVal2[1]; + pchVal1[1]=pchVal2[0]; +} + +void System::ReverseSHORT(short *pusVal) +{ + short usValue=*pusVal; + unsigned char *pchVal1=(unsigned char *)pusVal; + unsigned char *pchVal2=(unsigned char *)&usValue; + + pchVal1[0]=pchVal2[1]; + pchVal1[1]=pchVal2[0]; +} + +void System::ReverseULONG(unsigned long *pulVal) +{ + unsigned long ulValue=*pulVal; + unsigned char *pchVal1=(unsigned char *)pulVal; + unsigned char *pchVal2=(unsigned char *)&ulValue; + + pchVal1[0]=pchVal2[3]; + pchVal1[1]=pchVal2[2]; + pchVal1[2]=pchVal2[1]; + pchVal1[3]=pchVal2[0]; +} + +void System::ReverseULONG(unsigned int *pulVal) +{ + unsigned int ulValue=*pulVal; + unsigned char *pchVal1=(unsigned char *)pulVal; + unsigned char *pchVal2=(unsigned char *)&ulValue; + + pchVal1[0]=pchVal2[3]; + pchVal1[1]=pchVal2[2]; + pchVal1[2]=pchVal2[1]; + pchVal1[3]=pchVal2[0]; +} + +void System::ReverseINT(int *piVal) +{ + int ulValue=*piVal; + unsigned char *pchVal1=(unsigned char *)piVal; + unsigned char *pchVal2=(unsigned char *)&ulValue; + + pchVal1[0]=pchVal2[3]; + pchVal1[1]=pchVal2[2]; + pchVal1[2]=pchVal2[1]; + pchVal1[3]=pchVal2[0]; +} + +void System::ReverseULONGLONG(__int64 *pullVal) +{ + __int64 ullValue=*pullVal; + unsigned char *pchVal1=(unsigned char *)pullVal; + unsigned char *pchVal2=(unsigned char *)&ullValue; + + pchVal1[0]=pchVal2[7]; + pchVal1[1]=pchVal2[6]; + pchVal1[2]=pchVal2[5]; + pchVal1[3]=pchVal2[4]; + pchVal1[4]=pchVal2[3]; + pchVal1[5]=pchVal2[2]; + pchVal1[6]=pchVal2[1]; + pchVal1[7]=pchVal2[0]; +} + +void System::ReverseWCHARA(WCHAR *pwch,int iLen) +{ + for(int i=0;i +#include +#include +#include + +#include "..\..\Minecraft.Client\SkinBox.h" + + +#include + +#define MULTITHREAD_ENABLE + +typedef unsigned char byte; + +const int XUSER_INDEX_ANY = 255; +const int XUSER_INDEX_FOCUS = 254; + +#ifdef __PSVITA__ +const int XUSER_MAX_COUNT = 1; +const int MINECRAFT_NET_MAX_PLAYERS = 4; +#else +const int XUSER_MAX_COUNT = 4; +const int MINECRAFT_NET_MAX_PLAYERS = 8; +#endif + + + +#ifdef __ORBIS__ +#include +#include +#include +#include "..\..\Minecraft.Client\Orbis\Orbis_PlayerUID.h" +#include "..\..\Minecraft.Client\Orbis\Network\SQRNetworkManager_Orbis.h" +typedef SQRNetworkManager_Orbis::SessionID SessionID; +typedef SQRNetworkManager_Orbis::PresenceSyncInfo INVITE_INFO; + +#elif defined __PS3__ // defined in the profile lib +#include +#include +#include +#include +#include +#include "..\..\Minecraft.Client\PS3\PS3_PlayerUID.h" +#include "..\..\Minecraft.Client\PS3\Network\SQRNetworkManager_PS3.h" +typedef SQRNetworkManager::SessionID SessionID; +typedef SQRNetworkManager::PresenceSyncInfo INVITE_INFO; + +#elif defined __PSVITA__ +#include +#include +#include +#include "..\..\Minecraft.Client\PSVita\PSVita_PlayerUID.h" +#include "..\..\Minecraft.Client\PSVita\Network\SQRNetworkManager_Vita.h" +#include "..\..\Minecraft.Client\PSVita\Network\SQRNetworkManager_AdHoc_Vita.h" +typedef SQRNetworkManager_Vita::SessionID SessionID; +typedef SQRNetworkManager_Vita::PresenceSyncInfo INVITE_INFO; + +#elif defined _DURANGO +#include "..\..\Minecraft.Client\Durango\4JLibs\inc\4J_Profile.h" +#include "..\..\Minecraft.Client\Durango\Network\DQRNetworkManager.h" +typedef ULONGLONG SessionID; +typedef ULONGLONG GameSessionUID; +typedef DQRNetworkManager::SessionInfo INVITE_INFO; +#else +typedef ULONGLONG PlayerUID; +typedef ULONGLONG SessionID; +typedef PlayerUID GameSessionUID; +class INVITE_INFO; + +#endif // __PS3__ + +#ifndef _DURANGO +typedef PlayerUID *PPlayerUID; +#endif +typedef struct _XUIOBJ* HXUIOBJ; +typedef struct _XUICLASS* HXUICLASS; +typedef struct _XUIBRUSH* HXUIBRUSH; +typedef struct _XUIDC* HXUIDC; + + + +// #ifdef _DURANGO +// void GetLocalTime(SYSTEMTIME *time); +// #endif + +bool IsEqualXUID(PlayerUID a, PlayerUID b); + +using namespace std; + +// Temporary implementation of lock free stack with quite a bit more locking than you might expect +template class XLockFreeStack +{ + std::vector intStack; +public: + XLockFreeStack() { +#ifdef __ORBIS__ + OrbisInit(); // For PS4, we need to make sure Ult is set up for the critical sections to be able to initialise +#endif + InitializeCriticalSectionAndSpinCount(&m_cs,5120); + } + ~XLockFreeStack() { DeleteCriticalSection( &m_cs ); } + void Initialize() {} + void Push(T *data) + { + EnterCriticalSection(&m_cs); + intStack.push_back(data); + LeaveCriticalSection(&m_cs); + } + T *Pop() + { + EnterCriticalSection(&m_cs); + if( intStack.size() ) + { + T* ret = intStack.back(); + intStack.pop_back(); + LeaveCriticalSection(&m_cs); + return ret; + } + else + { + LeaveCriticalSection(&m_cs); + return NULL; + } + } +private: + CRITICAL_SECTION m_cs; +}; + +void XMemCpy(void *a, const void *b, size_t s); +void XMemSet(void *a, int t, size_t s); +void XMemSet128(void *a, int t, size_t s); +void *XPhysicalAlloc(SIZE_T a, ULONG_PTR b, ULONG_PTR c, DWORD d); +void XPhysicalFree(void *a); + +class DLCManager; + +//class LevelGenerationOptions; +class LevelRuleset; +class ModelPart; +class LevelChunk; +class IXACT3Engine; +class XACT_NOTIFICATION; +class ConsoleSchematicFile; + +const int XZP_ICON_SHANK_01 = 1; +const int XZP_ICON_SHANK_02 = 2; +const int XZP_ICON_SHANK_03 = 3; + +const int XN_SYS_SIGNINCHANGED = 0; +const int XN_SYS_INPUTDEVICESCHANGED = 1; +const int XN_LIVE_CONTENT_INSTALLED = 2; +const int XN_SYS_STORAGEDEVICESCHANGED = 3; + +// +// Codes returned for the gamepad input +// + +#define VK_PAD_A 0x5800 +#define VK_PAD_B 0x5801 +#define VK_PAD_X 0x5802 +#define VK_PAD_Y 0x5803 +#define VK_PAD_RSHOULDER 0x5804 +#define VK_PAD_LSHOULDER 0x5805 +#define VK_PAD_LTRIGGER 0x5806 +#define VK_PAD_RTRIGGER 0x5807 + +#define VK_PAD_DPAD_UP 0x5810 +#define VK_PAD_DPAD_DOWN 0x5811 +#define VK_PAD_DPAD_LEFT 0x5812 +#define VK_PAD_DPAD_RIGHT 0x5813 +#define VK_PAD_START 0x5814 +#define VK_PAD_BACK 0x5815 +#define VK_PAD_LTHUMB_PRESS 0x5816 +#define VK_PAD_RTHUMB_PRESS 0x5817 + +#define VK_PAD_LTHUMB_UP 0x5820 +#define VK_PAD_LTHUMB_DOWN 0x5821 +#define VK_PAD_LTHUMB_RIGHT 0x5822 +#define VK_PAD_LTHUMB_LEFT 0x5823 +#define VK_PAD_LTHUMB_UPLEFT 0x5824 +#define VK_PAD_LTHUMB_UPRIGHT 0x5825 +#define VK_PAD_LTHUMB_DOWNRIGHT 0x5826 +#define VK_PAD_LTHUMB_DOWNLEFT 0x5827 + +#define VK_PAD_RTHUMB_UP 0x5830 +#define VK_PAD_RTHUMB_DOWN 0x5831 +#define VK_PAD_RTHUMB_RIGHT 0x5832 +#define VK_PAD_RTHUMB_LEFT 0x5833 +#define VK_PAD_RTHUMB_UPLEFT 0x5834 +#define VK_PAD_RTHUMB_UPRIGHT 0x5835 +#define VK_PAD_RTHUMB_DOWNRIGHT 0x5836 +#define VK_PAD_RTHUMB_DOWNLEFT 0x5837 + +const int XUSER_NAME_SIZE = 32; + +class IQNetPlayer +{ +public: + BYTE GetSmallId(); + void SendData(IQNetPlayer *player, const void *pvData, DWORD dwDataSize, DWORD dwFlags); + bool IsSameSystem(IQNetPlayer *player); + DWORD GetSendQueueSize( IQNetPlayer *player, DWORD dwFlags ); + DWORD GetCurrentRtt(); + bool IsHost(); + bool IsGuest(); + bool IsLocal(); + PlayerUID GetXuid(); + LPCWSTR GetGamertag(); + int GetSessionIndex(); + bool IsTalking(); + bool IsMutedByLocalUser(DWORD dwUserIndex); + bool HasVoice(); + bool HasCamera(); + int GetUserIndex(); + void SetCustomDataValue(ULONG_PTR ulpCustomDataValue); + ULONG_PTR GetCustomDataValue(); +private: + ULONG_PTR m_customData; +}; + +const int QNET_GETSENDQUEUESIZE_SECONDARY_TYPE = 0; +const int QNET_GETSENDQUEUESIZE_MESSAGES = 0; +const int QNET_GETSENDQUEUESIZE_BYTES = 0; + + +typedef struct { + BYTE bFlags; + BYTE bReserved; + WORD cProbesXmit; + WORD cProbesRecv; + WORD cbData; + BYTE *pbData; + WORD wRttMinInMsecs; + WORD wRttMedInMsecs; + DWORD dwUpBitsPerSec; + DWORD dwDnBitsPerSec; +} XNQOSINFO; + +typedef struct { + UINT cxnqos; + UINT cxnqosPending; + XNQOSINFO axnqosinfo[1]; +} XNQOS; + +typedef struct _XOVERLAPPED { +} XOVERLAPPED, *PXOVERLAPPED; + +typedef struct _XSESSION_SEARCHRESULT { +} XSESSION_SEARCHRESULT, *PXSESSION_SEARCHRESULT; + +typedef struct { + DWORD dwContextId; + DWORD dwValue; +} XUSER_CONTEXT, *PXUSER_CONTEXT; + +typedef struct _XSESSION_SEARCHRESULT_HEADER { + DWORD dwSearchResults; + XSESSION_SEARCHRESULT *pResults; +} XSESSION_SEARCHRESULT_HEADER, *PXSESSION_SEARCHRESULT_HEADER; + +typedef struct _XONLINE_FRIEND { + PlayerUID xuid; + CHAR szGamertag[XUSER_NAME_SIZE]; + DWORD dwFriendState; + SessionID sessionID; + DWORD dwTitleID; + FILETIME ftUserTime; + SessionID xnkidInvite; + FILETIME gameinviteTime; + DWORD cchRichPresence; +// WCHAR wszRichPresence[MAX_RICHPRESENCE_SIZE]; +} XONLINE_FRIEND, *PXONLINE_FRIEND; + +class IQNetCallbacks +{ +}; + +class IQNetGameSearch +{ +}; + + +typedef enum _QNET_STATE +{ + QNET_STATE_IDLE, + QNET_STATE_SESSION_HOSTING, + QNET_STATE_SESSION_JOINING, + QNET_STATE_GAME_LOBBY, + QNET_STATE_SESSION_REGISTERING, + QNET_STATE_SESSION_STARTING, + QNET_STATE_GAME_PLAY, + QNET_STATE_SESSION_ENDING, + QNET_STATE_SESSION_LEAVING, + QNET_STATE_SESSION_DELETING +} QNET_STATE, * PQNET_STATE; + +class IQNet +{ +public: + HRESULT AddLocalPlayerByUserIndex(DWORD dwUserIndex); + IQNetPlayer *GetHostPlayer(); + IQNetPlayer *GetLocalPlayerByUserIndex(DWORD dwUserIndex); + IQNetPlayer *GetPlayerByIndex(DWORD dwPlayerIndex); + IQNetPlayer *GetPlayerBySmallId(BYTE SmallId); + IQNetPlayer *GetPlayerByXuid(PlayerUID xuid); + DWORD GetPlayerCount(); + QNET_STATE GetState(); + bool IsHost(); + HRESULT JoinGameFromInviteInfo(DWORD dwUserIndex, DWORD dwUserMask, const INVITE_INFO *pInviteInfo); + void HostGame(); + void EndGame(); + + static IQNetPlayer m_player[4]; +}; + +#ifdef _DURANGO +// 4J Stu - We don't want to be doing string conversions at runtime for timing instrumentation, so do this instead +#define PIXBeginNamedEvent(a, b, ...) PIXBeginEvent(a,L ## b, __VA_ARGS__) +#define PIXEndNamedEvent() PIXEndEvent() +#define PIXSetMarkerDeprecated(a, b, ...) PIXSetMarker(a, L ## b, __VA_ARGS__) +#define PIXAddNamedCounter(a, b) PIXReportCounter( L ## b, a) +#else +void PIXAddNamedCounter(int a, char *b, ...); +void PIXBeginNamedEvent(int a, char *b, ...); +void PIXEndNamedEvent(); +void PIXSetMarkerDeprecated(int a, char *b, ...); +#endif + +void XSetThreadProcessor(HANDLE a, int b); +//BOOL XCloseHandle(HANDLE a); + +const int QNET_SENDDATA_LOW_PRIORITY = 0; +const int QNET_SENDDATA_SECONDARY = 0; +#if defined(__PS3__) || defined(__ORBIS__) || defined(_DURANGO) || defined(__PSVITA__) +#define INVALID_XUID PlayerUID() +#else +const int INVALID_XUID = 0; +#endif +// const int MOJANG_DATA = 0; + +// typedef struct _STRING_VERIFY_RESPONSE +// { +// WORD wNumStrings; +// HRESULT *pStringResult; +// } STRING_VERIFY_RESPONSE; + +const int XCONTENT_MAX_DISPLAYNAME_LENGTH = 256; +const int XCONTENT_MAX_FILENAME_LENGTH = 256; +typedef int XCONTENTDEVICEID; + +#if !defined( __PS3__) && !defined(__ORBIS__) && !defined(_DURANGO) && !defined(__PSVITA__) +typedef struct _XCONTENT_DATA +{ + XCONTENTDEVICEID DeviceID; + DWORD dwContentType; + WCHAR szDisplayName[XCONTENT_MAX_DISPLAYNAME_LENGTH]; + CHAR szFileName[XCONTENT_MAX_FILENAME_LENGTH]; +} XCONTENT_DATA, *PXCONTENT_DATA; +#endif //__PS3__ + +static const int XMARKETPLACE_CONTENT_ID_LEN = 4; + +#ifndef _DURANGO +typedef struct _XMARKETPLACE_CONTENTOFFER_INFO +{ + ULONGLONG qwOfferID; + ULONGLONG qwPreviewOfferID; + DWORD dwOfferNameLength; + WCHAR *wszOfferName; + DWORD dwOfferType; + BYTE contentId[XMARKETPLACE_CONTENT_ID_LEN]; + BOOL fIsUnrestrictedLicense; + DWORD dwLicenseMask; + DWORD dwTitleID; + DWORD dwContentCategory; + DWORD dwTitleNameLength; + WCHAR *wszTitleName; + BOOL fUserHasPurchased; + DWORD dwPackageSize; + DWORD dwInstallSize; + DWORD dwSellTextLength; + WCHAR *wszSellText; + DWORD dwAssetID; + DWORD dwPurchaseQuantity; + DWORD dwPointsPrice; +} XMARKETPLACE_CONTENTOFFER_INFO, *PXMARKETPLACE_CONTENTOFFER_INFO; + +typedef enum +{ + XMARKETPLACE_OFFERING_TYPE_CONTENT = 0x00000002, + XMARKETPLACE_OFFERING_TYPE_GAME_DEMO = 0x00000020, + XMARKETPLACE_OFFERING_TYPE_GAME_TRAILER = 0x00000040, + XMARKETPLACE_OFFERING_TYPE_THEME = 0x00000080, + XMARKETPLACE_OFFERING_TYPE_TILE = 0x00000800, + XMARKETPLACE_OFFERING_TYPE_ARCADE = 0x00002000, + XMARKETPLACE_OFFERING_TYPE_VIDEO = 0x00004000, + XMARKETPLACE_OFFERING_TYPE_CONSUMABLE = 0x00010000, + XMARKETPLACE_OFFERING_TYPE_AVATARITEM = 0x00100000 +} XMARKETPLACE_OFFERING_TYPE; +#endif // _DURANGO + +const int QNET_SENDDATA_RELIABLE = 0; +const int QNET_SENDDATA_SEQUENTIAL = 0; + +struct XRNM_SEND_BUFFER +{ + DWORD dwDataSize; + byte *pbyData; +}; + +const int D3DBLEND_CONSTANTALPHA = 0; +const int D3DBLEND_INVCONSTANTALPHA = 0; +const int D3DPT_QUADLIST = 0; + + +typedef struct _XUSER_SIGNIN_INFO { + PlayerUID xuid; + DWORD dwGuestNumber; +} XUSER_SIGNIN_INFO, *PXUSER_SIGNIN_INFO; + +#define XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY 0x00000001 +#define XUSER_GET_SIGNIN_INFO_OFFLINE_XUID_ONLY 0x00000002 + +DWORD XUserGetSigninInfo( + DWORD dwUserIndex, + DWORD dwFlags, + PXUSER_SIGNIN_INFO pSigninInfo +); + +class CXuiStringTable +{ +public: + LPCWSTR Lookup(LPCWSTR szId); + LPCWSTR Lookup(UINT nIndex); + void Clear(); + HRESULT Load(LPCWSTR szId); +}; + +#if !defined(__ORBIS__) && !defined(_XBOX_ONE) +typedef VOID * XMEMDECOMPRESSION_CONTEXT; +typedef VOID * XMEMCOMPRESSION_CONTEXT; + +typedef enum _XMEMCODEC_TYPE +{ + XMEMCODEC_DEFAULT = 0, + XMEMCODEC_LZX = 1 +} XMEMCODEC_TYPE; + +HRESULT XMemDecompress( + XMEMDECOMPRESSION_CONTEXT Context, + VOID *pDestination, + SIZE_T *pDestSize, + CONST VOID *pSource, + SIZE_T SrcSize +); + + +HRESULT XMemCompress( + XMEMCOMPRESSION_CONTEXT Context, + VOID *pDestination, + SIZE_T *pDestSize, + CONST VOID *pSource, + SIZE_T SrcSize +); + +HRESULT XMemCreateCompressionContext( + XMEMCODEC_TYPE CodecType, + CONST VOID *pCodecParams, + DWORD Flags, + XMEMCOMPRESSION_CONTEXT *pContext +); + +HRESULT XMemCreateDecompressionContext( + XMEMCODEC_TYPE CodecType, + CONST VOID *pCodecParams, + DWORD Flags, + XMEMDECOMPRESSION_CONTEXT *pContext +); + +typedef struct _XMEMCODEC_PARAMETERS_LZX { + DWORD Flags; + DWORD WindowSize; + DWORD CompressionPartitionSize; +} XMEMCODEC_PARAMETERS_LZX; + +void XMemDestroyCompressionContext(XMEMCOMPRESSION_CONTEXT Context); +void XMemDestroyDecompressionContext(XMEMDECOMPRESSION_CONTEXT Context); +#endif + +typedef struct { + BYTE type; + union { + LONG nData; + LONGLONG i64Data; + double dblData; + struct { + DWORD cbData; + LPWSTR pwszData; + }string; + float fData; + struct { + DWORD cbData; + PBYTE pbData; + }binary; + FILETIME ftData; + }; +} XUSER_DATA, *PXUSER_DATA; + +typedef struct { + DWORD dwPropertyId; + XUSER_DATA value; +} XUSER_PROPERTY, *PXUSER_PROPERTY; + +// these need to match apwstrLocaleCode +// const int XC_LANGUAGE_ENGLISH =1; +// const int XC_LANGUAGE_JAPANESE =2; +// const int XC_LANGUAGE_GERMAN =3; +// const int XC_LANGUAGE_FRENCH =4; +// const int XC_LANGUAGE_SPANISH =5; +// const int XC_LANGUAGE_ITALIAN =6; +// const int XC_LANGUAGE_KOREAN =7; +// const int XC_LANGUAGE_TCHINESE =8; +// const int XC_LANGUAGE_PORTUGUESE =9; +// const int XC_LANGUAGE_BRAZILIAN =10; +// #if defined __PS3__ || defined __PSVITA__ || defined __ORBIS__ +// const int XC_LANGUAGE_RUSSIAN =11; +// // more PS3 +// const int XC_LANGUAGE_DUTCH =12; +// const int XC_LANGUAGE_FINISH =13; +// const int XC_LANGUAGE_SWEDISH =14; +// const int XC_LANGUAGE_DANISH =15; +// const int XC_LANGUAGE_NORWEGIAN =16; +// const int XC_LANGUAGE_POLISH =17; +// const int XC_LANGUAGE_TURKISH =18; +// const int XC_LANGUAGE_LATINAMERICANSPANISH =19; +// +// const int XC_LANGUAGE_GREEK =20; +// #else +// const int XC_LANGUAGE_UKENGLISH =11; +// const int XC_LANGUAGE_MEXICANSPANISH=12; +// #endif + +// matching Xbox 360 +const int XC_LANGUAGE_ENGLISH =0x01; +const int XC_LANGUAGE_JAPANESE =0x02; +const int XC_LANGUAGE_GERMAN =0x03; +const int XC_LANGUAGE_FRENCH =0x04; +const int XC_LANGUAGE_SPANISH =0x05; +const int XC_LANGUAGE_ITALIAN =0x06; +const int XC_LANGUAGE_KOREAN =0x07; +const int XC_LANGUAGE_TCHINESE =0x08; +const int XC_LANGUAGE_PORTUGUESE =0x09; +const int XC_LANGUAGE_POLISH =0x0B; +const int XC_LANGUAGE_RUSSIAN =0x0C; +const int XC_LANGUAGE_SWEDISH =0x0D; +const int XC_LANGUAGE_TURKISH =0x0E; +const int XC_LANGUAGE_BNORWEGIAN =0x0F; +const int XC_LANGUAGE_DUTCH =0x10; +const int XC_LANGUAGE_SCHINESE =0x11; + +// for Sony +const int XC_LANGUAGE_LATINAMERICANSPANISH =0xF0; +const int XC_LANGUAGE_FINISH =0xF1; +const int XC_LANGUAGE_GREEK =0xF2; +const int XC_LANGUAGE_DANISH =0xF3; + +// for Xbox One +const int XC_LANGUAGE_CZECH =0xF4; +const int XC_LANGUAGE_SLOVAK =0xF5; + +const int XC_LOCALE_AUSTRALIA =1; +const int XC_LOCALE_AUSTRIA =2; +const int XC_LOCALE_BELGIUM =3; +const int XC_LOCALE_BRAZIL =4; +const int XC_LOCALE_CANADA =5; +const int XC_LOCALE_CHILE =6; +const int XC_LOCALE_CHINA =7; +const int XC_LOCALE_COLOMBIA =8; +const int XC_LOCALE_CZECH_REPUBLIC =9; +const int XC_LOCALE_DENMARK =10; +const int XC_LOCALE_FINLAND =11; +const int XC_LOCALE_FRANCE =12; +const int XC_LOCALE_GERMANY =13; +const int XC_LOCALE_GREECE =14; +const int XC_LOCALE_HONG_KONG =15; +const int XC_LOCALE_HUNGARY =16; +const int XC_LOCALE_INDIA =17; +const int XC_LOCALE_IRELAND =18; +const int XC_LOCALE_ITALY =19; +const int XC_LOCALE_JAPAN =20; +const int XC_LOCALE_KOREA =21; +const int XC_LOCALE_MEXICO =22; +const int XC_LOCALE_NETHERLANDS =23; +const int XC_LOCALE_NEW_ZEALAND =24; +const int XC_LOCALE_NORWAY =25; +const int XC_LOCALE_POLAND =26; +const int XC_LOCALE_PORTUGAL =27; +const int XC_LOCALE_SINGAPORE =28; +const int XC_LOCALE_SLOVAK_REPUBLIC =29; +const int XC_LOCALE_SOUTH_AFRICA =30; +const int XC_LOCALE_SPAIN =31; +const int XC_LOCALE_SWEDEN =32; +const int XC_LOCALE_SWITZERLAND =33; +const int XC_LOCALE_TAIWAN =34; +const int XC_LOCALE_GREAT_BRITAIN =35; +const int XC_LOCALE_UNITED_STATES =36; +const int XC_LOCALE_RUSSIAN_FEDERATION =37; +const int XC_LOCALE_WORLD_WIDE =38; +const int XC_LOCALE_TURKEY =39; +const int XC_LOCALE_ARGENTINA =40; +const int XC_LOCALE_SAUDI_ARABIA =41; +const int XC_LOCALE_ISRAEL =42; +const int XC_LOCALE_UNITED_ARAB_EMIRATES =43; + +// for Sony +const int XC_LOCALE_LATIN_AMERICA =240; + + + +#if !(defined _DURANGO || defined __PS3__ || defined __ORBIS__ || defined __PSVITA__) +DWORD XGetLanguage(); +DWORD XGetLocale(); +DWORD XEnableGuestSignin(BOOL fEnable); +#endif + +class D3DXVECTOR3 +{ +public: + D3DXVECTOR3(); + D3DXVECTOR3(float,float,float); + float x,y,z,pad; + D3DXVECTOR3& operator += ( CONST D3DXVECTOR3& add ); +}; + +#define QNET_E_SESSION_FULL 0 +#define QNET_USER_MASK_USER0 1 +#define QNET_USER_MASK_USER1 2 +#define QNET_USER_MASK_USER2 4 +#define QNET_USER_MASK_USER3 8 diff --git a/Minecraft.World/x64headers/qnet.h b/Minecraft.World/x64headers/qnet.h new file mode 100644 index 00000000..e69de29b diff --git a/Minecraft.World/x64headers/xmcore.h b/Minecraft.World/x64headers/xmcore.h new file mode 100644 index 00000000..e69de29b diff --git a/Minecraft.World/x64headers/xrnm.h b/Minecraft.World/x64headers/xrnm.h new file mode 100644 index 00000000..e69de29b diff --git a/Minecraft.World/x64headers/xsocialpost.h b/Minecraft.World/x64headers/xsocialpost.h new file mode 100644 index 00000000..e69de29b diff --git a/Minecraft.World/x64headers/xuiapp.h b/Minecraft.World/x64headers/xuiapp.h new file mode 100644 index 00000000..e69de29b diff --git a/Minecraft.World/x64headers/xuiresource.h b/Minecraft.World/x64headers/xuiresource.h new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3