diff --git a/Minecraft.Client/CMakeLists.txt b/Minecraft.Client/CMakeLists.txt index 51f9c32f..c29cbdbf 100644 --- a/Minecraft.Client/CMakeLists.txt +++ b/Minecraft.Client/CMakeLists.txt @@ -133,6 +133,19 @@ set(ASSET_FOLDER_PAIRS ) setup_asset_folder_copy(Minecraft.Client "${ASSET_FOLDER_PAIRS}") +set(LOOT_TABLES_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/Common/Media/MediaWindows64/Structures/loot_tables") +set(LOOT_TABLES_DEST "$/Common/Media/MediaWindows64/Structures/loot_tables") +add_custom_target(AssetLootTablesCopy_Minecraft.Client ALL + COMMAND ${CMAKE_COMMAND} -E make_directory "${LOOT_TABLES_DEST}" + COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different + "${LOOT_TABLES_SOURCE}" + "${LOOT_TABLES_DEST}" + COMMENT "Copying loot table XML assets into build folder..." + VERBATIM +) +add_dependencies(Minecraft.Client AssetLootTablesCopy_Minecraft.Client) +set_property(TARGET AssetLootTablesCopy_Minecraft.Client PROPERTY FOLDER "Build") + # copy prebuilt loc folder and use it lmao if(PLATFORM_NAME STREQUAL "Windows64") add_custom_target(AssetLocalizationCopy_Minecraft.Client ALL diff --git a/Minecraft.Client/Common/Audio/SoundEngine.cpp b/Minecraft.Client/Common/Audio/SoundEngine.cpp index d9a94929..f24a760d 100644 --- a/Minecraft.Client/Common/Audio/SoundEngine.cpp +++ b/Minecraft.Client/Common/Audio/SoundEngine.cpp @@ -57,6 +57,7 @@ void SoundEngine::playUI(int iSound, float volume, float pitch) {} void SoundEngine::updateMusicVolume(float fVal) {} void SoundEngine::updateSoundEffectVolume(float fVal) {} +void SoundEngine::stopStreamingNow() {} void SoundEngine::add(const wstring& name, File *file) {} void SoundEngine::addMusic(const wstring& name, File *file) {} @@ -66,6 +67,22 @@ bool SoundEngine::isStreamingWavebankReady() { return true; } void SoundEngine::playMusicTick() {}; #else +void SoundEngine::stopStreamingNow() +{ + if (m_musicStreamActive) + { + ma_sound_stop(&m_musicStream); + ma_sound_uninit(&m_musicStream); + m_musicStreamActive = false; + } + + SetIsPlayingStreamingCDMusic(false); + SetIsPlayingStreamingGameMusic(false); + + m_StreamState = eMusicStreamState_Idle; + m_musicID = -1; + m_iMusicDelay = 0; +} #ifdef _WINDOWS64 char SoundEngine::m_szSoundPath[]={"Windows64Media\\Sound\\"}; @@ -904,8 +921,15 @@ void SoundEngine::playStreaming(const wstring& name, float x, float y, float z, { // jukebox m_StreamingAudioInfo.bIs3D=true; - m_musicID=getMusicID(name); - m_iMusicDelay=0; + m_musicID = getMusicID(name); + m_iMusicDelay = 0; + + if (m_StreamState == eMusicStreamState_Playing || m_StreamState == eMusicStreamState_Opening || m_musicStreamActive) + { + m_StreamState = eMusicStreamState_Fading; + m_musicFadeSecondsRemaining = MUSIC_FADE_DURATION_SECONDS; + m_musicFadeLastUpdateTime = std::chrono::steady_clock::now(); + } } } diff --git a/Minecraft.Client/Common/Audio/SoundEngine.h b/Minecraft.Client/Common/Audio/SoundEngine.h index 3cef9432..3fe3f9dd 100644 --- a/Minecraft.Client/Common/Audio/SoundEngine.h +++ b/Minecraft.Client/Common/Audio/SoundEngine.h @@ -135,6 +135,7 @@ public: void startElytraSound(float x, float y, float z, float volume, float pitch); void stopElytraSound(); void playStreaming(const wstring& name, float x, float y , float z, float volume, float pitch, bool bMusicDelay=true) override; + void stopStreamingNow(); void playUI(int iSound, float volume, float pitch) override; void playMusicTick() override; void updateMusicVolume(float fVal) override; diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/abandoned_mineshaft.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/abandoned_mineshaft.xml new file mode 100644 index 00000000..50f2ef7e --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/abandoned_mineshaft.xml @@ -0,0 +1,277 @@ + + + + + 1 + + + item + minecraft:golden_apple + 20 + + + item + minecraft:golden_apple + 1 + + + set_data + 1 + + + + + item + minecraft:name_tag + 30 + + + item + minecraft:book + 10 + + + enchant_randomly + + + + + item + minecraft:iron_pickaxe + 5 + + + empty + 5 + + + + + + 2 + 4 + + + + item + minecraft:iron_ingot + + + set_count + + 1 + 5 + + + + 10 + + + item + minecraft:gold_ingot + + + set_count + + 1 + 3 + + + + 5 + + + item + minecraft:redstone + + + set_count + + 4 + 9 + + + + 5 + + + item + minecraft:dye + + + set_data + 4 + + + set_count + + 4 + 9 + + + + 5 + + + item + minecraft:diamond + + + set_count + + 1 + 2 + + + + 3 + + + item + minecraft:coal + + + set_count + + 3 + 8 + + + + 10 + + + item + minecraft:bread + + + set_count + + 1 + 3 + + + + 15 + + + item + minecraft:melon_seeds + + + set_count + + 2 + 4 + + + + 10 + + + item + minecraft:pumpkin_seeds + + + set_count + + 2 + 4 + + + + 10 + + + item + minecraft:beetroot_seeds + + + set_count + + 2 + 4 + + + + 10 + + + + + 3 + + + item + minecraft:rail + + + set_count + + 4 + 8 + + + + 20 + + + item + minecraft:golden_rail + + + set_count + + 1 + 4 + + + + 5 + + + item + minecraft:detector_rail + + + set_count + + 1 + 4 + + + + 5 + + + item + minecraft:activator_rail + + + set_count + + 1 + 4 + + + + 5 + + + item + minecraft:torch + + + set_count + + 1 + 16 + + + + 15 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/desert_pyramid.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/desert_pyramid.xml new file mode 100644 index 00000000..f48e34e2 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/desert_pyramid.xml @@ -0,0 +1,236 @@ + + + + + + 2 + 4 + + + + item + minecraft:diamond + + + set_count + + 1 + 3 + + + + 5 + + + item + minecraft:iron_ingot + + + set_count + + 1 + 5 + + + + 15 + + + item + minecraft:gold_ingot + + + set_count + + 2 + 7 + + + + 15 + + + item + minecraft:emerald + + + set_count + + 1 + 3 + + + + 15 + + + item + minecraft:bone + + + set_count + + 4 + 6 + + + + 25 + + + item + minecraft:spider_eye + + + set_count + + 1 + 3 + + + + 25 + + + item + minecraft:rotten_flesh + + + set_count + + 3 + 7 + + + + 25 + + + item + minecraft:saddle + 20 + + + item + minecraft:iron_horse_armor + 15 + + + item + minecraft:golden_horse_armor + 10 + + + item + minecraft:diamond_horse_armor + 5 + + + item + minecraft:book + 20 + + + enchant_randomly + + + + + item + minecraft:golden_apple + 20 + + + item + minecraft:golden_apple + 2 + + + set_data + 1 + + + + + empty + 15 + + + + + 4 + + + item + minecraft:bone + 10 + + + set_count + + 1 + 8 + + + + + + item + minecraft:gunpowder + 10 + + + set_count + + 1 + 8 + + + + + + item + minecraft:rotten_flesh + 10 + + + set_count + + 1 + 8 + + + + + + item + minecraft:string + 10 + + + set_count + + 1 + 8 + + + + + + item + minecraft:sand + 10 + + + set_count + + 1 + 8 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/end_city_treasure.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/end_city_treasure.xml new file mode 100644 index 00000000..d2092424 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/end_city_treasure.xml @@ -0,0 +1,313 @@ + + + + + + 2 + 6 + + + + item + minecraft:diamond + 5 + + + set_count + + 2 + 7 + + + + + + item + minecraft:iron_ingot + 10 + + + set_count + + 4 + 8 + + + + + + item + minecraft:gold_ingot + 15 + + + set_count + + 2 + 7 + + + + + + item + minecraft:emerald + 2 + + + set_count + + 2 + 6 + + + + + + item + minecraft:beetroot_seeds + 5 + + + set_count + + 1 + 10 + + + + + + item + minecraft:saddle + 3 + + + item + minecraft:iron_horse_armor + 1 + + + item + minecraft:golden_horse_armor + 1 + + + item + minecraft:diamond_horse_armor + 1 + + + item + minecraft:diamond_sword + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:diamond_boots + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:diamond_chestplate + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:diamond_leggings + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:diamond_helmet + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:diamond_pickaxe + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:diamond_shovel + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:iron_sword + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:iron_boots + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:iron_chestplate + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:iron_leggings + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:iron_helmet + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:iron_pickaxe + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + item + minecraft:iron_shovel + 3 + + + enchant_with_levels + True + + 20 + 39 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/igloo_chest.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/igloo_chest.xml new file mode 100644 index 00000000..f4c1dd19 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/igloo_chest.xml @@ -0,0 +1,94 @@ + + + + + + 2 + 8 + + + + item + minecraft:apple + 15 + + + set_count + + 1 + 3 + + + + + + item + minecraft:coal + 15 + + + set_count + + 1 + 4 + + + + + + item + minecraft:gold_nugget + 10 + + + set_count + + 1 + 3 + + + + + + item + minecraft:stone_axe + 2 + + + item + minecraft:rotten_flesh + 10 + + + item + minecraft:emerald + 1 + + + item + minecraft:wheat + 10 + + + set_count + + 2 + 3 + + + + + + + + 1 + + + item + minecraft:golden_apple + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/jungle_temple.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/jungle_temple.xml new file mode 100644 index 00000000..22990369 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/jungle_temple.xml @@ -0,0 +1,129 @@ + + + + + + 2 + 6 + + + + item + minecraft:diamond + + + set_count + + 1 + 3 + + + + 3 + + + item + minecraft:iron_ingot + + + set_count + + 1 + 5 + + + + 10 + + + item + minecraft:gold_ingot + + + set_count + + 2 + 7 + + + + 15 + + + item + minecraft:emerald + + + set_count + + 1 + 3 + + + + 2 + + + item + minecraft:bone + + + set_count + + 4 + 6 + + + + 20 + + + item + minecraft:rotten_flesh + + + set_count + + 3 + 7 + + + + 16 + + + item + minecraft:saddle + 3 + + + item + minecraft:iron_horse_armor + 1 + + + item + minecraft:golden_horse_armor + 1 + + + item + minecraft:diamond_horse_armor + 1 + + + item + minecraft:book + 1 + + + enchant_with_levels + 30 + True + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/jungle_temple_dispenser.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/jungle_temple_dispenser.xml new file mode 100644 index 00000000..e524d830 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/jungle_temple_dispenser.xml @@ -0,0 +1,27 @@ + + + + + + 1 + 2 + + + + item + minecraft:arrow + + + set_count + + 2 + 7 + + + + 30 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/nether_bridge.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/nether_bridge.xml new file mode 100644 index 00000000..d2a5c2d0 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/nether_bridge.xml @@ -0,0 +1,118 @@ + + + + + + 2 + 4 + + + + item + minecraft:diamond + + + set_count + + 1 + 3 + + + + 5 + + + item + minecraft:iron_ingot + + + set_count + + 1 + 5 + + + + 5 + + + item + minecraft:gold_ingot + + + set_count + + 1 + 3 + + + + 15 + + + item + minecraft:golden_sword + 5 + + + item + minecraft:golden_chestplate + 5 + + + item + minecraft:flint_and_steel + 5 + + + item + minecraft:nether_wart + + + set_count + + 3 + 7 + + + + 5 + + + item + minecraft:saddle + 10 + + + item + minecraft:golden_horse_armor + 8 + + + item + minecraft:iron_horse_armor + 5 + + + item + minecraft:diamond_horse_armor + 3 + + + item + minecraft:obsidian + + + set_count + + 2 + 4 + + + + 2 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/simple_dungeon.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/simple_dungeon.xml new file mode 100644 index 00000000..d82c7a1c --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/simple_dungeon.xml @@ -0,0 +1,265 @@ + + + + + + 1 + 3 + + + + item + minecraft:saddle + 20 + + + item + minecraft:golden_apple + 15 + + + item + minecraft:golden_apple + 2 + + + set_data + 1 + + + + + item + minecraft:record_13 + 15 + + + item + minecraft:record_cat + 15 + + + item + minecraft:name_tag + 20 + + + item + minecraft:golden_horse_armor + 10 + + + item + minecraft:iron_horse_armor + 15 + + + item + minecraft:diamond_horse_armor + 5 + + + item + minecraft:book + 10 + + + enchant_randomly + + + + + + + + 1 + 4 + + + + item + minecraft:iron_ingot + 10 + + + set_count + + 1 + 4 + + + + + + item + minecraft:gold_ingot + 5 + + + set_count + + 1 + 4 + + + + + + item + minecraft:bread + 20 + + + item + minecraft:wheat + 20 + + + set_count + + 1 + 4 + + + + + + item + minecraft:bucket + 10 + + + item + minecraft:redstone + 15 + + + set_count + + 1 + 4 + + + + + + item + minecraft:coal + 15 + + + set_count + + 1 + 4 + + + + + + item + minecraft:melon_seeds + + + set_count + + 2 + 4 + + + + 10 + + + item + minecraft:pumpkin_seeds + + + set_count + + 2 + 4 + + + + 10 + + + item + minecraft:beetroot_seeds + + + set_count + + 2 + 4 + + + + 10 + + + + + 3 + + + item + minecraft:bone + 10 + + + set_count + + 1 + 8 + + + + + + item + minecraft:gunpowder + 10 + + + set_count + + 1 + 8 + + + + + + item + minecraft:rotten_flesh + 10 + + + set_count + + 1 + 8 + + + + + + item + minecraft:string + 10 + + + set_count + + 1 + 8 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/spawn_bonus_chest.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/spawn_bonus_chest.xml new file mode 100644 index 00000000..38b528fa --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/spawn_bonus_chest.xml @@ -0,0 +1,161 @@ + + + + + 1 + + + item + minecraft:stone_axe + 1 + + + item + minecraft:wooden_axe + 3 + + + + + 1 + + + item + minecraft:stone_pickaxe + 1 + + + item + minecraft:wooden_pickaxe + 3 + + + + + 3 + + + item + minecraft:apple + 5 + + + set_count + + 1 + 2 + + + + + + item + minecraft:bread + 3 + + + set_count + + 1 + 2 + + + + + + item + minecraft:fish + 3 + + + set_data + 1 + + + set_count + + 1 + 2 + + + + + + + + 4 + + + item + minecraft:stick + 10 + + + set_count + + 1 + 12 + + + + + + item + minecraft:planks + 10 + + + set_count + + 1 + 12 + + + + + + item + minecraft:log + 10 + + + set_count + + 1 + 3 + + + + set_data + + 0 + 3 + + + + + + item + minecraft:log2 + 10 + + + set_count + + 1 + 3 + + + + set_data + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/stronghold_corridor.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/stronghold_corridor.xml new file mode 100644 index 00000000..1a6a164e --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/stronghold_corridor.xml @@ -0,0 +1,169 @@ + + + + + + 2 + 3 + + + + item + minecraft:ender_pearl + 10 + + + item + minecraft:diamond + + + set_count + + 1 + 3 + + + + 3 + + + item + minecraft:iron_ingot + + + set_count + + 1 + 5 + + + + 10 + + + item + minecraft:gold_ingot + + + set_count + + 1 + 3 + + + + 5 + + + item + minecraft:redstone + + + set_count + + 4 + 9 + + + + 5 + + + item + minecraft:bread + + + set_count + + 1 + 3 + + + + 15 + + + item + minecraft:apple + + + set_count + + 1 + 3 + + + + 15 + + + item + minecraft:iron_pickaxe + 5 + + + item + minecraft:iron_sword + 5 + + + item + minecraft:iron_chestplate + 5 + + + item + minecraft:iron_helmet + 5 + + + item + minecraft:iron_leggings + 5 + + + item + minecraft:iron_boots + 5 + + + item + minecraft:golden_apple + 1 + + + item + minecraft:saddle + 1 + + + item + minecraft:iron_horse_armor + 1 + + + item + minecraft:golden_horse_armor + 1 + + + item + minecraft:diamond_horse_armor + 1 + + + item + minecraft:book + 1 + + + enchant_with_levels + 30 + True + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/stronghold_crossing.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/stronghold_crossing.xml new file mode 100644 index 00000000..0ef7287c --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/stronghold_crossing.xml @@ -0,0 +1,114 @@ + + + + + + 1 + 4 + + + + item + minecraft:iron_ingot + + + set_count + + 1 + 5 + + + + 10 + + + item + minecraft:gold_ingot + + + set_count + + 1 + 3 + + + + 5 + + + item + minecraft:redstone + + + set_count + + 4 + 9 + + + + 5 + + + item + minecraft:coal + + + set_count + + 3 + 8 + + + + 10 + + + item + minecraft:bread + + + set_count + + 1 + 3 + + + + 15 + + + item + minecraft:apple + + + set_count + + 1 + 3 + + + + 15 + + + item + minecraft:iron_pickaxe + 1 + + + item + minecraft:book + 1 + + + enchant_with_levels + 30 + True + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/stronghold_library.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/stronghold_library.xml new file mode 100644 index 00000000..2022f6e2 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/stronghold_library.xml @@ -0,0 +1,63 @@ + + + + + + 2 + 10 + + + + item + minecraft:book + + + set_count + + 1 + 3 + + + + 20 + + + item + minecraft:paper + + + set_count + + 2 + 7 + + + + 20 + + + item + minecraft:map + 1 + + + item + minecraft:compass + 1 + + + item + minecraft:book + 10 + + + enchant_with_levels + 30 + True + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/village_blacksmith.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/village_blacksmith.xml new file mode 100644 index 00000000..a1e18b0c --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/chests/village_blacksmith.xml @@ -0,0 +1,161 @@ + + + + + + 3 + 8 + + + + item + minecraft:diamond + + + set_count + + 1 + 3 + + + + 3 + + + item + minecraft:iron_ingot + + + set_count + + 1 + 5 + + + + 10 + + + item + minecraft:gold_ingot + + + set_count + + 1 + 3 + + + + 5 + + + item + minecraft:bread + + + set_count + + 1 + 3 + + + + 15 + + + item + minecraft:apple + + + set_count + + 1 + 3 + + + + 15 + + + item + minecraft:iron_pickaxe + 5 + + + item + minecraft:iron_sword + 5 + + + item + minecraft:iron_chestplate + 5 + + + item + minecraft:iron_helmet + 5 + + + item + minecraft:iron_leggings + 5 + + + item + minecraft:iron_boots + 5 + + + item + minecraft:obsidian + + + set_count + + 3 + 7 + + + + 5 + + + item + minecraft:sapling + + + set_count + + 3 + 7 + + + + 5 + + + item + minecraft:saddle + 3 + + + item + minecraft:iron_horse_armor + 1 + + + item + minecraft:golden_horse_armor + 1 + + + item + minecraft:diamond_horse_armor + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/bat.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/bat.xml new file mode 100644 index 00000000..f209a425 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/bat.xml @@ -0,0 +1,2 @@ + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/blaze.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/blaze.xml new file mode 100644 index 00000000..31fe0de5 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/blaze.xml @@ -0,0 +1,70 @@ + + + + + + 1 + + + item + minecraft:blaze_rod + 1 + + + set_count + + 0 + 1 + + + + looting_enchant + + 0 + 1 + + + + + + + + killed_by_player + + + + + + 1 + + + item + minecraft:glowstone_dust + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + killed_by_player + + + + + + \ No newline at end of file diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/cavespider.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/cavespider.xml new file mode 100644 index 00000000..fb211074 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/cavespider.xml @@ -0,0 +1,62 @@ + + + + + 1 + + + item + minecraft:string + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:spider_eye + 1 + + + set_count + + -1 + 1 + + + + looting_enchant + + 0 + 1 + + + + + + + + killed_by_player + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/chicken.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/chicken.xml new file mode 100644 index 00000000..1ddc3d1e --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/chicken.xml @@ -0,0 +1,62 @@ + + + + + 1 + + + item + minecraft:feather + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:chicken + 1 + + + furnace_smelt + + + entity_properties + this + + True + + + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/cow.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/cow.xml new file mode 100644 index 00000000..6878eb9c --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/cow.xml @@ -0,0 +1,69 @@ + + + + + 1 + + + item + minecraft:leather + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:beef + 1 + + + set_count + + 1 + 3 + + + + furnace_smelt + + + entity_properties + this + + True + + + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/creeper.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/creeper.xml new file mode 100644 index 00000000..674d6f07 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/creeper.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:gunpowder + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/elderguardian.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/elderguardian.xml new file mode 100644 index 00000000..ddafed7d --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/elderguardian.xml @@ -0,0 +1,103 @@ + + + + + 1 + + + item + minecraft:prismarine_shard + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:fish + 3 + + + looting_enchant + + 0 + 1 + + + + + + item + minecraft:prismarine_crystals + 2 + + + looting_enchant + + 0 + 1 + + + + + + empty + 1 + + + + + 1 + + + item + minecraft:sponge + 1 + + + + + killed_by_player + + + + + + + killed_by_player + + + random_chance_with_looting + 0.025 + 0.01 + + + 1 + + + loot_table + minecraft:gameplay/fishing/fish + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/enderman.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/enderman.xml new file mode 100644 index 00000000..e186212b --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/enderman.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:ender_pearl + 1 + + + set_count + + 0 + 1 + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/endermite.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/endermite.xml new file mode 100644 index 00000000..f209a425 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/endermite.xml @@ -0,0 +1,2 @@ + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/ghast.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/ghast.xml new file mode 100644 index 00000000..75ab205e --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/ghast.xml @@ -0,0 +1,57 @@ + + + + + 1 + + + item + minecraft:ghast_tear + 1 + + + set_count + + 0 + 1 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:gunpowder + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/giant.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/giant.xml new file mode 100644 index 00000000..f209a425 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/giant.xml @@ -0,0 +1,2 @@ + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/guardian.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/guardian.xml new file mode 100644 index 00000000..f3b848f4 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/guardian.xml @@ -0,0 +1,88 @@ + + + + + 1 + + + item + minecraft:prismarine_shard + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:fish + 2 + + + looting_enchant + + 0 + 1 + + + + + + item + minecraft:prismarine_crystals + 2 + + + looting_enchant + + 0 + 1 + + + + + + empty + 1 + + + + + + + killed_by_player + + + random_chance_with_looting + 0.025 + 0.01 + + + 1 + + + loot_table + minecraft:gameplay/fishing/fish + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/horse.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/horse.xml new file mode 100644 index 00000000..7c2a11cc --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/horse.xml @@ -0,0 +1,30 @@ + + + + + 1 + + + item + minecraft:leather + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/lavaslime.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/lavaslime.xml new file mode 100644 index 00000000..eed5d9f4 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/lavaslime.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:magma_cream + 1 + + + set_count + + -2 + 1 + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/mushroomcow.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/mushroomcow.xml new file mode 100644 index 00000000..6878eb9c --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/mushroomcow.xml @@ -0,0 +1,69 @@ + + + + + 1 + + + item + minecraft:leather + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:beef + 1 + + + set_count + + 1 + 3 + + + + furnace_smelt + + + entity_properties + this + + True + + + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/ozelot.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/ozelot.xml new file mode 100644 index 00000000..f209a425 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/ozelot.xml @@ -0,0 +1,2 @@ + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/pig.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/pig.xml new file mode 100644 index 00000000..a0efff9d --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/pig.xml @@ -0,0 +1,43 @@ + + + + + 1 + + + item + minecraft:porkchop + 1 + + + set_count + + 1 + 3 + + + + furnace_smelt + + + entity_properties + this + + True + + + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/pigzombie.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/pigzombie.xml new file mode 100644 index 00000000..22c25660 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/pigzombie.xml @@ -0,0 +1,77 @@ + + + + + 1 + + + item + minecraft:rotten_flesh + 1 + + + set_count + + 0 + 1 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:gold_nugget + 1 + + + set_count + + 0 + 1 + + + + looting_enchant + + 0 + 1 + + + + + + + + + + killed_by_player + + + random_chance_with_looting + 0.025 + 0.01 + + + 1 + + + item + minecraft:gold_ingot + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/polarbear.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/polarbear.xml new file mode 100644 index 00000000..f9325c3f --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/polarbear.xml @@ -0,0 +1,60 @@ + + + + + 1 + + + item + minecraft:fish + 3 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + set_data + 0 + + + + + item + minecraft:fish + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + set_data + 1 + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/rabbit.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/rabbit.xml new file mode 100644 index 00000000..c78677aa --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/rabbit.xml @@ -0,0 +1,82 @@ + + + + + 1 + + + item + minecraft:rabbit_hide + 1 + + + set_count + + 0 + 1 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:rabbit + 1 + + + set_count + + 0 + 1 + + + + furnace_smelt + + + entity_properties + this + + True + + + + + + + + + + + + killed_by_player + + + random_chance_with_looting + 0.1 + 0.03 + + + 1 + + + item + minecraft:rabbit_foot + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep.xml new file mode 100644 index 00000000..7c9c8a94 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep.xml @@ -0,0 +1,43 @@ + + + + + 1 + + + item + minecraft:mutton + 1 + + + set_count + + 1 + 2 + + + + furnace_smelt + + + entity_properties + this + + True + + + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/black.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/black.xml new file mode 100644 index 00000000..5d92d74d --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/black.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 15 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/blue.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/blue.xml new file mode 100644 index 00000000..cf8ba28b --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/blue.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 11 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/brown.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/brown.xml new file mode 100644 index 00000000..ea18dd62 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/brown.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 12 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/cyan.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/cyan.xml new file mode 100644 index 00000000..2b7d2bef --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/cyan.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 9 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/gray.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/gray.xml new file mode 100644 index 00000000..66327025 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/gray.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 7 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/green.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/green.xml new file mode 100644 index 00000000..b908003f --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/green.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 13 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/light_blue.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/light_blue.xml new file mode 100644 index 00000000..44f9f8e4 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/light_blue.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 3 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/lime.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/lime.xml new file mode 100644 index 00000000..56eb3a63 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/lime.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 5 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/magenta.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/magenta.xml new file mode 100644 index 00000000..83a1f426 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/magenta.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 2 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/orange.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/orange.xml new file mode 100644 index 00000000..e133a94b --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/orange.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 1 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/pink.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/pink.xml new file mode 100644 index 00000000..fe6a766d --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/pink.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 6 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/purple.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/purple.xml new file mode 100644 index 00000000..44f80329 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/purple.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 10 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/red.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/red.xml new file mode 100644 index 00000000..e2287c46 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/red.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 14 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/silver.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/silver.xml new file mode 100644 index 00000000..f5538ccb --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/silver.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 8 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/white.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/white.xml new file mode 100644 index 00000000..4f2f76f7 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/white.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 0 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/yellow.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/yellow.xml new file mode 100644 index 00000000..1d5407af --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/sheep/yellow.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:wool + 1 + + + set_data + 4 + + + + + + + 1 + + + loot_table + minecraft:entities/sheep + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/shulker.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/shulker.xml new file mode 100644 index 00000000..f209a425 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/shulker.xml @@ -0,0 +1,2 @@ + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/silverfish.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/silverfish.xml new file mode 100644 index 00000000..f209a425 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/silverfish.xml @@ -0,0 +1,2 @@ + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/skeleton.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/skeleton.xml new file mode 100644 index 00000000..4a7c181e --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/skeleton.xml @@ -0,0 +1,57 @@ + + + + + 1 + + + item + minecraft:arrow + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:bone + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/skeleton_horse.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/skeleton_horse.xml new file mode 100644 index 00000000..2bd787ed --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/skeleton_horse.xml @@ -0,0 +1,14 @@ + + + + + 1 + + + item + minecraft:bone + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/slime.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/slime.xml new file mode 100644 index 00000000..a9d4575a --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/slime.xml @@ -0,0 +1,31 @@ + + + + + 1 + + + item + minecraft:slime_ball + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/snowman.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/snowman.xml new file mode 100644 index 00000000..596163de --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/snowman.xml @@ -0,0 +1,24 @@ + + + + + 1 + + + item + minecraft:snowball + 1 + + + set_count + + 0 + 15 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/spider.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/spider.xml new file mode 100644 index 00000000..fb211074 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/spider.xml @@ -0,0 +1,62 @@ + + + + + 1 + + + item + minecraft:string + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:spider_eye + 1 + + + set_count + + -1 + 1 + + + + looting_enchant + + 0 + 1 + + + + + + + + killed_by_player + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/squid.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/squid.xml new file mode 100644 index 00000000..c89630c5 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/squid.xml @@ -0,0 +1,35 @@ + + + + + 1 + + + item + minecraft:dye + 1 + + + set_count + + 1 + 3 + + + + set_data + 0 + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/stray.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/stray.xml new file mode 100644 index 00000000..32560f16 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/stray.xml @@ -0,0 +1,93 @@ + + + + + 1 + + + item + minecraft:arrow + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:bone + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:tipped_arrow + 1 + + + set_count + + 0 + 1 + + + + looting_enchant + + 0 + 1 + + 1 + + + set_nbt + {Potion:"slowness"} + + + + + + + killed_by_player + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/villagergolem.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/villagergolem.xml new file mode 100644 index 00000000..56fcfa12 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/villagergolem.xml @@ -0,0 +1,43 @@ + + + + + 1 + + + item + minecraft:red_flower + 1 + + + set_count + + 0 + 2 + + + + + + + + 1 + + + item + minecraft:iron_ingot + 1 + + + set_count + + 3 + 5 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/witch.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/witch.xml new file mode 100644 index 00000000..7dc7a32e --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/witch.xml @@ -0,0 +1,160 @@ + + + + + + 1 + 3 + + + + item + minecraft:glowstone_dust + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + item + minecraft:sugar + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + item + minecraft:redstone + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + item + minecraft:spider_eye + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + item + minecraft:glass_bottle + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + item + minecraft:gunpowder + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + item + minecraft:stick + 2 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/wither_skeleton.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/wither_skeleton.xml new file mode 100644 index 00000000..c875eba1 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/wither_skeleton.xml @@ -0,0 +1,83 @@ + + + + + 1 + + + item + minecraft:coal + 1 + + + set_count + + -1 + 1 + + + + looting_enchant + + 0 + 1 + + + + + + + + 1 + + + item + minecraft:bone + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + + + killed_by_player + + + random_chance_with_looting + 0.025 + 0.01 + + + 1 + + + item + minecraft:skull + 1 + + + set_data + 1 + + + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/wolf.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/wolf.xml new file mode 100644 index 00000000..f209a425 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/wolf.xml @@ -0,0 +1,2 @@ + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/zombie.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/zombie.xml new file mode 100644 index 00000000..3baffefb --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/zombie.xml @@ -0,0 +1,61 @@ + + + + + 1 + + + item + minecraft:rotten_flesh + 1 + + + set_count + + 0 + 2 + + + + looting_enchant + + 0 + 1 + + + + + + + + + + killed_by_player + + + random_chance_with_looting + 0.025 + 0.01 + + + 1 + + + item + minecraft:iron_ingot + 1 + + + item + minecraft:carrot + 1 + + + item + minecraft:potato + 1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/zombie_horse.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/zombie_horse.xml new file mode 100644 index 00000000..4ab3c252 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/entities/zombie_horse.xml @@ -0,0 +1,14 @@ + + + + + 1 + + + item + minecraft:rotten_flesh + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/gameplay/fishing.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/gameplay/fishing.xml new file mode 100644 index 00000000..b89c587a --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/gameplay/fishing.xml @@ -0,0 +1,28 @@ + + + + + 1 + + + loot_table + minecraft:gameplay/fishing/junk + 10 + -2 + + + loot_table + minecraft:gameplay/fishing/treasure + 5 + 2 + + + loot_table + minecraft:gameplay/fishing/fish + 85 + -1 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/gameplay/fishing/fish.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/gameplay/fishing/fish.xml new file mode 100644 index 00000000..5b85bd27 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/gameplay/fishing/fish.xml @@ -0,0 +1,54 @@ + + + + + 1 + + + item + minecraft:fish + + + set_data + 0 + + + 60 + + + item + minecraft:fish + + + set_data + 1 + + + 25 + + + item + minecraft:fish + + + set_data + 2 + + + 2 + + + item + minecraft:fish + + + set_data + 3 + + + 13 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/gameplay/fishing/junk.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/gameplay/fishing/junk.xml new file mode 100644 index 00000000..28065447 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/gameplay/fishing/junk.xml @@ -0,0 +1,89 @@ + + + + + 1 + + + item + minecraft:leather_boots + 10 + + + set_damage + + 0 + 0.9 + + + + + + item + minecraft:leather + 10 + + + item + minecraft:bone + 10 + + + item + minecraft:potion + 10 + + + item + minecraft:string + 5 + + + item + minecraft:fishing_rod + 2 + + + set_damage + + 0 + 0.9 + + + + + + item + minecraft:bowl + 10 + + + item + minecraft:stick + 5 + + + item + minecraft:dye + 10 + + + set_data + 0 + + + + + item + minecraft:tripwire_hook + 10 + + + item + minecraft:rotten_flesh + 10 + + + + + diff --git a/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/gameplay/fishing/treasure.xml b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/gameplay/fishing/treasure.xml new file mode 100644 index 00000000..fe9789b6 --- /dev/null +++ b/Minecraft.Client/Common/Media/MediaWindows64/Structures/loot_tables/gameplay/fishing/treasure.xml @@ -0,0 +1,75 @@ + + + + + 1 + + + item + minecraft:waterlily + 1 + + + item + minecraft:name_tag + 1 + + + item + minecraft:saddle + 1 + + + item + minecraft:bow + 1 + + + set_damage + + 0 + 0.25 + + + + enchant_with_levels + 30 + True + + + + + item + minecraft:fishing_rod + 1 + + + set_damage + + 0 + 0.25 + + + + enchant_with_levels + 30 + True + + + + + item + minecraft:book + 1 + + + enchant_with_levels + 30 + True + + + + + + + diff --git a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp index 09201999..77d91ced 100644 --- a/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp +++ b/Minecraft.Client/Common/UI/IUIScene_CreativeMenu.cpp @@ -677,6 +677,14 @@ void IUIScene_CreativeMenu::staticCtor() debugSword->enchant( Enchantment::damageBonus, 50 ); debugSword->setHoverName(L"Sword of Debug"); list->push_back(debugSword); + + /* + shared_ptr debugRod = std::make_shared(Item::fishing_rod_Id, 1, 0); + debugRod->enchant( Enchantment::lure, 50 ); + debugRod->enchant( Enchantment::luckOfTheSea, 50 ); + debugRod->setHoverName(L"Rod of Debug"); + list->push_back(debugRod); + */ } #endif diff --git a/Minecraft.Client/LevelRenderer.cpp b/Minecraft.Client/LevelRenderer.cpp index 2bb48002..849e6a0d 100644 --- a/Minecraft.Client/LevelRenderer.cpp +++ b/Minecraft.Client/LevelRenderer.cpp @@ -3566,8 +3566,9 @@ void LevelRenderer::levelEvent(shared_ptr source, int type, int x, int y else { // 4J-PB - only play streaming music if there isn't already some playing - the CD playing may have finished, and game music started playing already - if(!mc->soundEngine->GetIsPlayingStreamingGameMusic()) + if(mc->soundEngine->GetIsPlayingStreamingCDMusic()) { + mc->soundEngine->stopStreamingNow(); level[playerIndex]->playStreamingMusic(L"", x, y, z); // 4J - used to pass nullptr, but using empty string here now instead } } diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index f398f2ca..f255b0d5 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -3648,6 +3648,149 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) } } +#ifdef _WINDOWS64 + static bool wasMiddleMouseDown = false; + const bool middleMouseDown = (iPad == 0 && g_KBMInput.IsKBMActive() && g_KBMInput.IsMouseGrabbed() && g_KBMInput.IsMouseButtonDown(KeyboardMouseInput::MOUSE_MIDDLE)); + const bool pickBlockPressed = middleMouseDown && !wasMiddleMouseDown; + wasMiddleMouseDown = middleMouseDown; + + if (pickBlockPressed && gameMode->hasInfiniteItems() && hitResult != nullptr) + { + int pickedId = -1; + int pickedData = 0; + + if (hitResult->type == HitResult::TILE) + { + const int hitX = hitResult->x; + const int hitY = hitResult->y; + const int hitZ = hitResult->z; + const int tileId = level->getTile(hitX, hitY, hitZ); + + if (tileId > 0 && tileId < Tile::TILE_NUM_COUNT && Tile::tiles[tileId] != nullptr) + { + Tile *pickedTile = Tile::tiles[tileId]; + const int tileData = level->getData(hitX, hitY, hitZ); + + if (pickedTile->mayPick(tileData, false)) + { + pickedId = pickedTile->cloneTileId(level, hitX, hitY, hitZ); + pickedData = pickedTile->cloneTileData(level, hitX, hitY, hitZ); + } + } + } + else if (hitResult->type == HitResult::ENTITY && hitResult->entity != nullptr) + { + shared_ptr pickedEntity = hitResult->entity; + int eggAux = EntityIO::eTypeToIoid(pickedEntity->GetType()); + + if (eggAux >= 0) + { + if (pickedEntity->instanceof(eTYPE_GUARDIAN)) + { + shared_ptr pickedGuardian = dynamic_pointer_cast(pickedEntity); + if (pickedGuardian != nullptr && pickedGuardian->isElder()) + { + eggAux = EntityIO::eTypeToIoid(eTYPE_ELDER_GUARDIAN); + } + } + else if (pickedEntity->instanceof(eTYPE_HORSE)) + { + shared_ptr pickedHorse = dynamic_pointer_cast(pickedEntity); + if (pickedHorse != nullptr) + { + const int horseType = pickedHorse->getType(); + if (horseType != EntityHorse::TYPE_HORSE) + { + eggAux = (eggAux & 0xFFF) | ((horseType + 1) << 12); + } + } + } + else if (pickedEntity->instanceof(eTYPE_OCELOT)) + { + shared_ptr pickedOcelot = dynamic_pointer_cast(pickedEntity); + if (pickedOcelot != nullptr) + { + const int catType = pickedOcelot->getCatType(); + if (catType != Ocelot::TYPE_OCELOT) + { + eggAux = (eggAux & 0xFFF) | ((catType + 1) << 12); + } + } + } + + auto spawnEggIt = EntityIO::idsSpawnableInCreative.find(eggAux); + if (spawnEggIt == EntityIO::idsSpawnableInCreative.end()) + { + const int fallbackEggAux = eggAux & 0xFFF; + auto fallbackIt = EntityIO::idsSpawnableInCreative.find(fallbackEggAux); + if (fallbackIt != EntityIO::idsSpawnableInCreative.end()) + { + eggAux = fallbackEggAux; + } + else + { + eggAux = -1; + } + } + + if (eggAux >= 0 && Item::spawn_egg_Id >= 0 && Item::spawn_egg_Id < Item::items.length && Item::items[Item::spawn_egg_Id] != nullptr) + { + pickedId = Item::spawn_egg_Id; + pickedData = eggAux; + } + } + } + + if (pickedId >= 0 && pickedId < Item::items.length && Item::items[pickedId] != nullptr) + { + shared_ptr playerInventory = player->inventory; + const int previousSelected = playerInventory->selected; + const int fallbackSelected = (previousSelected >= 0 && previousSelected < Inventory::getSelectionSize()) ? previousSelected : 0; + int targetSlot = -1; + + for (int slot = 0; slot < Inventory::getSelectionSize(); ++slot) + { + shared_ptr hotbarItem = playerInventory->items[slot]; + if (hotbarItem != nullptr && hotbarItem->id == pickedId && + (!hotbarItem->isStackedByData() || hotbarItem->getAuxValue() == pickedData)) + { + targetSlot = slot; + break; + } + } + + if (targetSlot < 0) + { + for (int slot = 0; slot < Inventory::getSelectionSize(); ++slot) + { + if (playerInventory->items[slot] == nullptr) + { + targetSlot = slot; + break; + } + } + + if (targetSlot < 0) + { + targetSlot = fallbackSelected; + } + + playerInventory->items[targetSlot] = std::make_shared(Item::items[pickedId], 1, pickedData); + gameMode->handleCreativeModeItemAdd(playerInventory->items[targetSlot], 36 + targetSlot); + } + + playerInventory->selected = targetSlot; + + if( gameMode != nullptr && gameMode->getTutorial() != nullptr ) + { + gameMode->getTutorial()->onSelectedItemChanged(playerInventory->getSelected()); + } + + player->updateRichPresence(); + } + } +#endif + if( gameMode->isInputAllowed(MINECRAFT_ACTION_ACTION) ) { if((player->ullButtonsPressed&(1LL<playStreaming(L"", 0, 0, 0, 1, 1); + if (soundEngine != nullptr) + { + soundEngine->stopStreamingNow(); + soundEngine->playStreaming(L"", 0, 0, 0, 1, 1); + } // 4J - stop update thread from processing this level, which blocks until it is safe to move on - will be re-enabled if we set the level to be non-nullptr gameRenderer->DisableUpdateThread(); diff --git a/Minecraft.Client/OzelotModel.cpp b/Minecraft.Client/OzelotModel.cpp deleted file mode 100644 index 63ab51f6..00000000 --- a/Minecraft.Client/OzelotModel.cpp +++ /dev/null @@ -1,248 +0,0 @@ -#include "stdafx.h" -#include "ModelPart.h" -#include "../Minecraft.World/net.minecraft.world.entity.animal.h" -#include "../Minecraft.World/Mth.h" -#include "OzelotModel.h" - -const float OzelotModel::xo = 0; -const float OzelotModel::yo = 16; -const float OzelotModel::zo = -9; - -const float OzelotModel::headWalkY = -1 + yo; -const float OzelotModel::headWalkZ = 0 + zo; -const float OzelotModel::bodyWalkY = -4 + yo; -const float OzelotModel::bodyWalkZ = -1 + zo; -const float OzelotModel::tail1WalkY = -1 + yo; -const float OzelotModel::tail1WalkZ = 17 + zo; -const float OzelotModel::tail2WalkY = 4 + yo; -const float OzelotModel::tail2WalkZ = 23 + zo; -const float OzelotModel::backLegY = 2.f + yo; -const float OzelotModel::backLegZ = 14 + zo; -const float OzelotModel::frontLegY = -2.2f + yo; -const float OzelotModel::frontLegZ = 4.f + zo; - -OzelotModel::OzelotModel() -{ - state = WALK_STATE; - - setMapTex(L"head.main", 0, 0); - setMapTex(L"head.nose", 0, 24); - setMapTex(L"head.ear1", 0, 10); - setMapTex(L"head.ear2", 6, 10); - - head = new ModelPart(this, L"head"); - head->addBox(L"main", -2.5f, -2, -3, 5, 4, 5); - head->addBox(L"nose", -1.5f, 0, -4, 3, 2, 2); - head->addBox(L"ear1", -2, -3, 0, 1, 1, 2); - head->addBox(L"ear2", 1, -3, 0, 1, 1, 2); - head->setPos(0 + xo, headWalkY, headWalkZ); - - body = new ModelPart(this, 20, 0); - body->addBox(-2, 3, -8, 4, 16, 6, 0); - body->setPos(0 + xo, bodyWalkY, bodyWalkZ); - - tail1 = new ModelPart(this, 0, 15); - tail1->addBox(-0.5f, 0, 0, 1, 8, 1); - tail1->xRot = 0.9f; - tail1->setPos(0 + xo, tail1WalkY, tail1WalkZ); - - tail2 = new ModelPart(this, 4, 15); - tail2->addBox(-0.5f, 0, 0, 1, 8, 1); - tail2->setPos(0 + xo, tail2WalkY, tail2WalkZ); - - backLegL = new ModelPart(this, 8, 13); - backLegL->addBox(-1, 0, 1, 2, 6, 2); - backLegL->setPos(1.1f + xo, backLegY, backLegZ); - - backLegR = new ModelPart(this, 8, 13); - backLegR->addBox(-1, 0, 1, 2, 6, 2); - backLegR->setPos(-1.1f + xo, backLegY, backLegZ); - - frontLegL = new ModelPart(this, 40, 0); - frontLegL->addBox(-1, 0, 0, 2, 10, 2); - frontLegL->setPos(1.2f + xo, frontLegY, frontLegZ); - - frontLegR = new ModelPart(this, 40, 0); - frontLegR->addBox(-1, 0, 0, 2, 10, 2); - frontLegR->setPos(-1.2f + xo, frontLegY, frontLegZ); - - - // 4J added - compile now to avoid random performance hit first time cubes are rendered - head->compile(1.0f/16.0f); - body->compile(1.0f/16.0f); - tail1->compile(1.0f/16.0f); - tail2->compile(1.0f/16.0f); - backLegL->compile(1.0f/16.0f); - backLegR->compile(1.0f/16.0f); - backLegL->compile(1.0f/16.0f); - backLegR->compile(1.0f/16.0f); -} - -void OzelotModel::render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled) -{ - setupAnim(time, r, bob, yRot, xRot, scale); - if (young) - { - float ss = 2.0f; - glPushMatrix(); - glScalef(1.5f / ss, 1.5f / ss, 1.5f / ss); - glTranslatef(0, 10 * scale, 4 * scale); - head->render(scale, usecompiled); - glPopMatrix(); - glPushMatrix(); - glScalef(1 / ss, 1 / ss, 1 / ss); - glTranslatef(0, 24 * scale, 0); - body->render(scale, usecompiled); - backLegL->render(scale, usecompiled); - backLegR->render(scale, usecompiled); - frontLegL->render(scale, usecompiled); - frontLegR->render(scale, usecompiled); - tail1->render(scale, usecompiled); - tail2->render(scale, usecompiled); - glPopMatrix(); - } - else - { - head->render(scale, usecompiled); - body->render(scale, usecompiled); - tail1->render(scale, usecompiled); - tail2->render(scale, usecompiled); - backLegL->render(scale, usecompiled); - backLegR->render(scale, usecompiled); - frontLegL->render(scale, usecompiled); - frontLegR->render(scale, usecompiled); - } -} - -void OzelotModel::render(OzelotModel *model, float scale, bool usecompiled) -{ - head->yRot = model->head->yRot; - head->xRot = model->head->xRot; - head->y = model->head->y; - head->x = model->head->x; - body->yRot = model->body->yRot; - body->xRot = model->body->xRot; - - tail1->yRot = model->body->yRot; - tail1->y = model->body->y; - tail1->x = model->body->x; - tail1->render(scale, usecompiled); - - tail2->yRot = model->body->yRot; - tail2->y = model->body->y; - tail2->x = model->body->x; - tail2->render(scale, usecompiled); - - backLegL->xRot = model->backLegL->xRot; - backLegR->xRot = model->backLegR->xRot; - backLegL->render(scale, usecompiled); - backLegR->render(scale, usecompiled); - - frontLegL->xRot = model->frontLegL->xRot; - frontLegR->xRot = model->frontLegR->xRot; - frontLegL->render(scale, usecompiled); - frontLegR->render(scale, usecompiled); - - head->render(scale, usecompiled); - body->render(scale, usecompiled); -} - -void OzelotModel::setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim) -{ - head->xRot = xRot / (float) (180 / PI); - head->yRot = yRot / (float) (180 / PI); - - if (state == SITTING_STATE) - { - - } - else - { - body->xRot = 90 / (float) (180 / PI); - if (state == SPRINT_STATE) - { - backLegL->xRot = ((float) Mth::cos(time * 0.6662f) * 1.f) * r; - backLegR->xRot = ((float) Mth::cos(time * 0.6662f + 0.3f) * 1.f) * r; - frontLegL->xRot = ((float) Mth::cos(time * 0.6662f + PI + 0.3f) * 1.f) * r; - frontLegR->xRot = ((float) Mth::cos(time * 0.6662f + PI) * 1.f) * r; - tail2->xRot = 0.55f * PI + 0.1f * PI * Mth::cos(time) * r; - } - else - { - backLegL->xRot = ((float) Mth::cos(time * 0.6662f) * 1.f) * r; - backLegR->xRot = ((float) Mth::cos(time * 0.6662f + PI) * 1.f) * r; - frontLegL->xRot = ((float) Mth::cos(time * 0.6662f + PI) * 1.f) * r; - frontLegR->xRot = ((float) Mth::cos(time * 0.6662f) * 1.f) * r; - - if (state == WALK_STATE) tail2->xRot = 0.55f * PI + 0.25f * PI * Mth::cos(time) * r; - else tail2->xRot = 0.55f * PI + 0.15f * PI * Mth::cos(time) * r; - } - } -} - -void OzelotModel::prepareMobModel(shared_ptr mob, float time, float r, float a) -{ - shared_ptr ozelot = dynamic_pointer_cast(mob); - - body->y = bodyWalkY; - body->z = bodyWalkZ; - head->y = headWalkY; - head->z = headWalkZ; - tail1->y = tail1WalkY; - tail1->z = tail1WalkZ; - tail2->y = tail2WalkY; - tail2->z = tail2WalkZ; - frontLegL->y = frontLegR->y = frontLegY; - frontLegL->z = frontLegR->z = frontLegZ; - backLegL->y = backLegR->y = backLegY; - backLegL->z = backLegR->z = backLegZ; - tail1->xRot = 0.9f; - - if (ozelot->isSneaking()) - { - body->y += 1; - head->y += 2; - tail1->y += 1; - tail2->y += -4; - tail2->z += 2; - tail1->xRot = 0.5f * PI; - tail2->xRot = 0.5f * PI; - state = SNEAK_STATE; - } - else if (ozelot->isSprinting()) - { - tail2->y = tail1->y; - tail2->z += 2; - tail1->xRot = 0.5f * PI; - tail2->xRot = 0.5f * PI; - state = SPRINT_STATE; - } - else if (ozelot->isSitting()) - { - body->xRot = 45 / (float) (180 / PI); - body->y += -4; - body->z += 5; - head->y += -3.3f; - head->z += 1; - - tail1->y += 8; - tail1->z += -2; - tail2->y += 2; - tail2->z += -0.8f; - tail1->xRot = PI * 0.55f; - tail2->xRot = PI * 0.85f; - - frontLegL->xRot = frontLegR->xRot = -PI * 0.05f; - frontLegL->y = frontLegR->y = frontLegY + 2; - frontLegL->z = frontLegR->z = -7; - - backLegL->xRot = backLegR->xRot = -PI * 0.5f; - backLegL->y = backLegR->y = backLegY + 3; - backLegL->z = backLegR->z = backLegZ - 4; - state = SITTING_STATE; - } - else - { - state = WALK_STATE; - } -} \ No newline at end of file diff --git a/Minecraft.Client/OzelotModel.h b/Minecraft.Client/OzelotModel.h deleted file mode 100644 index 2c08f3df..00000000 --- a/Minecraft.Client/OzelotModel.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "Model.h" - -class OzelotModel : public Model -{ -private: - ModelPart *backLegL, *backLegR; - ModelPart *frontLegL, *frontLegR; - ModelPart *tail1, *tail2, *head, *body; - - static const int SNEAK_STATE = 0; - static const int WALK_STATE = 1; - static const int SPRINT_STATE = 2; - static const int SITTING_STATE = 3; - - int state; - - static const float xo; - static const float yo; - static const float zo; - - static const float headWalkY; - static const float headWalkZ; - static const float bodyWalkY; - static const float bodyWalkZ; - static const float tail1WalkY; - static const float tail1WalkZ; - static const float tail2WalkY; - static const float tail2WalkZ; - static const float backLegY; - static const float backLegZ; - static const float frontLegY; - static const float frontLegZ ; - -public: - OzelotModel(); - - void render(shared_ptr entity, float time, float r, float bob, float yRot, float xRot, float scale, bool usecompiled); - void render(OzelotModel *model, float scale, bool usecompiled); - void setupAnim(float time, float r, float bob, float yRot, float xRot, float scale, unsigned int uiBitmaskOverrideAnim=0); - void prepareMobModel(shared_ptr mob, float time, float r, float a); -}; \ No newline at end of file diff --git a/Minecraft.Client/OzelotRenderer.cpp b/Minecraft.Client/OzelotRenderer.cpp deleted file mode 100644 index 45575767..00000000 --- a/Minecraft.Client/OzelotRenderer.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "stdafx.h" -#include "../Minecraft.World/net.minecraft.world.entity.animal.h" -#include "OzelotRenderer.h" - -OzelotRenderer::OzelotRenderer(Model *model, float shadow) : MobRenderer(model, shadow) -{ -} - -void OzelotRenderer::render(shared_ptr _mob, double x, double y, double z, float rot, float a) -{ - MobRenderer::render(_mob, x, y, z, rot, a); -} - -void OzelotRenderer::scale(shared_ptr _mob, float a) -{ - // 4J - original version used generics and thus had an input parameter of type Blaze rather than shared_ptr we have here - - // do some casting around instead - shared_ptr mob = dynamic_pointer_cast(_mob); - MobRenderer::scale(mob, a); - if (mob->isTame()) - { - glScalef(.8f, .8f, .8f); - } -} \ No newline at end of file diff --git a/Minecraft.Client/OzelotRenderer.h b/Minecraft.Client/OzelotRenderer.h deleted file mode 100644 index 9627b4f1..00000000 --- a/Minecraft.Client/OzelotRenderer.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "MobRenderer.h" - -class OzelotRenderer : public MobRenderer -{ -public: - OzelotRenderer(Model *model, float shadow); - - void render(shared_ptr _mob, double x, double y, double z, float rot, float a); - -protected: - void scale(shared_ptr _mob, float a); -}; \ No newline at end of file diff --git a/Minecraft.Client/ServerLevel.cpp b/Minecraft.Client/ServerLevel.cpp index 4f941aa6..5aab82f6 100644 --- a/Minecraft.Client/ServerLevel.cpp +++ b/Minecraft.Client/ServerLevel.cpp @@ -1001,7 +1001,7 @@ void ServerLevel::generateBonusItemsNearSpawn() } } - BonusChestFeature *feature = new BonusChestFeature(RANDOM_BONUS_ITEMS, 16); + BonusChestFeature *feature = new BonusChestFeature("chests/spawn_bonus_chest"); for (int attempt = 0; attempt < 16; attempt++) { int x = levelData->getXSpawn() + random->nextInt(6) - random->nextInt(6); diff --git a/Minecraft.Client/VillagerZombieModel.cpp b/Minecraft.Client/VillagerZombieModel.cpp index 4217b4b8..92d054dc 100644 --- a/Minecraft.Client/VillagerZombieModel.cpp +++ b/Minecraft.Client/VillagerZombieModel.cpp @@ -41,6 +41,10 @@ void VillagerZombieModel::_init(float g, float yOffset, bool isArmor) pants1->addBox(0, 0, 0, 0, 0, 0, g); pants1->setPos(0, 0 + yOffset, 0); + jacket = new ModelPart(this, 40, 16); + jacket->addBox(0, 0, 0, 0, 0, 0, g); + jacket->setPos(0, 0 + yOffset, 0); + delete arm1; arm1 = new ModelPart(this, 40, 16); arm1->bMirror = true; diff --git a/Minecraft.World/Blaze.cpp b/Minecraft.World/Blaze.cpp index e80c164f..e8a2bafd 100644 --- a/Minecraft.World/Blaze.cpp +++ b/Minecraft.World/Blaze.cpp @@ -182,6 +182,7 @@ bool Blaze::isOnFire() return isCharged(); } +/* void Blaze::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { if (wasKilledByPlayer) @@ -199,6 +200,7 @@ void Blaze::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) } } } +*/ bool Blaze::isCharged() { diff --git a/Minecraft.World/Blaze.h b/Minecraft.World/Blaze.h index 7242e9c3..ad4b1ab7 100644 --- a/Minecraft.World/Blaze.h +++ b/Minecraft.World/Blaze.h @@ -39,8 +39,8 @@ protected: public: virtual bool isOnFire(); -protected: - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); +// protected: + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); public: bool isCharged(); diff --git a/Minecraft.World/BonusChestFeature.cpp b/Minecraft.World/BonusChestFeature.cpp index 50e74540..023d296b 100644 --- a/Minecraft.World/BonusChestFeature.cpp +++ b/Minecraft.World/BonusChestFeature.cpp @@ -3,17 +3,18 @@ #include "net.minecraft.world.level.h" #include "net.minecraft.world.level.tile.h" #include "net.minecraft.world.level.tile.entity.h" +#include "LootTableManager.h" #include "WeighedTreasure.h" #include "StructurePiece.h" -BonusChestFeature::BonusChestFeature(WeighedTreasureArray treasureList, int numRolls) : treasureList(treasureList), numRolls(numRolls) +BonusChestFeature::BonusChestFeature(const std::string &lootTableName) : lootTableName(lootTableName) { } // 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 ); + 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 @@ -24,7 +25,7 @@ bool BonusChestFeature::place(Level *level, Random *random, int x, int y, int z) bool BonusChestFeature::place(Level *level, Random *random, int x, int y, int z, bool force) { - // check if level is corrupted first + // check if level is corrupted first if (level == nullptr || random == nullptr) { return false; @@ -33,11 +34,11 @@ bool BonusChestFeature::place(Level *level, Random *random, int x, int y, int z, //Will only spawn a bonus chest if the world is new and has never been saved. if (level->isNew) { - if( !force ) + if (!force) { int t = 0; while (((t = level->getTile(x, y, z)) == 0 || t == Tile::leaves_Id) && y > 1) - y--; + y--; if (y < 1) { @@ -46,54 +47,56 @@ bool BonusChestFeature::place(Level *level, Random *random, int x, int y, int z, y++; } - for (int i = 0; i < 4; i++) - { - int x2, y2, z2; + 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) + { + 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->setTileAndData(x2, y2, z2, Tile::chest_Id, 0, Tile::UPDATE_CLIENTS); + shared_ptr chest = dynamic_pointer_cast(level->getTileEntity(x2, y2, z2)); + if (chest != nullptr) + { + std::vector drops = LootTableManager::Get().ResolveDrops( + lootTableName, false, 0, + [random](int bound) { return random->nextInt(bound); }); + WeighedTreasure::addChestItems(random, drops, chest); + chest->isBonusChest = true; // 4J added + } + if (level->isEmptyTile(x2 - 1, y2, z2) && level->isTopSolidBlocking(x2 - 1, y2 - 1, z2)) + { + level->setTileAndData(x2 - 1, y2, z2, Tile::torch_Id, 0, Tile::UPDATE_CLIENTS); + } + if (level->isEmptyTile(x2 + 1, y2, z2) && level->isTopSolidBlocking(x2 - 1, y2 - 1, z2)) + { + level->setTileAndData(x2 + 1, y2, z2, Tile::torch_Id, 0, Tile::UPDATE_CLIENTS); + } + if (level->isEmptyTile(x2, y2, z2 - 1) && level->isTopSolidBlocking(x2 - 1, y2 - 1, z2)) + { + level->setTileAndData(x2, y2, z2 - 1, Tile::torch_Id, 0, Tile::UPDATE_CLIENTS); + } + if (level->isEmptyTile(x2, y2, z2 + 1) && level->isTopSolidBlocking(x2 - 1, y2 - 1, z2)) + { + level->setTileAndData(x2, y2, z2 + 1, Tile::torch_Id, 0, Tile::UPDATE_CLIENTS); + } + return true; + } } - if (force || ( level->isEmptyTile(x2, y2, z2) && level->isTopSolidBlocking(x2, y2 - 1, z2))) - { - level->setTileAndData(x2, y2, z2, Tile::chest_Id, 0, Tile::UPDATE_CLIENTS); - shared_ptr chest = dynamic_pointer_cast(level->getTileEntity(x2, y2, z2)); - if (chest != nullptr) - { - 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->setTileAndData(x2 - 1, y2, z2, Tile::torch_Id, 0, Tile::UPDATE_CLIENTS); - } - if (level->isEmptyTile(x2 + 1, y2, z2) && level->isTopSolidBlocking(x2 - 1, y2 - 1, z2)) - { - level->setTileAndData(x2 + 1, y2, z2, Tile::torch_Id, 0, Tile::UPDATE_CLIENTS); - } - if (level->isEmptyTile(x2, y2, z2 - 1) && level->isTopSolidBlocking(x2 - 1, y2 - 1, z2)) - { - level->setTileAndData(x2, y2, z2 - 1, Tile::torch_Id, 0, Tile::UPDATE_CLIENTS); - } - if (level->isEmptyTile(x2, y2, z2 + 1) && level->isTopSolidBlocking(x2 - 1, y2 - 1, z2)) - { - level->setTileAndData(x2, y2, z2 + 1, Tile::torch_Id, 0, Tile::UPDATE_CLIENTS); - } - return true; - } - } - - return false; - + return false; } } diff --git a/Minecraft.World/BonusChestFeature.h b/Minecraft.World/BonusChestFeature.h index cd4aca3e..ddd8a41a 100644 --- a/Minecraft.World/BonusChestFeature.h +++ b/Minecraft.World/BonusChestFeature.h @@ -1,6 +1,8 @@ #pragma once #include "Feature.h" +#include + class Random; class Level; @@ -8,12 +10,11 @@ class BonusChestFeature : public Feature { private: - const WeighedTreasureArray treasureList; - const int numRolls; + const std::string lootTableName; public: - BonusChestFeature(WeighedTreasureArray treasureList, int numRolls); + explicit BonusChestFeature(const std::string &lootTableName); - 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 + 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/CMakeLists.txt b/Minecraft.World/CMakeLists.txt index 82169c29..52d3d5d7 100644 --- a/Minecraft.World/CMakeLists.txt +++ b/Minecraft.World/CMakeLists.txt @@ -14,6 +14,8 @@ set(MINECRAFT_WORLD_SOURCES list(APPEND MINECRAFT_WORLD_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/BlockStateDecoder.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/BlockStateDecoderRegistry.cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/LootTableManager.cpp" + "${CMAKE_SOURCE_DIR}/Minecraft.Client/Xbox/XML/ATGXmlParser.cpp" ) add_library(Minecraft.World STATIC ${MINECRAFT_WORLD_SOURCES}) @@ -23,6 +25,7 @@ target_include_directories(Minecraft.World "${CMAKE_BINARY_DIR}/generated/" "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/include/" + "${CMAKE_SOURCE_DIR}/Minecraft.Client" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/x64headers" ) diff --git a/Minecraft.World/Chicken.cpp b/Minecraft.World/Chicken.cpp index 1af53bda..30a3d2da 100644 --- a/Minecraft.World/Chicken.cpp +++ b/Minecraft.World/Chicken.cpp @@ -120,6 +120,7 @@ int Chicken::getDeathLoot() return Item::feather->id; } +/* void Chicken::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { // drop some feathers @@ -138,6 +139,7 @@ void Chicken::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) spawnAtLocation(Item::chicken_Id, 1); } } +*/ void Chicken::addAdditonalSaveData(CompoundTag *tag) { diff --git a/Minecraft.World/Chicken.h b/Minecraft.World/Chicken.h index fbbcf6c7..971e86af 100644 --- a/Minecraft.World/Chicken.h +++ b/Minecraft.World/Chicken.h @@ -39,7 +39,7 @@ protected: virtual int getDeathSound(); virtual void playStepSound(int xt, int yt, int zt, int t); virtual int getDeathLoot(); - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); virtual void addAdditonalSaveData(CompoundTag *tag); virtual void readAdditionalSaveData(CompoundTag *tag); diff --git a/Minecraft.World/Cow.cpp b/Minecraft.World/Cow.cpp index 86620892..5232a3fb 100644 --- a/Minecraft.World/Cow.cpp +++ b/Minecraft.World/Cow.cpp @@ -80,6 +80,7 @@ int Cow::getDeathLoot() return Item::leather->id; } +/* void Cow::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { // drop some leather @@ -102,6 +103,7 @@ void Cow::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) } } } +*/ bool Cow::mobInteract(shared_ptr player) { diff --git a/Minecraft.World/Cow.h b/Minecraft.World/Cow.h index 24210631..3ac716f5 100644 --- a/Minecraft.World/Cow.h +++ b/Minecraft.World/Cow.h @@ -25,7 +25,7 @@ protected: virtual float getSoundVolume(); virtual int getDeathLoot(); virtual void playStepSound(int xt, int yt, int zt, int t); - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); public: virtual bool mobInteract(shared_ptr player); diff --git a/Minecraft.World/EnderMan.cpp b/Minecraft.World/EnderMan.cpp index 7c69f3c4..d7bc87c8 100644 --- a/Minecraft.World/EnderMan.cpp +++ b/Minecraft.World/EnderMan.cpp @@ -395,6 +395,7 @@ int EnderMan::getDeathLoot() return Item::ender_pearl_Id; } +/* void EnderMan::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { int loot = getDeathLoot(); @@ -405,6 +406,7 @@ void EnderMan::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) spawnAtLocation(loot, 1); } } +*/ // 4J Brought forward from 1.2.3 to help fix Enderman behaviour void EnderMan::setCarryingTile(int carryingTile) diff --git a/Minecraft.World/EnderMan.h b/Minecraft.World/EnderMan.h index 66dc8c19..166f2c62 100644 --- a/Minecraft.World/EnderMan.h +++ b/Minecraft.World/EnderMan.h @@ -53,7 +53,7 @@ protected: virtual int getHurtSound(); virtual int getDeathSound(); virtual int getDeathLoot(); - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); public: void setCarryingTile(int carryingTile); diff --git a/Minecraft.World/FishingHelper.cpp b/Minecraft.World/FishingHelper.cpp index 3418b271..b434a472 100644 --- a/Minecraft.World/FishingHelper.cpp +++ b/Minecraft.World/FishingHelper.cpp @@ -1,5 +1,4 @@ - -#include "../Minecraft.World/WeighedTreasure.h" +#include "../Minecraft.World/LootTableManager.h" #include "../Minecraft.World/Biome.h" #include "../Minecraft.World/FishingHelper.h" #include "../Minecraft.World/ItemInstance.h" @@ -13,31 +12,8 @@ FishingHelper* FishingHelper::getInstance() return &instance; } -FishingHelper::FishingHelper() : fishingFishArray(4), fishingJunkArray(11), fishingTreasuresArray(6) +FishingHelper::FishingHelper() { - fishingTreasuresArray[0] = new CatchWeighedItem(Item::bow_Id, 1, 0, 1); - fishingTreasuresArray[1] = new CatchWeighedItem(Item::book_Id, 1, 0, 1); - fishingTreasuresArray[2] = new CatchWeighedItem(Item::fishing_rod_Id, 1, 0, 1); - fishingTreasuresArray[3] = new CatchWeighedItem(Item::name_tag_Id, 1, 0, 1); - fishingTreasuresArray[4] = new CatchWeighedItem(Item::saddle_Id, 1, 0, 1); - fishingTreasuresArray[5] = new CatchWeighedItem(Tile::waterlily_Id, 1, 0, 1); - - fishingFishArray[0] = new CatchWeighedItem(Item::fish_Id, 1, 0, 60); // Fish - fishingFishArray[1] = new CatchWeighedItem(Item::fish_Id, 1, 1, 25); // Salmon - fishingFishArray[2] = new CatchWeighedItem(Item::fish_Id, 1, 2, 2); // Clownfish - fishingFishArray[3] = new CatchWeighedItem(Item::fish_Id, 1, 3, 13); // Pufferfish - - fishingJunkArray[0] = new CatchWeighedItem(Item::leather_Id, 1, 0, 10); - fishingJunkArray[1] = new CatchWeighedItem(Item::bone_Id, 1, 0, 10); - fishingJunkArray[2] = new CatchWeighedItem(Item::potion_Id, 1, 0, 10); // Water bottle - fishingJunkArray[3] = new CatchWeighedItem(Item::bowl_Id, 1, 0, 10); - fishingJunkArray[4] = new CatchWeighedItem(Item::leather_boots_Id, 1, 0, 10); - fishingJunkArray[5] = new CatchWeighedItem(Item::rotten_flesh_Id, 1, 0, 10); - fishingJunkArray[6] = new CatchWeighedItem(Tile::tripwire_hook_Id, 1, 0, 10); - fishingJunkArray[7] = new CatchWeighedItem(Item::stick_Id, 1, 0, 5); - fishingJunkArray[8] = new CatchWeighedItem(Item::string_Id, 1, 0, 5); - fishingJunkArray[9] = new CatchWeighedItem(Item::fishing_rod_Id, 1, 0, 2); - fishingJunkArray[10] = new CatchWeighedItem(Item::dye_Id, 10, 0, 1); // 10 ink sacs } CatchType FishingHelper::getRandCatchType(int luckLevel, int lureLevel, Random* random) @@ -55,49 +31,28 @@ CatchType FishingHelper::getRandCatchType(int luckLevel, int lureLevel, Random* else { randFloat -= junkChance; - if (randFloat < treasureChance) { return CatchType::TREASURE; } - else - { - return CatchType::FISH; - } + return CatchType::FISH; } } -CatchWeighedItem* FishingHelper::getRandCatch(CatchType catchType, Random* random) +std::shared_ptr FishingHelper::handleCatch(const LootTableDropResult &drop, Random* random) { - switch (catchType) { - case CatchType::FISH: - return static_cast(WeighedRandom::getRandomItem(random, fishingFishArray)); - case CatchType::TREASURE: - return static_cast(WeighedRandom::getRandomItem(random, fishingTreasuresArray)); - case CatchType::JUNK: - return static_cast(WeighedRandom::getRandomItem(random, fishingJunkArray)); - } -} - -std::shared_ptr FishingHelper::handleCatch(CatchWeighedItem* weighedCatch, CatchType catchType, Random* random) -{ std::shared_ptr itemInstance = std::make_shared( - weighedCatch->getItemId(), weighedCatch->getCount(), weighedCatch->getAuxValue() + drop.itemId, drop.count, drop.data ); - - if ((itemInstance->id == Item::fishing_rod_Id && catchType == CatchType::JUNK) || (itemInstance->id == Item::leather_boots_Id)) { - itemInstance->setAuxValue((int) ((double) itemInstance->getMaxDamage() * ((double) random->nextInt(901) + 100.0) / 1000.0)); // 10% to 100% damage + + if (drop.damageFraction > 0.0) + { + itemInstance->setAuxValue((int)((double)itemInstance->getMaxDamage() * drop.damageFraction)); } - else if (itemInstance->id == Item::fishing_rod_Id && catchType == CatchType::TREASURE) { - itemInstance->setAuxValue((int)((double) itemInstance->getMaxDamage() * ((double)random->nextInt(251) / 1000.0))); // 0% to 25% damage - EnchantmentHelper::enchantItem(random, itemInstance, 30); - } - else if (itemInstance->id == Item::bow_Id) { - itemInstance->setAuxValue((int)((double) itemInstance->getMaxDamage() * ((double)random->nextInt(251) / 1000.0))); // 0% to 25% damage - EnchantmentHelper::enchantItem(random, itemInstance, 30); - } - else if (itemInstance->id == Item::book_Id) { - EnchantmentHelper::enchantItem(random, itemInstance, 30); + + if (drop.enchantLevels > 0) + { + EnchantmentHelper::enchantItem(random, itemInstance, drop.enchantLevels); } return itemInstance; @@ -106,6 +61,25 @@ std::shared_ptr FishingHelper::handleCatch(CatchWeighedItem* weigh std::shared_ptr FishingHelper::getCatch(int luckLevel, int lureLevel, Random* random) { CatchType catchType = getRandCatchType(luckLevel, lureLevel, random); - CatchWeighedItem* catchWeighedItem = getRandCatch(catchType, random); - return handleCatch(catchWeighedItem, catchType, random); + + std::string tableName; + switch (catchType) + { + case CatchType::JUNK: tableName = "gameplay/fishing/junk"; break; + case CatchType::TREASURE: tableName = "gameplay/fishing/treasure"; break; + case CatchType::FISH: + default: tableName = "gameplay/fishing/fish"; break; + } + + std::vector drops = LootTableManager::Get().ResolveDrops( + tableName, false, 0, + [random](int bound) { return random->nextInt(bound); }); + + if (drops.empty()) + { + // just return a fish as this should NOT happen + return std::make_shared(Item::raw_fish); + } + + return handleCatch(drops.front(), random); } \ No newline at end of file diff --git a/Minecraft.World/FishingHelper.h b/Minecraft.World/FishingHelper.h index 77972e11..55b38f71 100644 --- a/Minecraft.World/FishingHelper.h +++ b/Minecraft.World/FishingHelper.h @@ -50,7 +50,7 @@ class FishingHelper WeighedRandomItemArray fishingTreasuresArray; CatchWeighedItem* getRandCatch(CatchType catchType, Random* random); - std::shared_ptr handleCatch(CatchWeighedItem* weighedCatch, CatchType catchType, Random* random); + std::shared_ptr handleCatch(const LootTableDropResult &drop, Random* random); CatchType getRandCatchType(int luckLevel, int lureLevel, Random* random); public: // Setup singleton diff --git a/Minecraft.World/FishingHook.cpp b/Minecraft.World/FishingHook.cpp index c3b07232..34c700c7 100644 --- a/Minecraft.World/FishingHook.cpp +++ b/Minecraft.World/FishingHook.cpp @@ -621,7 +621,7 @@ int FishingHook::retrieve() else if (nibble > 0) { FishingHelper* helper = FishingHelper::getInstance(); - std::shared_ptr fishingItemInstance = helper->getCatch(luckLevel, lureLevel, random); + std::shared_ptr fishingItemInstance = helper->getCatch(luckLevel, lureLevel, random); // TODO: add a nullptr check for this std::shared_ptr ie = std::make_shared(this->Entity::level, x, y, z, fishingItemInstance); double xa = owner->x - x; double ya = owner->y - y; diff --git a/Minecraft.World/Ghast.cpp b/Minecraft.World/Ghast.cpp index 7a4963ba..8d64fa52 100644 --- a/Minecraft.World/Ghast.cpp +++ b/Minecraft.World/Ghast.cpp @@ -220,6 +220,7 @@ int Ghast::getDeathLoot() return Item::gunpowder_Id; } +/* void Ghast::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { int count = random->nextInt(2) + random->nextInt(1 + playerBonusLevel); @@ -233,6 +234,7 @@ void Ghast::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) spawnAtLocation(Item::gunpowder_Id, 1); } } +*/ float Ghast::getSoundVolume() { diff --git a/Minecraft.World/Ghast.h b/Minecraft.World/Ghast.h index f19f737c..e8efcd45 100644 --- a/Minecraft.World/Ghast.h +++ b/Minecraft.World/Ghast.h @@ -55,7 +55,7 @@ protected: virtual int getHurtSound(); virtual int getDeathSound(); virtual int getDeathLoot(); - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); virtual float getSoundVolume(); public: diff --git a/Minecraft.World/Guardian.cpp b/Minecraft.World/Guardian.cpp index b7ed612b..6c34b0e3 100644 --- a/Minecraft.World/Guardian.cpp +++ b/Minecraft.World/Guardian.cpp @@ -200,10 +200,7 @@ float Guardian::getSpikesAnimation(float partialTicks) return isMoving() ? 1.0f : 0.0f; } - - - - +/* void Guardian::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { if (isElder()) @@ -211,6 +208,7 @@ void Guardian::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) spawnAtLocation(make_shared(Tile::sponge_Id, 1, 0), 0.0f); } } +*/ float Guardian::getEyeHeight() { diff --git a/Minecraft.World/Guardian.h b/Minecraft.World/Guardian.h index 826ec376..089944f3 100644 --- a/Minecraft.World/Guardian.h +++ b/Minecraft.World/Guardian.h @@ -39,7 +39,7 @@ protected: void playFlopSound(); void playAttackSound(); void playCurseSound(); - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); virtual void serverAiStep(); public: diff --git a/Minecraft.World/LavaSlime.cpp b/Minecraft.World/LavaSlime.cpp index 77ad827f..631195aa 100644 --- a/Minecraft.World/LavaSlime.cpp +++ b/Minecraft.World/LavaSlime.cpp @@ -66,18 +66,10 @@ int LavaSlime::getDeathLoot() void LavaSlime::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { - int loot = getDeathLoot(); - if (loot > 0 && getSize() > 1) + // drop at smallest size + if (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); - } + Mob::dropDeathLoot(wasKilledByPlayer, playerBonusLevel); } } @@ -86,7 +78,6 @@ bool LavaSlime::isOnFire() return false; } - int LavaSlime::getJumpDelay() { return Slime::getJumpDelay() * 4; diff --git a/Minecraft.World/LootTableManager.cpp b/Minecraft.World/LootTableManager.cpp new file mode 100644 index 00000000..c9d008f6 --- /dev/null +++ b/Minecraft.World/LootTableManager.cpp @@ -0,0 +1,1111 @@ +#include "stdafx.h" +#include "LootTableManager.h" +#include "StringHelpers.h" +#include "net.minecraft.world.item.h" +#include "../generated/ItemNameMap.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "Xbox/XML/ATGXmlParser.h" + +namespace fs = std::filesystem; + +// set to one to enable the yapper +#ifndef LOOT_TABLE_VERBOSE_LOGGING +#define LOOT_TABLE_VERBOSE_LOGGING 0 +#endif + +static bool IsValidDirectory(const fs::path &path) +{ + std::error_code ec; + return fs::exists(path, ec) && !ec && fs::is_directory(path, ec) && !ec; +} + +namespace +{ +// route everything through printf cause debugprintf sucks on linux +void LootLog(const char *format, ...) +{ +#if LOOT_TABLE_VERBOSE_LOGGING + char buffer[1024]; + va_list args; + va_start(args, format); + vsnprintf(buffer, sizeof(buffer), format, args); + va_end(args); + printf("%s", buffer); +#else + (void)format; +#endif +} + +std::string ToUtf8(const WCHAR *value, UINT length) +{ + if (value == nullptr || length == 0) + { + return std::string(); + } + + std::string result; + result.reserve(length); + for (UINT i = 0; i < length; ++i) + { + result.push_back(static_cast(value[i] & 0xFF)); + } + return result; +} + +bool IsXmlFileName(const std::wstring &path) +{ + if (path.length() < 4) + { + return false; + } + + return toLower(path.substr(path.length() - 4)) == L".xml"; +} + +std::string TrimString(const std::string &value) +{ + size_t start = 0; + size_t end = value.size(); + while (start < end && std::isspace(static_cast(value[start]))) + { + start++; + } + while (end > start && std::isspace(static_cast(value[end - 1]))) + { + end--; + } + return value.substr(start, end - start); +} + +int ParseInt(const std::string &value) +{ + std::stringstream stream(value); + int parsed = 0; + stream >> parsed; + return parsed; +} + +double ParseFloat(const std::string &value) +{ + std::stringstream stream(value); + double parsed = 0.0; + stream >> parsed; + return parsed; +} + +bool RandomChance(double chance, const std::function &randomInt) +{ + if (chance <= 0.0) + { + return false; + } + if (chance >= 1.0) + { + return true; + } + + const int scale = 1000000; + const int threshold = static_cast(chance * scale + 0.5); + return randomInt(scale) < threshold; +} + +std::string NormalizeTableName(const std::string &tableName) +{ + std::string normalized = tableName; + std::replace(normalized.begin(), normalized.end(), '\\', '/'); + if (!normalized.empty() && normalized[0] == '/') + { + normalized.erase(normalized.begin()); + } + + while (!normalized.empty() && normalized[0] == '.') + { + normalized.erase(normalized.begin()); + } + + return normalized; +} + +bool IsTrueValue(const std::string &value) +{ + std::string normalized = TrimString(value); + std::transform(normalized.begin(), normalized.end(), normalized.begin(), [](unsigned char ch) { + return static_cast(std::tolower(ch)); + }); + return normalized == "true" || normalized == "1" || normalized == "yes"; +} + +bool EvaluateCondition(const LootTableConditionDefinition &condition, bool entityIsOnFire) +{ + if (condition.conditionName == "entity_properties" && condition.entity == "this" && condition.propertyName == "on_fire") + { + if (condition.propertyValue.empty()) + { + return false; + } + return entityIsOnFire == IsTrueValue(condition.propertyValue); + } + + return false; +} + +// very hacky +int ResolveSmeltedItemId(const std::string &itemName, int currentItemId) +{ + if (currentItemId == 0) + { + return 0; + } + + const std::string normalized = (itemName.find("minecraft:") == 0) ? itemName.substr(10) : itemName; + + if (normalized == "potato") + { + return GetItemIdByName("baked_potato"); + } + else if (normalized == "beef" + || normalized == "porkchop" + || normalized == "chicken" + || normalized == "mutton") + { + return GetItemIdByName("cooked_" + normalized); + } + + return currentItemId; +} + +void CollectXmlFiles(const std::string &directory, std::vector &files) +{ + std::error_code ec; + for (const fs::directory_entry &entry : fs::recursive_directory_iterator(directory, ec)) + { + if (ec) + { + app.DebugPrintf("LootTableManager::CollectXmlFiles directory iteration failed: %s\n", ec.message().c_str()); + return; + } + + if (!entry.is_regular_file(ec) || ec) + { + continue; + } + + const fs::path &path = entry.path(); + if (IsXmlFileName(path.wstring())) + { + files.push_back(path.string()); + } + } +} + +class XmlLootTableCallback : public ATG::ISAXCallback +{ +public: + explicit XmlLootTableCallback(std::vector *tables) + : m_tables(tables) + { + } + + HRESULT StartDocument() override + { + return S_OK; + } + + HRESULT EndDocument() override + { + return S_OK; + } + + HRESULT ElementBegin(CONST WCHAR *strName, UINT NameLen, CONST ATG::XMLAttribute *pAttributes, UINT NumAttributes) override + { + const std::string elementName = ToUtf8(strName, NameLen); + m_elementStack.push_back(elementName); + + if (elementName == "loot_table") + { + m_tables->push_back(LootTableDefinition()); + m_currentTableIndex = static_cast(m_tables->size() - 1); + m_currentPoolIndex = -1; + m_currentEntryIndex = -1; + m_currentFunctionIndex = -1; + return S_OK; + } + + if (m_currentTableIndex < 0) + { + return S_OK; + } + + LootTableDefinition *table = &(*m_tables)[m_currentTableIndex]; + if (elementName == "pool") + { + table->pools.push_back(LootTablePoolDefinition()); + m_currentPoolIndex = static_cast(table->pools.size() - 1); + m_currentEntryIndex = -1; + m_currentFunctionIndex = -1; + return S_OK; + } + + if (m_currentPoolIndex < 0) + { + return S_OK; + } + + LootTablePoolDefinition *pool = &table->pools[m_currentPoolIndex]; + + // helper functions in an attempt to kill off repetitiveness + auto parentIs = [&](const std::string &parent) -> bool { + return m_elementStack.size() >= 2 && m_elementStack[m_elementStack.size() - 2] == parent; + }; + auto beginCapture = [&](const std::string &tag) { + if (m_captureText) + { + m_captureStack.emplace_back(m_captureText, m_captureTag); + } + m_captureText = true; + m_captureTag = tag; + m_captureBuffer.clear(); + }; + + // rolls and conditions first + if (elementName == "rolls") + { + m_currentRollCountContext = 0; + m_currentRollRange = false; + beginCapture(elementName); + return S_OK; + } + + if (elementName == "min" && m_currentFunctionIndex < 0 && m_currentPoolIndex >= 0 && parentIs("rolls")) + { + m_currentRollCountContext = 1; + m_currentRollRange = true; + beginCapture(elementName); + return S_OK; + } + + if (elementName == "max" && m_currentFunctionIndex < 0 && m_currentPoolIndex >= 0 && parentIs("rolls")) + { + m_currentRollCountContext = 2; + m_currentRollRange = true; + beginCapture(elementName); + return S_OK; + } + + if (elementName == "condition" && m_currentFunctionIndex < 0 && parentIs("conditions")) + { + pool->conditions.push_back(LootTablePoolConditionDefinition()); + m_currentPoolConditionIndex = static_cast(pool->conditions.size() - 1); + return S_OK; + } + + if (m_currentPoolConditionIndex >= 0 && m_currentFunctionIndex < 0 && parentIs("condition")) + { + if (elementName == "condition" || elementName == "chance" || elementName == "looting_multiplier") + { + beginCapture(elementName); + return S_OK; + } + } + + // entrie as fallback now since the xmls have been 'cleansed' + if ((elementName == "entry" || elementName == "entrie") && m_currentPoolIndex >= 0) + { + pool->entries.push_back(LootTableEntryDefinition()); + m_currentEntryIndex = static_cast(pool->entries.size() - 1); + m_currentFunctionIndex = -1; + return S_OK; + } + + if (m_currentEntryIndex < 0) + { + return S_OK; + } + + LootTableEntryDefinition *entry = &pool->entries[m_currentEntryIndex]; + if (elementName == "function" && m_elementStack.size() >= 2 && m_elementStack[m_elementStack.size() - 2] == "functions") + { + entry->functions.push_back(LootTableFunctionDefinition()); + m_currentFunctionIndex = static_cast(entry->functions.size() - 1); + m_currentFunctionConditionIndex = -1; + return S_OK; + } + + if (elementName == "conditions" && m_currentFunctionIndex >= 0) + { + return S_OK; + } + + if (elementName == "condition" && m_currentFunctionIndex >= 0 && m_elementStack.size() >= 2 && m_elementStack[m_elementStack.size() - 2] == "conditions") + { + LootTableFunctionDefinition *function = &entry->functions[m_currentFunctionIndex]; + function->conditions.push_back(LootTableConditionDefinition()); + m_currentFunctionConditionIndex = static_cast(function->conditions.size() - 1); + return S_OK; + } + + if (elementName == "condition" && m_currentFunctionConditionIndex >= 0 && m_currentFunctionIndex >= 0 && m_elementStack.size() >= 2 && m_elementStack[m_elementStack.size() - 2] == "condition") + { + m_captureText = true; + m_captureTag = elementName; + return S_OK; + } + + if (elementName == "entity" && m_currentFunctionConditionIndex >= 0 && m_currentFunctionIndex >= 0) + { + m_captureText = true; + m_captureTag = elementName; + return S_OK; + } + + if (elementName == "properties" && m_currentFunctionConditionIndex >= 0 && m_currentFunctionIndex >= 0) + { + return S_OK; + } + + if (m_currentFunctionConditionIndex >= 0 && m_currentFunctionIndex >= 0 && m_elementStack.size() >= 2 && m_elementStack[m_elementStack.size() - 2] == "properties") + { + m_captureText = true; + m_captureTag = elementName; + return S_OK; + } + + // then come entry tags + if (elementName == "weight" || elementName == "type" || elementName == "name" || elementName == "quality") + { + m_captureText = true; + m_captureTag = elementName; + return S_OK; + } + + if (elementName == "function" && m_currentFunctionIndex >= 0) + { + m_captureText = true; + m_captureTag = elementName; + return S_OK; + } + + if (elementName == "count") + { + m_captureText = false; + m_captureTag.clear(); + m_currentCountContext = 0; + return S_OK; + } + + // (set_data), (set_damage), and + // (enchant_with_levels) can either be plain values and/or min/max ranges + if ((elementName == "data" || elementName == "damage" || elementName == "levels") && m_currentFunctionIndex >= 0) + { + m_captureText = true; + m_captureTag = elementName; + m_captureBuffer.clear(); + if (elementName == "damage") + { + m_currentDamageContext = true; + } + return S_OK; + } + + // (enchant_with_levels), (set_nbt), and + // (looting_enchant) is always a regular value(?) + if ((elementName == "treasure" || elementName == "tag" || elementName == "limit") && m_currentFunctionIndex >= 0) + { + m_captureText = true; + m_captureTag = elementName; + m_captureBuffer.clear(); + return S_OK; + } + + if (elementName == "min") + { + if (m_currentFunctionIndex >= 0) + { + m_captureText = true; + m_captureTag = elementName; + m_currentCountContext = 1; + } + return S_OK; + } + + if (elementName == "max") + { + if (m_currentFunctionIndex >= 0) + { + m_captureText = true; + m_captureTag = elementName; + m_currentCountContext = 2; + } + return S_OK; + } + + return S_OK; + } + + HRESULT ElementContent(CONST WCHAR *strData, UINT DataLen, BOOL) override + { + if (m_captureText && strData != nullptr && DataLen > 0) + { + const std::string chunk = ToUtf8(strData, DataLen); + m_captureBuffer.append(chunk); + } + return S_OK; + } + + HRESULT ElementEnd(CONST WCHAR *strName, UINT NameLen) override + { + const std::string elementName = ToUtf8(strName, NameLen); + + if (m_captureText && m_captureTag == elementName) + { + const std::string trimmedValue = TrimString(m_captureBuffer); + + if (elementName == "rolls" && m_currentPoolIndex >= 0 && m_currentTableIndex >= 0) + { + (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].rolls = ParseInt(trimmedValue); + } + else if (elementName == "weight" && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0) + { + (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].weight = ParseInt(trimmedValue); + } + else if (elementName == "type" && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0) + { + (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].type = trimmedValue; + } + else if (elementName == "name" && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0) + { + (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].name = trimmedValue; + } + else if (elementName == "condition" && m_currentPoolIndex >= 0 && m_currentPoolConditionIndex >= 0 && m_currentFunctionIndex < 0 && m_elementStack.size() >= 2 && m_elementStack[m_elementStack.size() - 2] == "condition") + { + LootTablePoolConditionDefinition &condition = (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].conditions[m_currentPoolConditionIndex]; + condition.conditionName = trimmedValue; + } + else if (elementName == "chance" && m_currentPoolIndex >= 0 && m_currentPoolConditionIndex >= 0 && m_currentFunctionIndex < 0 && m_elementStack.size() >= 2 && m_elementStack[m_elementStack.size() - 2] == "condition") + { + LootTablePoolConditionDefinition &condition = (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].conditions[m_currentPoolConditionIndex]; + condition.chance = ParseFloat(trimmedValue); + } + else if (elementName == "looting_multiplier" && m_currentPoolIndex >= 0 && m_currentPoolConditionIndex >= 0 && m_currentFunctionIndex < 0 && m_elementStack.size() >= 2 && m_elementStack[m_elementStack.size() - 2] == "condition") + { + LootTablePoolConditionDefinition &condition = (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].conditions[m_currentPoolConditionIndex]; + condition.lootingMultiplier = ParseFloat(trimmedValue); + } + else if (elementName == "function" && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0 && m_currentFunctionIndex >= 0) + { + // just in case + std::string functionName = trimmedValue; + if (functionName.find("minecraft:") == 0) + { + functionName = functionName.substr(10); + } + (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].functions[m_currentFunctionIndex].functionName = functionName; + } + else if (elementName == "condition" && m_currentFunctionConditionIndex >= 0 && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0 && m_currentFunctionIndex >= 0) + { + LootTableFunctionDefinition &function = (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].functions[m_currentFunctionIndex]; + function.conditions[m_currentFunctionConditionIndex].conditionName = trimmedValue; + } + else if (elementName == "entity" && m_currentFunctionConditionIndex >= 0 && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0 && m_currentFunctionIndex >= 0) + { + LootTableFunctionDefinition &function = (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].functions[m_currentFunctionIndex]; + function.conditions[m_currentFunctionConditionIndex].entity = trimmedValue; + } + else if (m_currentFunctionConditionIndex >= 0 && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0 && m_currentFunctionIndex >= 0 && m_elementStack.size() >= 2 && m_elementStack[m_elementStack.size() - 2] == "properties") + { + LootTableFunctionDefinition &function = (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].functions[m_currentFunctionIndex]; + function.conditions[m_currentFunctionConditionIndex].propertyName = elementName; + function.conditions[m_currentFunctionConditionIndex].propertyValue = trimmedValue; + } + else if (elementName == "min" && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0 && m_currentFunctionIndex >= 0) + { + LootTableFunctionDefinition &function = (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].functions[m_currentFunctionIndex]; + if (m_currentDamageContext) + { + function.minDamageFraction = ParseFloat(trimmedValue); + } + else + { + function.minCount = ParseInt(trimmedValue); + } + } + else if (elementName == "max" && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0 && m_currentFunctionIndex >= 0) + { + LootTableFunctionDefinition &function = (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].functions[m_currentFunctionIndex]; + if (m_currentDamageContext) + { + function.maxDamageFraction = ParseFloat(trimmedValue); + } + else + { + function.maxCount = ParseInt(trimmedValue); + } + } + else if ((elementName == "data" || elementName == "damage" || elementName == "levels") && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0 && m_currentFunctionIndex >= 0) + { + LootTableFunctionDefinition &function = (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].functions[m_currentFunctionIndex]; + if (elementName == "damage") + { + const double value = ParseFloat(trimmedValue); + function.minDamageFraction = value; + function.maxDamageFraction = value; + } + else + { + const int value = ParseInt(trimmedValue); + function.minCount = value; + function.maxCount = value; + } + } + else if (elementName == "treasure" && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0 && m_currentFunctionIndex >= 0) + { + (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].functions[m_currentFunctionIndex].treasureOnly = IsTrueValue(trimmedValue); + } + else if (elementName == "tag" && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0 && m_currentFunctionIndex >= 0) + { + (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].functions[m_currentFunctionIndex].nbtTag = trimmedValue; + } + else if (elementName == "limit" && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0 && m_currentFunctionIndex >= 0) + { + (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].functions[m_currentFunctionIndex].limit = ParseInt(trimmedValue); + } + else if (elementName == "quality" && m_currentPoolIndex >= 0 && m_currentEntryIndex >= 0) + { + (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex].entries[m_currentEntryIndex].quality = ParseInt(trimmedValue); + } + else if (m_currentRollCountContext > 0 && m_currentPoolIndex >= 0 && m_currentTableIndex >= 0 && m_currentRollRange) + { + LootTablePoolDefinition &pool = (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex]; + if (elementName == "min") + { + pool.minRolls = ParseInt(trimmedValue); + } + else if (elementName == "max") + { + pool.maxRolls = ParseInt(trimmedValue); + } + } + else if (elementName == "condition" && m_currentPoolConditionIndex >= 0 && m_currentFunctionIndex < 0 && m_elementStack.size() >= 2 && m_elementStack[m_elementStack.size() - 2] == "conditions") + { + m_currentPoolConditionIndex = -1; + } + + if (elementName == "rolls" && m_currentPoolIndex >= 0 && m_currentTableIndex >= 0) + { + LootTablePoolDefinition &pool = (*m_tables)[m_currentTableIndex].pools[m_currentPoolIndex]; + if (!m_currentRollRange) + { + pool.rolls = ParseInt(trimmedValue); + pool.minRolls = pool.maxRolls = pool.rolls; + } + m_currentRollRange = false; + } + + m_captureBuffer.clear(); + m_captureText = false; + m_captureTag.clear(); + if (m_currentRollCountContext > 0) + { + m_currentRollCountContext = 0; + } + m_currentCountContext = 0; + if (elementName == "damage") + { + m_currentDamageContext = false; + } + if (!m_captureStack.empty()) + { + m_captureText = m_captureStack.back().first; + m_captureTag = m_captureStack.back().second; + m_captureStack.pop_back(); + } + } + + if (elementName == "pool") + { + m_currentPoolIndex = -1; + m_currentEntryIndex = -1; + m_currentFunctionIndex = -1; + m_currentPoolConditionIndex = -1; + } + else if (elementName == "entrie" || elementName == "entry") + { + m_currentEntryIndex = -1; + m_currentFunctionIndex = -1; + m_currentFunctionConditionIndex = -1; + } + else if (elementName == "function" && !m_elementStack.empty() && m_elementStack.size() >= 2 && m_elementStack[m_elementStack.size() - 2] == "functions") + { + m_currentFunctionIndex = -1; + m_currentFunctionConditionIndex = -1; + } + else if (elementName == "loot_table") + { + if (m_currentTableIndex >= 0) + { + const LootTableDefinition &finishedTable = (*m_tables)[m_currentTableIndex]; + for (size_t p = 0; p < finishedTable.pools.size(); ++p) + { + LootLog("[LootDbg] parsed pool[%zu]: entries=%zu conditions=%zu\n", + p, finishedTable.pools[p].entries.size(), finishedTable.pools[p].conditions.size()); + } + } + + m_currentTableIndex = -1; + m_currentPoolIndex = -1; + m_currentEntryIndex = -1; + m_currentFunctionIndex = -1; + } + + if (!m_elementStack.empty()) + { + m_elementStack.pop_back(); + } + + return S_OK; + } + + HRESULT CDATABegin() override { return S_OK; } + HRESULT CDATAData(CONST WCHAR *strCDATA, UINT CDATALen, BOOL /*bMore*/) override + { + if (m_captureText && strCDATA != nullptr && CDATALen > 0) + { + m_captureBuffer.append(ToUtf8(strCDATA, CDATALen)); + } + return S_OK; + } + HRESULT CDATAEnd() override { return S_OK; } + + VOID Error(HRESULT hError, CONST CHAR *strMessage) override + { + app.DebugPrintf("Loot table XML parse error (%08X): %s\n", hError, strMessage ? strMessage : "(unknown)"); + } + +private: + std::vector *m_tables; + std::vector m_elementStack; + int m_currentTableIndex = -1; + int m_currentPoolIndex = -1; + int m_currentEntryIndex = -1; + int m_currentFunctionIndex = -1; + int m_currentPoolConditionIndex = -1; + int m_currentFunctionConditionIndex = -1; + int m_currentCountContext = 0; + int m_currentRollCountContext = 0; + bool m_currentRollRange = false; + bool m_currentDamageContext = false; + bool m_captureText = false; + std::string m_captureTag; + std::string m_captureBuffer; + std::vector> m_captureStack; +}; + +int ResolveItemId(const std::string &name) +{ + // strip minecraft prefix if present + const std::string itemName = (name.find("minecraft:") == 0) ? name.substr(10) : name; + + const int itemId = GetItemIdByName(itemName); + if (itemId == -1) + { + app.DebugPrintf("[LootDbg] ResolveItemId: unrecognized item name '%s' -> 0\n", name.c_str()); + return 0; + } + + return itemId; +} + +} + +LootTableManager &LootTableManager::Get() +{ + static LootTableManager instance; + return instance; +} + +bool LootTableManager::LoadFromDisk(const std::string &baseDirectory) +{ + m_tables.clear(); + m_tableIndexByPath.clear(); + m_loaded = false; + m_rootDirectory.clear(); + + // hardcoded but seems to work just fine nonetheless + // maybe we should un-hardcode it later? idk why we would need to tho + std::vector candidates; + if (!baseDirectory.empty()) + { + candidates.push_back(baseDirectory); + } + candidates.push_back("./Common/Media/MediaWindows64/Structures/loot_tables"); + + std::string resolvedRoot; + for (std::vector::const_iterator it = candidates.begin(); it != candidates.end(); ++it) + { + fs::path candidatePath(*it); + if (IsValidDirectory(candidatePath)) + { + resolvedRoot = candidatePath.string(); + break; + } + } + + if (resolvedRoot.empty()) + { + app.DebugPrintf("[LootDbg] LoadFromDisk failed: no valid root directory found\n"); + return false; + } + + std::vector files; + CollectXmlFiles(resolvedRoot, files); + + for (std::vector::const_iterator it = files.begin(); it != files.end(); ++it) + { + const size_t tableCountBeforeParse = m_tables.size(); + ATG::XMLParser parser; + XmlLootTableCallback callback(&m_tables); + parser.RegisterSAXCallbackInterface(&callback); + + const HRESULT result = parser.ParseXMLFile(it->c_str()); + const bool tableWasAdded = m_tables.size() > tableCountBeforeParse; + + if (result == S_OK && tableWasAdded) + { + LootTableDefinition &table = m_tables.back(); + size_t startPos = it->find("loot_tables"); + if (startPos != std::string::npos) + { + table.path = NormalizeTableName(it->substr(startPos + std::string("loot_tables").length() + 1)); + } + else + { + table.path = NormalizeTableName(*it); + } + std::replace(table.path.begin(), table.path.end(), '\\', '/'); + if (!table.path.empty() && table.path.substr(table.path.length() - 4) == ".xml") + { + table.path.erase(table.path.length() - 4); + } + + m_tableIndexByPath[table.path] = m_tables.size() - 1; + } + else + { + app.DebugPrintf("LootTableManager::LoadFromDisk failed to parse '%s' (0x%08X)\n", it->c_str(), result); + } + } + + m_rootDirectory = resolvedRoot; + m_loaded = !m_tables.empty(); + + return m_loaded; +} + +bool LootTableManager::HasLoadedTables() const +{ + return m_loaded; +} + +const LootTableDefinition *LootTableManager::GetTable(const std::string &tableName) const +{ + const std::string normalized = NormalizeTableName(tableName); + + auto it = m_tableIndexByPath.find(normalized); + if (it == m_tableIndexByPath.end()) + { + app.DebugPrintf("[LootDbg] GetTable: no match for '%s'\n", normalized.c_str()); + return nullptr; + } + + return &m_tables[it->second]; +} + +std::vector LootTableManager::ResolveDrops( + const std::string &tableName, + bool wasKilledByPlayer, + int playerBonusLevel, + const std::function &randomInt, + bool entityIsOnFire) const +{ + LootLog("[LootDbg] ResolveDrops: table='%s' wasKilledByPlayer=%d playerBonusLevel=%d\n", + tableName.c_str(), wasKilledByPlayer, playerBonusLevel); + + std::vector drops; + const LootTableDefinition *table = GetTable(tableName); + if (table == nullptr) + { + LootLog("[LootDbg] ResolveDrops: table not found, returning empty\n"); + return drops; + } + + // some entries have type="loot_table" instead of type="item" + const int kMaxLootTableRecursionDepth = 8; + std::function &)> resolveTable = + [&](const LootTableDefinition *currentTable, int depth, std::vector &outDrops) + { + if (currentTable == nullptr || depth > kMaxLootTableRecursionDepth) + { + return; + } + + for (size_t poolIndex = 0; poolIndex < currentTable->pools.size(); ++poolIndex) + { + const LootTablePoolDefinition &pool = currentTable->pools[poolIndex]; + + bool hasKilledByPlayerCondition = false; + bool poolConditionsPass = true; + for (std::vector::const_iterator condIt = pool.conditions.begin(); condIt != pool.conditions.end(); ++condIt) + { + LootLog("[LootDbg] pool[%zu] condition entry: '%s' chance=%f lootingMultiplier=%f\n", + poolIndex, condIt->conditionName.c_str(), condIt->chance, condIt->lootingMultiplier); + + if (condIt->conditionName == "killed_by_player") + { + hasKilledByPlayerCondition = true; + if (!wasKilledByPlayer) + { + poolConditionsPass = false; + break; + } + } + else if (condIt->conditionName == "random_chance_with_looting") + { + double adjustedChance = condIt->chance + condIt->lootingMultiplier * playerBonusLevel; + if (!RandomChance(adjustedChance, randomInt)) + { + poolConditionsPass = false; + break; + } + } + else if (!condIt->conditionName.empty()) + { + // dont comment this debug print out pls + app.DebugPrintf("[LootDbg] pool[%zu] condition '%s' not implemented, skipping pool\n", + poolIndex, condIt->conditionName.c_str()); + poolConditionsPass = false; + break; + } + else + { + poolConditionsPass = false; + break; + } + } + + if (!poolConditionsPass) + { + LootLog("[LootDbg] pool[%zu]: skipped due to condition failure\n", poolIndex); + continue; + } + + LootLog("[LootDbg] pool[%zu]: rolls=%d entries=%zu hasKilledByPlayerCondition=%d\n", + poolIndex, pool.rolls, pool.entries.size(), hasKilledByPlayerCondition); + + if (pool.minRolls != pool.maxRolls) + { + LootLog("[LootDbg] pool[%zu]: rolls range min=%d max=%d\n", poolIndex, pool.minRolls, pool.maxRolls); + } + + if (hasKilledByPlayerCondition && !wasKilledByPlayer) + { + LootLog("[LootDbg] pool[%zu]: skipped (requires killed_by_player)\n", poolIndex); + continue; + } + + int poolRolls = pool.rolls; + if (pool.minRolls != pool.maxRolls) + { + int range = pool.maxRolls - pool.minRolls + 1; + if (range > 0) + { + poolRolls = pool.minRolls + randomInt(range); + } + } + + for (int roll = 0; roll < poolRolls; ++roll) + { + int totalWeight = 0; + for (std::vector::const_iterator entryIt = pool.entries.begin(); entryIt != pool.entries.end(); ++entryIt) + { + totalWeight += entryIt->weight; + } + + LootLog("[LootDbg] roll %d: totalWeight=%d\n", roll, totalWeight); + + if (totalWeight <= 0) + { + LootLog("[LootDbg] roll %d: SKIPPED (totalWeight <= 0)\n", roll); + continue; + } + + int choice = randomInt(totalWeight); + LootLog("[LootDbg] roll %d: choice=%d\n", roll, choice); + + int runningWeight = 0; + for (std::vector::const_iterator entryIt = pool.entries.begin(); entryIt != pool.entries.end(); ++entryIt) + { + runningWeight += entryIt->weight; + if (choice < runningWeight) + { + if (entryIt->type == "empty") + { + LootLog("[LootDbg] selected entry type=empty -> no drop\n"); + break; + } + + if (entryIt->type == "loot_table") + { + // table path, NOT an item + const std::string referencedTableName = + (entryIt->name.find("minecraft:") == 0) ? entryIt->name.substr(10) : entryIt->name; + const LootTableDefinition *referencedTable = GetTable(referencedTableName); + LootLog("[LootDbg] selected entry type=loot_table name='%s' -> %s\n", + referencedTableName.c_str(), referencedTable ? "resolved" : "NOT FOUND"); + resolveTable(referencedTable, depth + 1, outDrops); + break; + } + + LootTableDropResult result; + result.itemId = ResolveItemId(entryIt->name); + result.count = 1; + + LootLog("[LootDbg] selected entry name='%s' weight=%d itemId=%d\n", + entryIt->name.c_str(), entryIt->weight, result.itemId); + + for (std::vector::const_iterator functionIt = entryIt->functions.begin(); functionIt != entryIt->functions.end(); ++functionIt) + { + bool shouldApplyFunction = true; + for (std::vector::const_iterator conditionIt = functionIt->conditions.begin(); conditionIt != functionIt->conditions.end(); ++conditionIt) + { + if (!EvaluateCondition(*conditionIt, entityIsOnFire)) + { + shouldApplyFunction = false; + LootLog("[LootDbg] skipping function '%s' due to condition '%s'\n", + functionIt->functionName.c_str(), conditionIt->conditionName.c_str()); + break; + } + } + + if (!shouldApplyFunction) + { + continue; + } + + if (functionIt->functionName == "set_count") + { + if (functionIt->maxCount >= functionIt->minCount) + { + const int range = functionIt->maxCount - functionIt->minCount + 1; + result.count = functionIt->minCount + (range > 0 ? randomInt(range) : 0); + LootLog("[LootDbg] set_count min=%d max=%d -> count=%d\n", + functionIt->minCount, functionIt->maxCount, result.count); + } + } + else if (functionIt->functionName == "looting_enchant") + { + if (playerBonusLevel > 0 && functionIt->maxCount >= functionIt->minCount) + { + const int range = functionIt->maxCount - functionIt->minCount + 1; + LootLog("[LootDbg] looting_enchant min=%d max=%d bonus=%d range=%d\n", + functionIt->minCount, functionIt->maxCount, playerBonusLevel, range); + for (int i = 0; i < playerBonusLevel; ++i) + { + if (range > 0) + { + const int lootingRoll = functionIt->minCount + randomInt(range); + result.count += lootingRoll; + LootLog("[LootDbg] looting roll %d: +%d -> count=%d\n", i + 1, lootingRoll, result.count); + } + } + if (functionIt->limit > 0 && result.count > functionIt->limit) + { + LootLog("[LootDbg] looting_enchant capped by limit=%d (was count=%d)\n", + functionIt->limit, result.count); + result.count = functionIt->limit; + } + } + else + { + LootLog("[LootDbg] looting_enchant SKIPPED (playerBonusLevel=%d)\n", playerBonusLevel); + } + } + else if (functionIt->functionName == "furnace_smelt") + { + result.itemId = ResolveSmeltedItemId(entryIt->name, result.itemId); + LootLog("[LootDbg] furnace_smelt -> itemId=%d\n", result.itemId); + } + else if (functionIt->functionName == "set_data") + { + if (functionIt->maxCount >= functionIt->minCount) + { + const int range = functionIt->maxCount - functionIt->minCount + 1; + result.data = functionIt->minCount + (range > 0 ? randomInt(range) : 0); + LootLog("[LootDbg] set_data min=%d max=%d -> data=%d\n", + functionIt->minCount, functionIt->maxCount, result.data); + } + } + else if (functionIt->functionName == "set_damage") + { + if (functionIt->maxDamageFraction >= functionIt->minDamageFraction) + { + const double range = functionIt->maxDamageFraction - functionIt->minDamageFraction; + double fraction = functionIt->minDamageFraction; + if (range > 0.0) + { + const int precision = 1000000; + fraction += (range * randomInt(precision)) / static_cast(precision); + } + result.damageFraction = fraction; + LootLog("[LootDbg] set_damage min=%f max=%f -> damageFraction=%f\n", + functionIt->minDamageFraction, functionIt->maxDamageFraction, result.damageFraction); + } + } + else if (functionIt->functionName == "set_nbt") + { + result.nbtTag = functionIt->nbtTag; + LootLog("[LootDbg] set_nbt -> tag='%s'\n", result.nbtTag.c_str()); + } + else if (functionIt->functionName == "enchant_with_levels") + { + if (functionIt->maxCount >= functionIt->minCount) + { + const int range = functionIt->maxCount - functionIt->minCount + 1; + result.enchantLevels = functionIt->minCount + (range > 0 ? randomInt(range) : 0); + result.treasureEnchant = functionIt->treasureOnly; + LootLog("[LootDbg] enchant_with_levels min=%d max=%d treasure=%d -> levels=%d\n", + functionIt->minCount, functionIt->maxCount, functionIt->treasureOnly, result.enchantLevels); + } + } + else if (functionIt->functionName == "enchant_randomly") + { + LootLog("[LootDbg] enchant_randomly -> deferred to enchantment system\n"); + } + } + + LootLog("[LootDbg] final result: itemId=%d count=%d (will drop=%d)\n", + result.itemId, result.count, (result.itemId != 0 && result.count > 0)); + + if (result.itemId != 0 && result.count > 0) + { + outDrops.push_back(result); + } + break; + } + } + } + } + }; + + resolveTable(table, 0, drops); + + LootLog("[LootDbg] ResolveDrops: returning %zu drop(s)\n", drops.size()); + return drops; +} diff --git a/Minecraft.World/LootTableManager.h b/Minecraft.World/LootTableManager.h new file mode 100644 index 00000000..48ff1394 --- /dev/null +++ b/Minecraft.World/LootTableManager.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include +#include + +struct LootTableConditionDefinition +{ + std::string conditionName; + std::string entity; + std::string propertyName; + std::string propertyValue; +}; + +struct LootTableFunctionDefinition +{ + std::string functionName; + int minCount = 0; + int maxCount = 0; + std::vector conditions; + + std::string nbtTag; // set_nbt + int limit = 0; // looting_enchant + bool treasureOnly = false; // enchant_with_levels + + double minDamageFraction = 0.0; // set_damage + double maxDamageFraction = 0.0; // set_damage +}; + +struct LootTableEntryDefinition +{ + std::string type; + std::string name; + int weight = 1; + int quality = 0; // only used by type="loot_table" entries it seems like + std::vector functions; +}; + +struct LootTablePoolConditionDefinition +{ + std::string conditionName; + double chance = 0.0; + double lootingMultiplier = 0.0; +}; + +struct LootTablePoolDefinition +{ + int rolls = 1; + int minRolls = 1; + int maxRolls = 1; + std::vector entries; + std::vector conditions; +}; + +struct LootTableDefinition +{ + std::string path; + std::vector pools; +}; + +struct LootTableDropResult +{ + int itemId = 0; + int count = 0; + + int data = 0; // set_data + int damage = 0; // unused (replaced by set_damage) + double damageFraction = 0.0; // set_damage: 0.0 = undamaged, 1.0 = fully damaged + std::string nbtTag; // set_nbt + int enchantLevels = 0; // enchant_with_levels: target enchantment level + bool treasureEnchant = false; // enchant_with_levels: treasure-only allowlist +}; + +class LootTableManager +{ +public: + static LootTableManager &Get(); + + bool LoadFromDisk(const std::string &baseDirectory = ""); + bool HasLoadedTables() const; + + const LootTableDefinition *GetTable(const std::string &tableName) const; + std::vector ResolveDrops( + const std::string &tableName, + bool wasKilledByPlayer, + int playerBonusLevel, + const std::function &randomInt, + bool entityIsOnFire = false) const; + +private: + std::vector m_tables; + std::unordered_map m_tableIndexByPath; + std::string m_rootDirectory; + bool m_loaded = false; +}; diff --git a/Minecraft.World/MineShaftPieces.cpp b/Minecraft.World/MineShaftPieces.cpp index d81223f8..d8c005c0 100644 --- a/Minecraft.World/MineShaftPieces.cpp +++ b/Minecraft.World/MineShaftPieces.cpp @@ -6,6 +6,7 @@ #include "net.minecraft.world.level.tile.entity.h" #include "net.minecraft.world.item.h" #include "JavaMath.h" +#include "LootTableManager.h" #include "WeighedTreasure.h" #include "MineShaftPieces.h" @@ -423,7 +424,7 @@ void MineShaftPieces::MineShaftCorridor::addChildren(StructurePiece *startPiece, } } -bool MineShaftPieces::MineShaftCorridor::createChest(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, WeighedTreasureArray treasure, int numRolls) +bool MineShaftPieces::MineShaftCorridor::createChest(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, const std::vector &drops) { int worldX = getWorldX(x, z); int worldY = getWorldY(y); @@ -435,7 +436,7 @@ bool MineShaftPieces::MineShaftCorridor::createChest(Level *level, BoundingBox * { level->setTileAndData(worldX, worldY, worldZ, Tile::rail_Id, getOrientationData(Tile::rail_Id, random->nextBoolean() ? RailTile::DIR_FLAT_X : RailTile::DIR_FLAT_Z), Tile::UPDATE_CLIENTS); shared_ptr chest = std::make_shared(level, worldX + 0.5f, worldY + 0.5f, worldZ + 0.5f); - WeighedTreasure::addChestItems(random, treasure, chest, numRolls); + WeighedTreasure::addChestItems(random, drops, chest); level->addEntity(chest); return true; } @@ -498,11 +499,15 @@ bool MineShaftPieces::MineShaftCorridor::postProcess(Level *level, Random *rando if (random->nextInt(100) == 0) { - createChest(level, chunkBB, random, x1, y0, z - 1, WeighedTreasure::addToTreasure(smallTreasureItems, Item::enchanted_book->createForRandomTreasure(random)), 3 + random->nextInt(4)); + createChest(level, chunkBB, random, x1, y0, z - 1, + LootTableManager::Get().ResolveDrops("chests/abandoned_mineshaft", false, 0, + [random](int bound) { return random->nextInt(bound); })); } if (random->nextInt(100) == 0) { - createChest(level, chunkBB, random, x0, y0, z + 1, WeighedTreasure::addToTreasure(smallTreasureItems, Item::enchanted_book->createForRandomTreasure(random)), 3 + random->nextInt(4)); + createChest(level, chunkBB, random, x0, y0, z + 1, + LootTableManager::Get().ResolveDrops("chests/abandoned_mineshaft", false, 0, + [random](int bound) { return random->nextInt(bound); })); } if (spiderCorridor && !hasPlacedSpider) diff --git a/Minecraft.World/MineShaftPieces.h b/Minecraft.World/MineShaftPieces.h index c2278467..af71da62 100644 --- a/Minecraft.World/MineShaftPieces.h +++ b/Minecraft.World/MineShaftPieces.h @@ -75,7 +75,7 @@ public: virtual void addChildren(StructurePiece *startPiece, list *pieces, Random *random); protected: - virtual bool createChest(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, WeighedTreasureArray treasure, int numRolls); + virtual bool createChest(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, const std::vector &drops); public: virtual bool postProcess(Level *level, Random *random, BoundingBox *chunkBB); diff --git a/Minecraft.World/Minecraft.World.cpp b/Minecraft.World/Minecraft.World.cpp index bbf11b67..3e9cb16d 100644 --- a/Minecraft.World/Minecraft.World.cpp +++ b/Minecraft.World/Minecraft.World.cpp @@ -17,6 +17,8 @@ #include "Minecraft.World.h" #include "../Minecraft.Client/ServerLevel.h" +#include "LootTableManager.h" + #ifdef _DURANGO #include "DurangoStats.h" #else @@ -28,6 +30,12 @@ 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 + // preload loot tables so bonus chests dont appear empty upon world generation + if (!LootTableManager::Get().LoadFromDisk("")) + { + app.DebugPrintf("FATAL: LootTableManager failed to load loot tables\n"); + } + Packet::staticCtor(); { diff --git a/Minecraft.World/Mob.cpp b/Minecraft.World/Mob.cpp index 08fd01c1..4f1fe6ca 100644 --- a/Minecraft.World/Mob.cpp +++ b/Minecraft.World/Mob.cpp @@ -23,6 +23,7 @@ #include "../Minecraft.Client/EntityTracker.h" #include "com.mojang.nbt.h" #include "Mob.h" +#include "LootTableManager.h" #include "../Minecraft.Client/Textures.h" #include "SoundTypes.h" #include "BasicTypeContainers.h" @@ -257,6 +258,47 @@ int Mob::getDeathLoot() void Mob::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { + const wstring entityName = getName(); + const wstring encodedId = getEncodeId(); + const wchar_t* nameToPrint = entityName.empty() + ? (encodedId.empty() ? L"unknown" : encodedId.c_str()) + : entityName.c_str(); + + wstring loweredName = nameToPrint; + std::transform(loweredName.begin(), loweredName.end(), loweredName.begin(), ::towlower); + std::replace(loweredName.begin(), loweredName.end(), L' ', L'_'); + + const std::string loweredNameNarrow(loweredName.begin(), loweredName.end()); + const std::string lootTableName = "entities/" + loweredNameNarrow; + + LootTableManager &lootTables = LootTableManager::Get(); + if (!lootTables.HasLoadedTables()) + { + lootTables.LoadFromDisk(); + } + + const LootTableDefinition *table = lootTables.GetTable(lootTableName); + if (table != nullptr) + { + const std::vector drops = lootTables.ResolveDrops( + lootTableName, + wasKilledByPlayer, + playerBonusLevel, + [this](int maxExclusive) -> int { return random->nextInt(maxExclusive); }, + isOnFire()); + + + if (!drops.empty()) + { + for (auto it = drops.begin(); it != drops.end(); ++it) + { + spawnAtLocation(it->itemId, it->count); + } + return; + } + } + + /* int loot = getDeathLoot(); if (loot > 0) { @@ -268,6 +310,7 @@ void Mob::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) for (int i = 0; i < count; i++) spawnAtLocation(loot, 1); } + */ } void Mob::addAdditonalSaveData(CompoundTag *entityTag) diff --git a/Minecraft.World/MonsterRoomFeature.cpp b/Minecraft.World/MonsterRoomFeature.cpp index a6c8ba71..6ec5c89e 100644 --- a/Minecraft.World/MonsterRoomFeature.cpp +++ b/Minecraft.World/MonsterRoomFeature.cpp @@ -5,27 +5,9 @@ #include "net.minecraft.world.level.tile.entity.h" #include "net.minecraft.world.item.h" #include "WeighedTreasure.h" +#include "LootTableManager.h" #include "MonsterRoomFeature.h" -WeighedTreasure *MonsterRoomFeature::monsterRoomTreasure[MonsterRoomFeature::TREASURE_ITEMS_COUNT] = -{ - new WeighedTreasure(Item::saddle_Id, 0, 1, 1, 10), - new WeighedTreasure(Item::iron_ingot_Id, 0, 1, 4, 10), - new WeighedTreasure(Item::bread_Id, 0, 1, 1, 10), - new WeighedTreasure(Item::wheat_Id, 0, 1, 4, 10), - new WeighedTreasure(Item::gunpowder_Id, 0, 1, 4, 10), - new WeighedTreasure(Item::string_Id, 0, 1, 4, 10), - new WeighedTreasure(Item::bucket_Id, 0, 1, 1, 10), - new WeighedTreasure(Item::golden_apple_Id, 0, 1, 1, 1), - new WeighedTreasure(Item::redstone_Id, 0, 1, 4, 10), - new WeighedTreasure(Item::record_13_Id, 0, 1, 1, 10), - new WeighedTreasure(Item::record_cat_Id, 0, 1, 1, 10), - new WeighedTreasure(Item::name_tag_Id, 0, 1, 1, 10), - new WeighedTreasure(Item::golden_horse_armor_Id, 0, 1, 1, 2), - new WeighedTreasure(Item::iron_horse_armor_Id, 0, 1, 1, 5), - new WeighedTreasure(Item::diamond_horse_armor_Id, 0, 1, 1, 1), -}; - bool MonsterRoomFeature::place(Level *level, Random *random, int x, int y, int z) { int hr = 3; @@ -107,12 +89,13 @@ bool MonsterRoomFeature::place(Level *level, Random *random, int x, int y, int z if (count != 1) continue; level->setTileAndData(xc, yc, zc, Tile::chest_Id, 0, Tile::UPDATE_CLIENTS); - WeighedTreasureArray wrapperArray(monsterRoomTreasure, TREASURE_ITEMS_COUNT); - WeighedTreasureArray treasure = WeighedTreasure::addToTreasure(wrapperArray, Item::enchanted_book->createForRandomTreasure(random)); shared_ptr chest = dynamic_pointer_cast( level->getTileEntity(xc, yc, zc) ); if (chest != nullptr ) { - WeighedTreasure::addChestItems(random, treasure, chest, 8); + WeighedTreasure::addChestItems(random, + LootTableManager::Get().ResolveDrops("chests/simple_dungeon", false, 0, + [random](int bound) { return random->nextInt(bound); }), + chest); } break; diff --git a/Minecraft.World/MonsterRoomFeature.h b/Minecraft.World/MonsterRoomFeature.h index 2b911e10..15156586 100644 --- a/Minecraft.World/MonsterRoomFeature.h +++ b/Minecraft.World/MonsterRoomFeature.h @@ -6,11 +6,6 @@ class WeighedTreasure; class MonsterRoomFeature : public Feature { -private: - - static const int TREASURE_ITEMS_COUNT = 15; - static WeighedTreasure *monsterRoomTreasure[TREASURE_ITEMS_COUNT]; - public: virtual bool place(Level *level, Random *random, int x, int y, int z); diff --git a/Minecraft.World/NetherBridgePieces.cpp b/Minecraft.World/NetherBridgePieces.cpp index f75f67b1..81b7e0ba 100644 --- a/Minecraft.World/NetherBridgePieces.cpp +++ b/Minecraft.World/NetherBridgePieces.cpp @@ -7,6 +7,7 @@ #include "net.minecraft.world.level.storage.h" #include "net.minecraft.world.level.levelgen.structure.h" #include "WeighedTreasure.h" +#include "LootTableManager.h" #include "NetherBridgePieces.h" #include "Direction.h" @@ -136,20 +137,6 @@ NetherBridgePieces::NetherBridgePiece *NetherBridgePieces::findAndCreateBridgePi return structurePiece; } -WeighedTreasure *NetherBridgePieces::NetherBridgePiece::fortressTreasureItems[FORTRESS_TREASURE_ITEMS_COUNT] = { - new WeighedTreasure(Item::diamond_Id, 0, 1, 3, 5), - new WeighedTreasure(Item::iron_ingot_Id, 0, 1, 5, 5), - new WeighedTreasure(Item::gold_ingot_Id, 0, 1, 3, 15), - new WeighedTreasure(Item::golden_sword_Id, 0, 1, 1, 5), - new WeighedTreasure(Item::golden_chestplate_Id, 0, 1, 1, 5), - new WeighedTreasure(Item::flint_and_steel_Id, 0, 1, 1, 5), - new WeighedTreasure(Item::netherwart_seeds_Id, 0, 3, 7, 5), - new WeighedTreasure(Item::saddle_Id, 0, 1, 1, 10), - new WeighedTreasure(Item::golden_horse_armor_Id, 0, 1, 1, 8), - new WeighedTreasure(Item::iron_horse_armor_Id, 0, 1, 1, 5), - new WeighedTreasure(Item::diamond_horse_armor_Id, 0, 1, 1, 3), -}; - NetherBridgePieces::NetherBridgePiece::NetherBridgePiece() { // for reflection @@ -1381,7 +1368,9 @@ bool NetherBridgePieces::CastleSmallCorridorRightTurnPiece::postProcess(Level *l if (chunkBB->isInside(x, y, z)) { isNeedingChest = false; - createChest(level, chunkBB, random, 1, 2, 3, WeighedTreasureArray(fortressTreasureItems,FORTRESS_TREASURE_ITEMS_COUNT), 2 + random->nextInt(4)); + createChest(level, chunkBB, random, 1, 2, 3, + LootTableManager::Get().ResolveDrops("chests/nether_bridge", false, 0, + [random](int bound) { return random->nextInt(bound); })); } } @@ -1473,7 +1462,9 @@ bool NetherBridgePieces::CastleSmallCorridorLeftTurnPiece::postProcess(Level *le if (chunkBB->isInside(x, y, z)) { isNeedingChest = false; - createChest(level, chunkBB, random, 3, 2, 3, WeighedTreasureArray(fortressTreasureItems,FORTRESS_TREASURE_ITEMS_COUNT), 2 + random->nextInt(4)); + createChest(level, chunkBB, random, 3, 2, 3, + LootTableManager::Get().ResolveDrops("chests/nether_bridge", false, 0, + [random](int bound) { return random->nextInt(bound); })); } } diff --git a/Minecraft.World/NetherBridgePieces.h b/Minecraft.World/NetherBridgePieces.h index 81cb1560..97c6c72b 100644 --- a/Minecraft.World/NetherBridgePieces.h +++ b/Minecraft.World/NetherBridgePieces.h @@ -65,9 +65,6 @@ private: class NetherBridgePiece : public StructurePiece { - protected: - static const int FORTRESS_TREASURE_ITEMS_COUNT = 11; - static WeighedTreasure *fortressTreasureItems[FORTRESS_TREASURE_ITEMS_COUNT]; public: NetherBridgePiece(); diff --git a/Minecraft.World/Ocelot.cpp b/Minecraft.World/Ocelot.cpp index ea2f543e..0ee2e63a 100644 --- a/Minecraft.World/Ocelot.cpp +++ b/Minecraft.World/Ocelot.cpp @@ -198,9 +198,11 @@ bool Ocelot::hurt(DamageSource *source, float dmg) return TamableAnimal::hurt(source, dmg); } +/* void Ocelot::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { } +*/ bool Ocelot::mobInteract(shared_ptr player) { diff --git a/Minecraft.World/Ocelot.h b/Minecraft.World/Ocelot.h index 8336f856..cdc7123b 100644 --- a/Minecraft.World/Ocelot.h +++ b/Minecraft.World/Ocelot.h @@ -71,8 +71,8 @@ public: virtual bool doHurtTarget(shared_ptr target); virtual bool hurt(DamageSource *source, float dmg); -protected: - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); +// protected: + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); public: virtual bool mobInteract(shared_ptr player); diff --git a/Minecraft.World/Ozelot.cpp b/Minecraft.World/Ozelot.cpp deleted file mode 100644 index 147366e3..00000000 --- a/Minecraft.World/Ozelot.cpp +++ /dev/null @@ -1,333 +0,0 @@ -#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_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_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_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 deleted file mode 100644 index c763f3bc..00000000 --- a/Minecraft.World/Ozelot.h +++ /dev/null @@ -1,76 +0,0 @@ -#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 deleted file mode 100644 index 27ca2b5c..00000000 --- a/Minecraft.World/OzelotAttackGoal.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#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 deleted file mode 100644 index c214b722..00000000 --- a/Minecraft.World/OzelotAttackGoal.h +++ /dev/null @@ -1,25 +0,0 @@ -#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/Pig.cpp b/Minecraft.World/Pig.cpp index 03c9a5da..046c847d 100644 --- a/Minecraft.World/Pig.cpp +++ b/Minecraft.World/Pig.cpp @@ -133,6 +133,7 @@ int Pig::getDeathLoot() return Item::porkchop_Id; } +/* void Pig::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { int count = random->nextInt(3) + 1 + random->nextInt(1 + playerBonusLevel); @@ -150,6 +151,7 @@ void Pig::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) } if (hasSaddle()) spawnAtLocation(Item::saddle_Id, 1); } +*/ bool Pig::hasSaddle() { diff --git a/Minecraft.World/Pig.h b/Minecraft.World/Pig.h index 0dfbf38c..63d1a0af 100644 --- a/Minecraft.World/Pig.h +++ b/Minecraft.World/Pig.h @@ -48,7 +48,7 @@ public: protected: virtual int getDeathLoot(); - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); public: bool hasSaddle(); diff --git a/Minecraft.World/PigZombie.cpp b/Minecraft.World/PigZombie.cpp index b294e9a3..a164b38b 100644 --- a/Minecraft.World/PigZombie.cpp +++ b/Minecraft.World/PigZombie.cpp @@ -143,6 +143,7 @@ int PigZombie::getDeathSound() return eSoundType_MOB_ZOMBIEPIG_DEATH; } +/* void PigZombie::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { int count = random->nextInt(2 + playerBonusLevel); @@ -156,16 +157,19 @@ void PigZombie::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) spawnAtLocation(Item::gold_nugget_Id, 1); } } +*/ bool PigZombie::mobInteract(shared_ptr player) { return false; } +/* void PigZombie::dropRareDeathLoot(int rareLootLevel) { spawnAtLocation(Item::gold_ingot_Id, 1); } +*/ int PigZombie::getDeathLoot() { diff --git a/Minecraft.World/PigZombie.h b/Minecraft.World/PigZombie.h index 21ccd49e..60c06b09 100644 --- a/Minecraft.World/PigZombie.h +++ b/Minecraft.World/PigZombie.h @@ -47,13 +47,13 @@ protected: virtual int getAmbientSound(); virtual int getHurtSound(); virtual int getDeathSound(); - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); public: virtual bool mobInteract(shared_ptr player); protected: - virtual void dropRareDeathLoot(int rareLootLevel); + // virtual void dropRareDeathLoot(int rareLootLevel); virtual int getDeathLoot(); virtual void populateDefaultEquipmentSlots(); diff --git a/Minecraft.World/Rabbit.cpp b/Minecraft.World/Rabbit.cpp index 6505a860..9c20aa00 100644 --- a/Minecraft.World/Rabbit.cpp +++ b/Minecraft.World/Rabbit.cpp @@ -70,6 +70,7 @@ bool Rabbit::useNewAi() { return true; } +/* void Rabbit::dropDeathLoot(bool wasKilledByPlayer, int lootingLevel) { int meatCount = random->nextInt(2) + random->nextInt(lootingLevel + 1); @@ -86,6 +87,7 @@ void Rabbit::dropDeathLoot(bool wasKilledByPlayer, int lootingLevel) { spawnAtLocation(Item::rabbit_foot_Id, 1); } } +*/ void Rabbit::tick() { Animal::tick(); diff --git a/Minecraft.World/Rabbit.h b/Minecraft.World/Rabbit.h index f273916f..8d55a2c8 100644 --- a/Minecraft.World/Rabbit.h +++ b/Minecraft.World/Rabbit.h @@ -47,7 +47,7 @@ protected: virtual int getHopSound() ; virtual float getSoundVolume() override { return 0.6f; } virtual bool makeStepSound() override { return true; } - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) override; + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) override; public: virtual bool isFood(shared_ptr item) override; diff --git a/Minecraft.World/ScatteredFeaturePieces.cpp b/Minecraft.World/ScatteredFeaturePieces.cpp index 6b955eae..7a1716a7 100644 --- a/Minecraft.World/ScatteredFeaturePieces.cpp +++ b/Minecraft.World/ScatteredFeaturePieces.cpp @@ -7,6 +7,7 @@ #include "net.minecraft.world.level.tile.h" #include "net.minecraft.world.level.levelgen.structure.h" #include "WeighedTreasure.h" +#include "LootTableManager.h" #include "ScatteredFeaturePieces.h" void ScatteredFeaturePieces::loadStatic() @@ -102,22 +103,6 @@ bool ScatteredFeaturePieces::ScatteredFeaturePiece::updateAverageGroundHeight(Le return true; } -WeighedTreasure *ScatteredFeaturePieces::DesertPyramidPiece::treasureItems[ScatteredFeaturePieces::DesertPyramidPiece::TREASURE_ITEMS_COUNT] = -{ - new WeighedTreasure(Item::diamond_Id, 0, 1, 3, 3), - new WeighedTreasure(Item::iron_ingot_Id, 0, 1, 5, 10), - new WeighedTreasure(Item::gold_ingot_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), - // very rare for pyramids ... - new WeighedTreasure(Item::saddle_Id, 0, 1, 1, 3), - new WeighedTreasure(Item::iron_horse_armor_Id, 0, 1, 1, 1), - new WeighedTreasure(Item::golden_horse_armor_Id, 0, 1, 1, 1), - new WeighedTreasure(Item::diamond_horse_armor_Id, 0, 1, 1, 1), - // ... -}; - ScatteredFeaturePieces::DesertPyramidPiece::DesertPyramidPiece() { hasPlacedChest[0] = false; @@ -360,36 +345,15 @@ bool ScatteredFeaturePieces::DesertPyramidPiece::postProcess(Level *level, Rando { 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::enchanted_book->createForRandomTreasure(random)), 2 + random->nextInt(5)); + hasPlacedChest[i] = createChest(level, chunkBB, random, 10 + xo, -11, 10 + zo, + LootTableManager::Get().ResolveDrops("chests/desert_pyramid", false, 0, + [random](int bound) { return random->nextInt(bound); })); } } return true; } -WeighedTreasure *ScatteredFeaturePieces::JunglePyramidPiece::treasureItems[ScatteredFeaturePieces::JunglePyramidPiece::TREASURE_ITEMS_COUNT] = -{ - new WeighedTreasure(Item::diamond_Id, 0, 1, 3, 3), - new WeighedTreasure(Item::iron_ingot_Id, 0, 1, 5, 10), - new WeighedTreasure(Item::gold_ingot_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), - // very rare for pyramids ... - new WeighedTreasure(Item::saddle_Id, 0, 1, 1, 3), - new WeighedTreasure(Item::iron_horse_armor_Id, 0, 1, 1, 1), - new WeighedTreasure(Item::golden_horse_armor_Id, 0, 1, 1, 1), - new WeighedTreasure(Item::diamond_horse_armor_Id, 0, 1, 1, 1), - // ... -}; - - -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() { // for reflection @@ -566,7 +530,9 @@ bool ScatteredFeaturePieces::JunglePyramidPiece::postProcess(Level *level, Rando placeBlock(level, Tile::mossy_cobblestone_Id, 0, 3, -3, 1, chunkBB); if (!placedTrap1) { - placedTrap1 = createDispenser(level, chunkBB, random, 3, -2, 1, Facing::NORTH, WeighedTreasureArray(dispenserItems,DISPENSER_ITEMS_COUNT), 2); + placedTrap1 = createDispenser(level, chunkBB, random, 3, -2, 1, Facing::NORTH, + LootTableManager::Get().ResolveDrops("chests/jungle_temple_dispenser", false, 0, + [random](int bound) { return random->nextInt(bound); })); } placeBlock(level, Tile::vine_Id, 0xf, 3, -2, 2, chunkBB); @@ -583,13 +549,17 @@ bool ScatteredFeaturePieces::JunglePyramidPiece::postProcess(Level *level, Rando placeBlock(level, Tile::redstone_wire_Id, 0, 9, -2, 4, chunkBB); if (!placedTrap2) { - placedTrap2 = createDispenser(level, chunkBB, random, 9, -2, 3, Facing::WEST, WeighedTreasureArray(dispenserItems,DISPENSER_ITEMS_COUNT), 2); + placedTrap2 = createDispenser(level, chunkBB, random, 9, -2, 3, Facing::WEST, + LootTableManager::Get().ResolveDrops("chests/jungle_temple_dispenser", false, 0, + [random](int bound) { return random->nextInt(bound); })); } 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::enchanted_book->createForRandomTreasure(random)), 2 + random->nextInt(5)); + placedMainChest = createChest(level, chunkBB, random, 8, -3, 3, + LootTableManager::Get().ResolveDrops("chests/jungle_temple", false, 0, + [random](int bound) { return random->nextInt(bound); })); } placeBlock(level, Tile::mossy_cobblestone_Id, 0, 9, -3, 2, chunkBB); placeBlock(level, Tile::mossy_cobblestone_Id, 0, 8, -3, 1, chunkBB); @@ -622,7 +592,9 @@ bool ScatteredFeaturePieces::JunglePyramidPiece::postProcess(Level *level, Rando placeBlock(level, Tile::unpowered_repeater_Id, getOrientationData(Tile::unpowered_repeater_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::enchanted_book->createForRandomTreasure(random)), 2 + random->nextInt(5)); + placedHiddenChest = createChest(level, chunkBB, random, 9, -3, 10, + LootTableManager::Get().ResolveDrops("chests/jungle_temple", false, 0, + [random](int bound) { return random->nextInt(bound); })); } return true; diff --git a/Minecraft.World/ScatteredFeaturePieces.h b/Minecraft.World/ScatteredFeaturePieces.h index 5fb334df..8568ea1c 100644 --- a/Minecraft.World/ScatteredFeaturePieces.h +++ b/Minecraft.World/ScatteredFeaturePieces.h @@ -32,11 +32,8 @@ public: static StructurePiece *Create() { return new DesertPyramidPiece(); } virtual EStructurePiece GetType() { return eStructurePiece_DesertPyramidPiece; } - public: - static const int TREASURE_ITEMS_COUNT = 10; private: bool hasPlacedChest[4]; - static WeighedTreasure *treasureItems[TREASURE_ITEMS_COUNT]; public: DesertPyramidPiece(); @@ -56,18 +53,12 @@ public: static StructurePiece *Create() { return new JunglePyramidPiece(); } virtual EStructurePiece GetType() { return eStructurePiece_JunglePyramidPiece; } - public: - static const int TREASURE_ITEMS_COUNT = 10; - 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(); JunglePyramidPiece(Random *random, int west, int north); diff --git a/Minecraft.World/Sheep.cpp b/Minecraft.World/Sheep.cpp index 3a9ae6a5..8fe23825 100644 --- a/Minecraft.World/Sheep.cpp +++ b/Minecraft.World/Sheep.cpp @@ -19,6 +19,7 @@ #include "../Minecraft.Client/Textures.h" #include "MobCategory.h" #include "GenericStats.h" +#include "ColoredTile.h" const float Sheep::COLOR[Sheep::COLOR_LENGTH][3] = { @@ -105,6 +106,37 @@ void Sheep::defineSynchedData() void Sheep::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { + const int colorData = getColor(); + const int dyeAuxValue = ColoredTile::getItemAuxValueForTileData(colorData); + const std::wstring colorName = DyePowderItem::COLOR_TEXTURES[dyeAuxValue]; + const std::string tableName = "entities/sheep/" + std::string(colorName.begin(), colorName.end()) ; + + LootTableManager &lootTables = LootTableManager::Get(); + if (!lootTables.HasLoadedTables()) + { + lootTables.LoadFromDisk(); + } + const LootTableDefinition *table = lootTables.GetTable(tableName); + if (table != nullptr) + { + const std::vector drops = lootTables.ResolveDrops( + tableName, + wasKilledByPlayer, + playerBonusLevel, + [this](int maxExclusive) -> int { return random->nextInt(maxExclusive); }, + isOnFire()); + if (!drops.empty()) + { + for (std::vector::const_iterator it = drops.begin(); it != drops.end(); ++it) + { + shared_ptr item = std::make_shared(it->itemId, it->count, it->data); + spawnAtLocation(item, 0.0f); + } + return; + } + } + + /* if(!isSheared()) { // killing a non-sheared sheep will drop a single block of cloth @@ -124,6 +156,7 @@ void Sheep::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) spawnAtLocation(Item::mutton_Id, 1); } } + */ } int Sheep::getDeathLoot() diff --git a/Minecraft.World/Skeleton.cpp b/Minecraft.World/Skeleton.cpp index d152a8cd..2a73e01a 100644 --- a/Minecraft.World/Skeleton.cpp +++ b/Minecraft.World/Skeleton.cpp @@ -188,29 +188,15 @@ void Skeleton::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { if (getSkeletonType() == TYPE_WITHER) { - // drop some coal + // wither skeletons use hardcoded coal drops int count = random->nextInt(3 + playerBonusLevel) - 1; for (int i = 0; i < count; i++) { spawnAtLocation(Item::coal_Id, 1); } } - else - { - // drop some arrows - int count = random->nextInt(3 + playerBonusLevel); - for (int i = 0; i < count; i++) - { - spawnAtLocation(Item::arrow_Id, 1); - } - } - // and some bones - int count = random->nextInt(3 + playerBonusLevel); - for (int i = 0; i < count; i++) - { - spawnAtLocation(Item::bone->id, 1); - } + Monster::dropDeathLoot(wasKilledByPlayer, playerBonusLevel); } void Skeleton::dropRareDeathLoot(int rareLootLevel) diff --git a/Minecraft.World/Slime.cpp b/Minecraft.World/Slime.cpp index 54043995..5679a0d6 100644 --- a/Minecraft.World/Slime.cpp +++ b/Minecraft.World/Slime.cpp @@ -18,6 +18,15 @@ +void Slime::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) +{ + // only drop loot if size > 1 (large slimes) + if (getSize() == 1) + { + Mob::dropDeathLoot(wasKilledByPlayer, playerBonusLevel); + } +} + void Slime::_init() { jumpDelay = 0; diff --git a/Minecraft.World/Slime.h b/Minecraft.World/Slime.h index 95a1aef1..f4e7c122 100644 --- a/Minecraft.World/Slime.h +++ b/Minecraft.World/Slime.h @@ -61,6 +61,7 @@ protected: virtual int getHurtSound(); virtual int getDeathSound(); virtual int getDeathLoot(); + virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); public: virtual bool canSpawn(); diff --git a/Minecraft.World/SnowMan.cpp b/Minecraft.World/SnowMan.cpp index c166bdde..5b178676 100644 --- a/Minecraft.World/SnowMan.cpp +++ b/Minecraft.World/SnowMan.cpp @@ -89,14 +89,16 @@ int SnowMan::getDeathLoot() } +/* void SnowMan::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { - // drop some feathers + // drop some snowballs int count = random->nextInt(16); for (int i = 0; i < count; i++) { spawnAtLocation(Item::snowball_Id, 1); } } +*/ void SnowMan::performRangedAttack(shared_ptr target, float power) { diff --git a/Minecraft.World/SnowMan.h b/Minecraft.World/SnowMan.h index 52dbb52f..ff473432 100644 --- a/Minecraft.World/SnowMan.h +++ b/Minecraft.World/SnowMan.h @@ -21,7 +21,7 @@ public: protected: virtual int getDeathLoot(); - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); public: virtual void performRangedAttack(shared_ptr target, float power); diff --git a/Minecraft.World/Spider.cpp b/Minecraft.World/Spider.cpp index 0ad2bec8..4eec0b98 100644 --- a/Minecraft.World/Spider.cpp +++ b/Minecraft.World/Spider.cpp @@ -126,6 +126,7 @@ int Spider::getDeathLoot() return Item::string->id; } +/* void Spider::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { Monster::dropDeathLoot(wasKilledByPlayer, playerBonusLevel); @@ -135,6 +136,7 @@ void Spider::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) spawnAtLocation(Item::spider_eye_Id, 1); } } +*/ /** * The the spiders act as if they're always on a ladder, which enables them diff --git a/Minecraft.World/Spider.h b/Minecraft.World/Spider.h index 941ac45a..a8b22bfb 100644 --- a/Minecraft.World/Spider.h +++ b/Minecraft.World/Spider.h @@ -31,7 +31,7 @@ protected: virtual void playStepSound(int xt, int yt, int zt, int t); virtual void checkHurtTarget(shared_ptr target, float d); virtual int getDeathLoot(); - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); public: virtual bool onLadder(); diff --git a/Minecraft.World/Squid.cpp b/Minecraft.World/Squid.cpp index 57d43347..fcfbe6cb 100644 --- a/Minecraft.World/Squid.cpp +++ b/Minecraft.World/Squid.cpp @@ -76,6 +76,7 @@ bool Squid::makeStepSound() return false; } +/* void Squid::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { int count = random->nextInt(3 + playerBonusLevel) + 1; @@ -84,6 +85,7 @@ void Squid::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) spawnAtLocation(std::make_shared(Item::dye, 1, DyePowderItem::BLACK), 0.0f); } } +*/ bool Squid::isInWater() { diff --git a/Minecraft.World/Squid.h b/Minecraft.World/Squid.h index c645d9f8..9ec9dfbf 100644 --- a/Minecraft.World/Squid.h +++ b/Minecraft.World/Squid.h @@ -38,7 +38,7 @@ protected: virtual float getSoundVolume(); virtual int getDeathLoot(); virtual bool makeStepSound(); - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); public: virtual bool isInWater(); diff --git a/Minecraft.World/StringHelpers.cpp b/Minecraft.World/StringHelpers.cpp index 9bd84660..c7eaf4a1 100644 --- a/Minecraft.World/StringHelpers.cpp +++ b/Minecraft.World/StringHelpers.cpp @@ -70,8 +70,9 @@ wstring convStringToWstring(const string& converting) // 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); + static std::string buf; + buf.clear(); + buf.reserve(name.length()); for(unsigned int i = 0; i < name.length(); i++ ) { wchar_t c = name[i]; @@ -82,23 +83,23 @@ const char *wstringtofilename(const wstring& name) #endif assert(c<128); // Will we have to do any conversion of non-ASCII characters in filenames? buf[i] = static_cast(c); + buf.push_back(static_cast(c)); } - buf[name.length()] = 0; - return buf; + return buf.c_str(); } const char *wstringtochararray(const wstring& name) { - static char buf[256]; - assert(name.length()<256); + static std::string buf; + buf.clear(); + buf.reserve(name.length()); for(unsigned int i = 0; i < name.length(); i++ ) { wchar_t c = name[i]; assert(c<128); // Will we have to do any conversion of non-ASCII characters in filenames? - buf[i] = static_cast(c); + buf.push_back(static_cast(c)); } - buf[name.length()] = 0; - return buf; + return buf.c_str(); } wstring filenametowstring(const char *name) diff --git a/Minecraft.World/StrongholdPieces.cpp b/Minecraft.World/StrongholdPieces.cpp index eb454b6c..9ee488fb 100644 --- a/Minecraft.World/StrongholdPieces.cpp +++ b/Minecraft.World/StrongholdPieces.cpp @@ -8,6 +8,7 @@ #include "net.minecraft.world.level.levelgen.structure.h" #include "net.minecraft.world.item.h" #include "WeighedTreasure.h" +#include "LootTableManager.h" #include "FileHeader.h" #include "Facing.h" @@ -715,30 +716,6 @@ bool StrongholdPieces::Straight::postProcess(Level *level, Random *random, Bound return true; } -WeighedTreasure *StrongholdPieces::ChestCorridor::treasureItems[TREASURE_ITEMS_COUNT] = -{ - new WeighedTreasure(Item::ender_pearl_Id, 0, 1, 1, 10), - new WeighedTreasure(Item::diamond_Id, 0, 1, 3, 3), - new WeighedTreasure(Item::iron_ingot_Id, 0, 1, 5, 10), - new WeighedTreasure(Item::gold_ingot_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::iron_pickaxe_Id, 0, 1, 1, 5), - new WeighedTreasure(Item::iron_sword_Id, 0, 1, 1, 5), - new WeighedTreasure(Item::iron_chestplate_Id, 0, 1, 1, 5), - new WeighedTreasure(Item::iron_helmet_Id, 0, 1, 1, 5), - new WeighedTreasure(Item::iron_leggings_Id, 0, 1, 1, 5), - new WeighedTreasure(Item::iron_boots_Id, 0, 1, 1, 5), - new WeighedTreasure(Item::golden_apple_Id, 0, 1, 1, 1), - // very rare for strongholds ... - new WeighedTreasure(Item::saddle_Id, 0, 1, 1, 1), - new WeighedTreasure(Item::iron_horse_armor_Id, 0, 1, 1, 1), - new WeighedTreasure(Item::golden_horse_armor_Id, 0, 1, 1, 1), - new WeighedTreasure(Item::diamond_horse_armor_Id, 0, 1, 1, 1), - // ... -}; - StrongholdPieces::ChestCorridor::ChestCorridor() { // for reflection @@ -816,7 +793,9 @@ bool StrongholdPieces::ChestCorridor::postProcess(Level *level, Random *random, if (chunkBB->isInside(x, y, z)) { hasPlacedChest = true; - createChest(level, chunkBB, random, 3, 2, 3, WeighedTreasure::addToTreasure(WeighedTreasureArray(treasureItems,TREASURE_ITEMS_COUNT), Item::enchanted_book->createForRandomTreasure(random)), 2 + random->nextInt(2)); + createChest(level, chunkBB, random, 3, 2, 3, + LootTableManager::Get().ResolveDrops("chests/stronghold_corridor", false, 0, + [random](int bound) { return random->nextInt(bound); })); } } @@ -1044,17 +1023,6 @@ StrongholdPieces::RoomCrossing *StrongholdPieces::RoomCrossing::createPiece(list return new RoomCrossing(genDepth, random, box, direction); } -WeighedTreasure *StrongholdPieces::RoomCrossing::smallTreasureItems[SMALL_TREASURE_ITEMS_COUNT] = -{ - new WeighedTreasure(Item::iron_ingot_Id, 0, 1, 5, 10), - new WeighedTreasure(Item::gold_ingot_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::iron_pickaxe_Id, 0, 1, 1, 1), -}; - bool StrongholdPieces::RoomCrossing::postProcess(Level *level, Random *random, BoundingBox *chunkBB) { if (edgesLiquid(level, chunkBB)) @@ -1153,7 +1121,9 @@ bool StrongholdPieces::RoomCrossing::postProcess(Level *level, Random *random, B 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::enchanted_book->createForRandomTreasure(random)), 1 + random->nextInt(4)); + createChest(level, chunkBB, random, 3, 4, 8, + LootTableManager::Get().ResolveDrops("chests/stronghold_crossing", false, 0, + [random](int bound) { return random->nextInt(bound); })); // System.out.println("Created chest at " + getWorldX(3, 8) + // "," + getWorldY(4) + "," + getWorldZ(3, 8)); @@ -1282,14 +1252,6 @@ StrongholdPieces::Library *StrongholdPieces::Library::createPiece(listcreateForRandomTreasure(random, 1, 5, 2)), 1 + random->nextInt(4)); + createChest(level, chunkBB, random, 3, 3, 5, + LootTableManager::Get().ResolveDrops("chests/stronghold_library", false, 0, + [random](int bound) { return random->nextInt(bound); })); 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::enchanted_book->createForRandomTreasure(random, 1, 5, 2)), 1 + random->nextInt(4)); + createChest(level, chunkBB, random, width - 2, tallHeight - 3, 1, + LootTableManager::Get().ResolveDrops("chests/stronghold_library", false, 0, + [random](int bound) { return random->nextInt(bound); })); } return true; diff --git a/Minecraft.World/StrongholdPieces.h b/Minecraft.World/StrongholdPieces.h index 2b15f3c2..11df9356 100644 --- a/Minecraft.World/StrongholdPieces.h +++ b/Minecraft.World/StrongholdPieces.h @@ -243,8 +243,6 @@ public: static const int width = 5; static const int height = 5; static const int depth = 7; - static const int TREASURE_ITEMS_COUNT = 18; - static WeighedTreasure *treasureItems[TREASURE_ITEMS_COUNT]; bool hasPlacedChest; @@ -340,10 +338,6 @@ public: static StructurePiece *Create() { return new RoomCrossing(); } virtual EStructurePiece GetType() { return eStructurePiece_StrongholdRoomCrossing; } - 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; @@ -401,10 +395,6 @@ public: static StructurePiece *Create() { return new Library(); } virtual EStructurePiece GetType() { return eStructurePiece_Library; } - 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; diff --git a/Minecraft.World/StructurePiece.cpp b/Minecraft.World/StructurePiece.cpp index d50b951f..d46fd9f0 100644 --- a/Minecraft.World/StructurePiece.cpp +++ b/Minecraft.World/StructurePiece.cpp @@ -803,43 +803,50 @@ void StructurePiece::fillColumnDown( Level* level, int tile, int tileData, int x } } -bool StructurePiece::createChest( Level* level, BoundingBox* chunkBB, Random* random, int x, int y, int z, - WeighedTreasureArray treasure, int numRolls ) +bool StructurePiece::createChest(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, + const std::vector &drops) { - int worldX = getWorldX( x, z ); - int worldY = getWorldY( y ); - int worldZ = getWorldZ( x, z ); + 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->setTileAndData( worldX, worldY, worldZ, Tile::chest->id, 0, Tile::UPDATE_CLIENTS ); - shared_ptr chest = dynamic_pointer_cast(level->getTileEntity( worldX, worldY, worldZ )); - if ( chest != nullptr ) WeighedTreasure::addChestItems( random, treasure, chest, numRolls ); - return true; - } - } - return false; + if (chunkBB->isInside(worldX, worldY, worldZ)) + { + if (level->getTile(worldX, worldY, worldZ) != Tile::chest->id) + { + level->setTileAndData(worldX, worldY, worldZ, Tile::chest->id, 0, Tile::UPDATE_CLIENTS); + shared_ptr chest = dynamic_pointer_cast(level->getTileEntity(worldX, worldY, worldZ)); + if (chest != nullptr) + { + WeighedTreasure::addChestItems(random, drops, chest); + } + 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) +bool StructurePiece::createDispenser(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, int facing, + const std::vector &drops) { - int worldX = getWorldX(x, z); - int worldY = getWorldY(y); - int worldZ = getWorldZ(x, z); + 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), Tile::UPDATE_CLIENTS); - shared_ptr dispenser = dynamic_pointer_cast(level->getTileEntity(worldX, worldY, worldZ)); - if (dispenser != nullptr) WeighedTreasure::addDispenserItems(random, items, dispenser, numRolls); - return true; - } - } - return false; + 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), Tile::UPDATE_CLIENTS); + shared_ptr dispenser = dynamic_pointer_cast(level->getTileEntity(worldX, worldY, worldZ)); + if (dispenser != nullptr) + { + WeighedTreasure::addDispenserItems(random, drops, dispenser); + } + return true; + } + } + return false; } void StructurePiece::createDoor( Level* level, BoundingBox* chunkBB, Random* random, int x, int y, int z, diff --git a/Minecraft.World/StructurePiece.h b/Minecraft.World/StructurePiece.h index 2d5767f9..518327bc 100644 --- a/Minecraft.World/StructurePiece.h +++ b/Minecraft.World/StructurePiece.h @@ -2,6 +2,9 @@ #include "WeighedRandom.h" #include "BoundingBox.h" #include "StructureFeatureIO.h" +#include "LootTableManager.h" + +#include class Level; class Random; @@ -130,8 +133,9 @@ public: 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); + + virtual bool createChest(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, const std::vector &drops); + virtual bool createDispenser(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, int facing, const std::vector &drops); protected: void createDoor(Level *level, BoundingBox *chunkBB, Random *random, int x, int y, int z, int orientation); diff --git a/Minecraft.World/VillagePieces.cpp b/Minecraft.World/VillagePieces.cpp index ece08934..641c8a68 100644 --- a/Minecraft.World/VillagePieces.cpp +++ b/Minecraft.World/VillagePieces.cpp @@ -9,6 +9,7 @@ #include "net.minecraft.world.level.dimension.h" #include "net.minecraft.world.entity.npc.h" #include "WeighedTreasure.h" +#include "LootTableManager.h" #include "VillagePieces.h" #include "VillageFeature.h" #include "Direction.h" @@ -1734,7 +1735,9 @@ bool VillagePieces::Smithy::postProcess(Level *level, Random *random, BoundingBo if (chunkBB->isInside(x, y, z)) { hasPlacedChest = true; - createChest(level, chunkBB, random, 5, 1, 5, treasureItems, 3 + random->nextInt(6)); + createChest(level, chunkBB, random, 5, 1, 5, + LootTableManager::Get().ResolveDrops("chests/village_blacksmith", false, 0, + [random](int bound) { return random->nextInt(bound); })); } } diff --git a/Minecraft.World/VillagerGolem.cpp b/Minecraft.World/VillagerGolem.cpp index e07237fa..c1af828a 100644 --- a/Minecraft.World/VillagerGolem.cpp +++ b/Minecraft.World/VillagerGolem.cpp @@ -205,6 +205,7 @@ void VillagerGolem::playStepSound(int xt, int yt, int zt, int t) playSound(eSoundType_MOB_IRONGOLEM_WALK, 1, 1); } +/* void VillagerGolem::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { int roses = random->nextInt(3); @@ -217,7 +218,7 @@ void VillagerGolem::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { spawnAtLocation(Item::iron_ingot_Id, 1); } -} +}*/ int VillagerGolem::getOfferFlowerTick() { diff --git a/Minecraft.World/VillagerGolem.h b/Minecraft.World/VillagerGolem.h index 55b6cae2..58662b0e 100644 --- a/Minecraft.World/VillagerGolem.h +++ b/Minecraft.World/VillagerGolem.h @@ -51,7 +51,7 @@ protected: virtual int getHurtSound(); virtual int getDeathSound(); virtual void playStepSound(int xt, int yt, int zt, int t); - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); public: virtual int getOfferFlowerTick(); diff --git a/Minecraft.World/WeighedTreasure.cpp b/Minecraft.World/WeighedTreasure.cpp index 3c20ae66..815b9f0d 100644 --- a/Minecraft.World/WeighedTreasure.cpp +++ b/Minecraft.World/WeighedTreasure.cpp @@ -1,86 +1,81 @@ #include "stdafx.h" #include "net.minecraft.world.level.tile.entity.h" #include "net.minecraft.world.item.h" +#include "LootTableManager.h" #include "WeighedRandom.h" #include "WeighedTreasure.h" +#include + +namespace +{ + template + void FillContainerFromDrops(Random *random, const std::vector &drops, shared_ptr dest) + { + for (const LootTableDropResult &drop : drops) + { + if (drop.itemId == 0 || drop.count <= 0) + { + continue; + } + + shared_ptr item = std::make_shared(drop.itemId, 1, drop.data); + + if (item->getMaxStackSize() >= drop.count) + { + shared_ptr copy = item->copy(); + copy->count = drop.count; + dest->setItem(random->nextInt(dest->getContainerSize()), copy); + } + else + { + // use multiple slots if possible + for (int c = 0; c < drop.count; c++) + { + shared_ptr copy = item->copy(); + copy->count = 1; + dest->setItem(random->nextInt(dest->getContainerSize()), copy); + } + } + } + } +} + WeighedTreasure::WeighedTreasure(int itemId, int auxValue, int minCount, int maxCount, int weight) : WeighedRandomItem(weight) { - this->item = std::make_shared(itemId, 1, auxValue); - this->minCount = minCount; - this->maxCount = maxCount; + this->item = std::make_shared(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; + this->item = item; + this->minCount = minCount; + this->maxCount = maxCount; } -void WeighedTreasure::addChestItems(Random *random, WeighedTreasureArray items, shared_ptr dest, int numRolls) +void WeighedTreasure::addChestItems(Random *random, const std::vector &drops, shared_ptr dest) { - for (int r = 0; r < numRolls; r++) - { - WeighedTreasure *treasure = static_cast(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); - } - } - } + FillContainerFromDrops(random, drops, dest); } -void WeighedTreasure::addDispenserItems(Random *random, WeighedTreasureArray items, shared_ptr dest, int numRolls) +void WeighedTreasure::addDispenserItems(Random *random, const std::vector &drops, shared_ptr dest) { - for (int r = 0; r < numRolls; r++) - { - WeighedTreasure *treasure = static_cast(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); - } - } - } + FillContainerFromDrops(random, drops, dest); } WeighedTreasureArray WeighedTreasure::addToTreasure(WeighedTreasureArray items, WeighedTreasure *extra) { - WeighedTreasureArray result(items.length + 1); - int i = 0; + WeighedTreasureArray result(items.length + 1); + int i = 0; - for (int j = 0; j < items.length; j++) - { - result[i++] = items[j]; - } + for (int j = 0; j < items.length; j++) + { + result[i++] = items[j]; + } - result[i++] = extra; + result[i++] = extra; - return result; + return result; } \ No newline at end of file diff --git a/Minecraft.World/WeighedTreasure.h b/Minecraft.World/WeighedTreasure.h index c97def3d..508b3dd9 100644 --- a/Minecraft.World/WeighedTreasure.h +++ b/Minecraft.World/WeighedTreasure.h @@ -1,6 +1,8 @@ #pragma once +#include #include "WeighedRandom.h" +#include "LootTableManager.h" class WeighedTreasure : public WeighedRandomItem { @@ -13,7 +15,7 @@ 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 void addChestItems(Random *random, const std::vector &drops, shared_ptr dest); + static void addDispenserItems(Random *random, const std::vector &drops, shared_ptr dest); static WeighedTreasureArray addToTreasure(WeighedTreasureArray items, WeighedTreasure *extra); }; \ No newline at end of file diff --git a/Minecraft.World/Witch.cpp b/Minecraft.World/Witch.cpp index 344be228..053b8023 100644 --- a/Minecraft.World/Witch.cpp +++ b/Minecraft.World/Witch.cpp @@ -178,21 +178,13 @@ float Witch::getDamageAfterMagicAbsorb(DamageSource *damageSource, float damage) return damage; } +/* void Witch::dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel) { - int passes = random->nextInt(3) + 1; - for (int pass = 0; pass < passes; pass++) - { - int count = random->nextInt(3); - int type = DEATH_LOOT[random->nextInt(DEATH_LOOT_COUNT)]; - if (playerBonusLevel > 0) count += random->nextInt(playerBonusLevel + 1); - - for (int i = 0; i < count; i++) - { - spawnAtLocation(type, 1); - } - } + // use loot table system instead of dropping entire inventory + Monster::dropDeathLoot(wasKilledByPlayer, playerBonusLevel); } +*/ void Witch::performRangedAttack(shared_ptr target, float power) { diff --git a/Minecraft.World/Witch.h b/Minecraft.World/Witch.h index 256f358f..ba7b417f 100644 --- a/Minecraft.World/Witch.h +++ b/Minecraft.World/Witch.h @@ -41,7 +41,7 @@ public: protected: virtual float getDamageAfterMagicAbsorb(DamageSource *damageSource, float damage); - virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); + // virtual void dropDeathLoot(bool wasKilledByPlayer, int playerBonusLevel); public: virtual void performRangedAttack(shared_ptr target, float power);