├── gradlew ├── logo-banner.png ├── src ├── generated │ └── resources │ │ ├── assets │ │ └── dragonmounts │ │ │ └── models │ │ │ └── entity │ │ │ └── dragon │ │ │ └── breed │ │ │ └── properties │ │ │ ├── ghost.json │ │ │ ├── water.json │ │ │ └── fire.json │ │ ├── data │ │ ├── dragonmounts │ │ │ ├── tags │ │ │ │ └── block │ │ │ │ │ ├── ice_dragon_habitat_blocks.json │ │ │ │ │ ├── reaper_transform.json │ │ │ │ │ ├── fire_dragon_habitat_blocks.json │ │ │ │ │ ├── forest_dragon_habitat_blocks.json │ │ │ │ │ ├── reaper_plant_destruction.json │ │ │ │ │ ├── water_dragon_habitat_blocks.json │ │ │ │ │ ├── reaper_plant_death.json │ │ │ │ │ ├── hot_feet_burnables.json │ │ │ │ │ └── nether_dragon_habitat_blocks.json │ │ │ ├── loot_modifiers │ │ │ │ └── dragonmounts │ │ │ │ │ ├── ice │ │ │ │ │ └── chests │ │ │ │ │ │ └── igloo_chest.json │ │ │ │ │ ├── fire │ │ │ │ │ └── chests │ │ │ │ │ │ └── desert_pyramid.json │ │ │ │ │ ├── forest │ │ │ │ │ └── chests │ │ │ │ │ │ └── jungle_temple.json │ │ │ │ │ ├── aether │ │ │ │ │ └── chests │ │ │ │ │ │ └── simple_dungeon.json │ │ │ │ │ ├── water │ │ │ │ │ └── chests │ │ │ │ │ │ └── buried_treasure.json │ │ │ │ │ ├── ghost │ │ │ │ │ └── chests │ │ │ │ │ │ ├── woodland_mansion.json │ │ │ │ │ │ └── abandoned_mineshaft.json │ │ │ │ │ └── nether │ │ │ │ │ └── chests │ │ │ │ │ └── bastion_treasure.json │ │ │ ├── dragonmounts │ │ │ │ └── dragon_breeds │ │ │ │ │ ├── aether.json │ │ │ │ │ ├── end.json │ │ │ │ │ ├── forest.json │ │ │ │ │ ├── ice.json │ │ │ │ │ ├── water.json │ │ │ │ │ ├── nether.json │ │ │ │ │ ├── fire.json │ │ │ │ │ └── ghost.json │ │ │ └── loot_table │ │ │ │ └── blocks │ │ │ │ └── dragon_egg.json │ │ └── forge │ │ │ └── loot_modifiers │ │ │ └── global_loot_modifiers.json │ │ └── .cache │ │ ├── 59eb3dbb5f86130e09b3c62d89b9525ee01cf52d │ │ ├── c31184ad3d9826d5a5f417546a2ca23fe387ca08 │ │ ├── 103d9f3f36b01595f1aa5172191e60eff02e6924 │ │ ├── 93b1357e1c4c69ccdbad96ca186d77c71b1b8ae5 │ │ └── 61b45c1b2a664e662355af098e0fe46142bb878b └── main │ ├── resources │ ├── assets │ │ └── dragonmounts │ │ │ ├── models │ │ │ ├── block │ │ │ │ ├── dragon_egg_models.json │ │ │ │ └── dragon_eggs │ │ │ │ │ ├── end_dragon_egg.json │ │ │ │ │ ├── ice_dragon_egg.json │ │ │ │ │ ├── fire_dragon_egg.json │ │ │ │ │ ├── ghost_dragon_egg.json │ │ │ │ │ ├── water_dragon_egg.json │ │ │ │ │ ├── aether_dragon_egg.json │ │ │ │ │ ├── forest_dragon_egg.json │ │ │ │ │ └── nether_dragon_egg.json │ │ │ └── item │ │ │ │ ├── dragon_egg.json │ │ │ │ └── spawn_egg.json │ │ │ ├── blockstates │ │ │ └── dragon_egg.json │ │ │ ├── sounds │ │ │ └── entity │ │ │ │ └── dragon │ │ │ │ ├── death.ogg │ │ │ │ ├── step1.ogg │ │ │ │ ├── step2.ogg │ │ │ │ ├── step3.ogg │ │ │ │ ├── step4.ogg │ │ │ │ ├── breathe1.ogg │ │ │ │ └── breathe2.ogg │ │ │ ├── textures │ │ │ ├── block │ │ │ │ ├── fire_dragon_egg.png │ │ │ │ ├── ghost_dragon_egg.png │ │ │ │ ├── ice_dragon_egg.png │ │ │ │ ├── water_dragon_egg.png │ │ │ │ ├── aether_dragon_egg.png │ │ │ │ ├── forest_dragon_egg.png │ │ │ │ ├── nether_dragon_egg.png │ │ │ │ ├── fire_dragon_egg.png.mcmeta │ │ │ │ ├── forest_dragon_egg.png.mcmeta │ │ │ │ ├── ghost_dragon_egg.png.mcmeta │ │ │ │ ├── nether_dragon_egg.png.mcmeta │ │ │ │ ├── water_dragon_egg.png.mcmeta │ │ │ │ ├── ice_dragon_egg.png.mcmeta │ │ │ │ └── aether_dragon_egg.png.mcmeta │ │ │ └── entity │ │ │ │ └── dragon │ │ │ │ ├── dissolve.png │ │ │ │ ├── end │ │ │ │ ├── body.png │ │ │ │ ├── glow.png │ │ │ │ └── saddle.png │ │ │ │ ├── ice │ │ │ │ ├── body.png │ │ │ │ ├── glow.png │ │ │ │ └── saddle.png │ │ │ │ ├── fire │ │ │ │ ├── body.png │ │ │ │ ├── glow.png │ │ │ │ └── saddle.png │ │ │ │ ├── ghost │ │ │ │ ├── body.png │ │ │ │ ├── glow.png │ │ │ │ └── saddle.png │ │ │ │ ├── water │ │ │ │ ├── body.png │ │ │ │ ├── glow.png │ │ │ │ └── saddle.png │ │ │ │ ├── aether │ │ │ │ ├── body.png │ │ │ │ ├── glow.png │ │ │ │ └── saddle.png │ │ │ │ ├── forest │ │ │ │ ├── body.png │ │ │ │ ├── glow.png │ │ │ │ └── saddle.png │ │ │ │ └── nether │ │ │ │ ├── body.png │ │ │ │ ├── glow.png │ │ │ │ └── saddle.png │ │ │ ├── sounds.json │ │ │ └── lang │ │ │ ├── ja_jp.json │ │ │ ├── en_us.json │ │ │ └── uk_ua.json │ ├── pack.mcmeta │ ├── META-INF │ │ ├── accesstransformer.cfg │ │ └── mods.toml │ └── dragonmounts.mixins.json │ └── java │ └── com │ └── github │ └── kay9 │ └── dragonmounts │ ├── accessors │ └── ModelPartAccess.java │ ├── util │ ├── DMLUtil.java │ ├── CircularBuffer.java │ └── LerpedFloat.java │ ├── dragon │ ├── ai │ │ ├── DragonBodyController.java │ │ ├── DragonMoveController.java │ │ ├── DragonBreedGoal.java │ │ └── DragonFollowOwnerGoal.java │ ├── egg │ │ └── habitats │ │ │ ├── DragonBreathHabitat.java │ │ │ ├── HeightHabitat.java │ │ │ ├── PickyHabitat.java │ │ │ ├── BiomeHabitat.java │ │ │ ├── LightHabitat.java │ │ │ ├── FluidHabitat.java │ │ │ ├── NearbyBlocksHabitat.java │ │ │ └── Habitat.java │ ├── abilities │ │ ├── FootprintAbility.java │ │ ├── HotFeetAbility.java │ │ ├── SnowStepperAbility.java │ │ ├── HydroStepAbility.java │ │ ├── FrostWalkerAbility.java │ │ ├── ReaperStepAbility.java │ │ ├── GreenToesAbility.java │ │ └── Ability.java │ └── DragonSpawnEgg.java │ ├── mixins │ ├── EnsureSafeFlyingVehicleMixin.java │ ├── ReplenishDragonEggMixin.java │ └── client │ │ └── ModelPartMixin.java │ ├── data │ ├── providers │ │ ├── DataProvider.java │ │ ├── ModelPropertiesProvider.java │ │ ├── LootModifierProvider.java │ │ ├── LootTableProvider.java │ │ └── BlockTagProvider.java │ ├── loot │ │ ├── conditions │ │ │ └── RandomChanceByConfig.java │ │ └── DragonEggLootMod.java │ ├── model │ │ └── DragonModelPropertiesListener.java │ └── CrossBreedingManager.java │ ├── client │ ├── MountCameraManager.java │ ├── MountControlsMessenger.java │ ├── KeyMappings.java │ ├── ModelPartProxy.java │ ├── DragonEggModel.java │ └── DragonRenderer.java │ ├── ForgeModImpl.java │ ├── DMLRegistry.java │ ├── DragonMountsLegacy.java │ └── DMLConfig.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── example_addon ├── pack.mcmeta ├── assets │ └── example_addon │ │ ├── models │ │ ├── entity │ │ │ └── dragon │ │ │ │ └── breed │ │ │ │ └── properties │ │ │ │ └── example_breed.json │ │ └── block │ │ │ └── dragon_eggs │ │ │ └── example_breed_dragon_egg.json │ │ ├── textures │ │ ├── block │ │ │ ├── example_breed_dragon_egg.png │ │ │ └── example_breed_dragon_egg.png.mcmeta │ │ └── entity │ │ │ └── dragon │ │ │ └── example_breed │ │ │ ├── body.png │ │ │ ├── glow.png │ │ │ └── saddle.png │ │ ├── lang │ │ └── en_us.json │ │ └── sounds.json ├── data │ ├── example_addon │ │ ├── dragonmounts │ │ │ ├── cross_breeding │ │ │ │ └── example_crossbreed.json │ │ │ └── dragon_breeds │ │ │ │ └── example_breed.json │ │ └── loot_modifiers │ │ │ ├── egg_chance_by_random.json │ │ │ ├── replace_first_example.json │ │ │ └── egg_chance_by_config.json │ └── forge │ │ └── loot_modifiers │ │ └── global_loot_modifiers.json └── META-INF │ └── mods.toml ├── .gitattributes ├── .gitignore ├── settings.gradle ├── .github ├── ISSUE_TEMPLATE │ └── issue.md └── workflows │ ├── publish-artifact.yml │ └── publish-release.yml ├── gradle.properties ├── README.md └── gradlew.bat /gradlew: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/gradlew -------------------------------------------------------------------------------- /logo-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/logo-banner.png -------------------------------------------------------------------------------- /src/generated/resources/assets/dragonmounts/models/entity/dragon/breed/properties/ghost.json: -------------------------------------------------------------------------------- 1 | { 2 | "thin_legs": true 3 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/models/block/dragon_egg_models.json: -------------------------------------------------------------------------------- 1 | { 2 | "loader": "dragonmounts:dragon_egg" 3 | } -------------------------------------------------------------------------------- /src/generated/resources/assets/dragonmounts/models/entity/dragon/breed/properties/water.json: -------------------------------------------------------------------------------- 1 | { 2 | "tail_horns": true 3 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/models/item/dragon_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "dragonmounts:block/dragon_egg_models" 3 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/models/item/spawn_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:item/template_spawn_egg" 3 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/generated/resources/assets/dragonmounts/models/entity/dragon/breed/properties/fire.json: -------------------------------------------------------------------------------- 1 | { 2 | "middle_tail_scales": false 3 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/models/block/dragon_eggs/end_dragon_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/dragon_egg" 3 | } -------------------------------------------------------------------------------- /example_addon/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "Example resources", 4 | "pack_format": 15 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "${mod_id} resources", 4 | "pack_format": ${game_pack_format} 5 | } 6 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/tags/block/ice_dragon_habitat_blocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | "#minecraft:ice", 4 | "#minecraft:snow" 5 | ] 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/blockstates/dragon_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "variants": { 3 | "": { 4 | "model": "dragonmounts:block/dragon_egg_models" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /example_addon/assets/example_addon/models/entity/dragon/breed/properties/example_breed.json: -------------------------------------------------------------------------------- 1 | { 2 | "middle_tail_scales": false, 3 | "tail_horns": true, 4 | "thin_legs": false 5 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/tags/block/reaper_transform.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | "#minecraft:sand", 4 | "#minecraft:dirt", 5 | "minecraft:grass_block" 6 | ] 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/sounds/entity/dragon/death.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/sounds/entity/dragon/death.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/sounds/entity/dragon/step1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/sounds/entity/dragon/step1.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/sounds/entity/dragon/step2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/sounds/entity/dragon/step2.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/sounds/entity/dragon/step3.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/sounds/entity/dragon/step3.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/sounds/entity/dragon/step4.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/sounds/entity/dragon/step4.ogg -------------------------------------------------------------------------------- /example_addon/data/example_addon/dragonmounts/cross_breeding/example_crossbreed.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent1": "example_addon:example_breed", 3 | "parent2": "dragonmounts:fire", 4 | "child": "dragonmounts:ghost" 5 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/sounds/entity/dragon/breathe1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/sounds/entity/dragon/breathe1.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/sounds/entity/dragon/breathe2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/sounds/entity/dragon/breathe2.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/fire_dragon_egg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/block/fire_dragon_egg.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/ghost_dragon_egg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/block/ghost_dragon_egg.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/ice_dragon_egg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/block/ice_dragon_egg.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/water_dragon_egg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/block/water_dragon_egg.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/dissolve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/dissolve.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/end/body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/end/body.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/end/glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/end/glow.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/ice/body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/ice/body.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/ice/glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/ice/glow.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/aether_dragon_egg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/block/aether_dragon_egg.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/forest_dragon_egg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/block/forest_dragon_egg.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/nether_dragon_egg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/block/nether_dragon_egg.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/end/saddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/end/saddle.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/fire/body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/fire/body.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/fire/glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/fire/glow.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/ghost/body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/ghost/body.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/ghost/glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/ghost/glow.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/ice/saddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/ice/saddle.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/water/body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/water/body.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/water/glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/water/glow.png -------------------------------------------------------------------------------- /example_addon/assets/example_addon/textures/block/example_breed_dragon_egg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/example_addon/assets/example_addon/textures/block/example_breed_dragon_egg.png -------------------------------------------------------------------------------- /src/generated/resources/.cache/59eb3dbb5f86130e09b3c62d89b9525ee01cf52d: -------------------------------------------------------------------------------- 1 | // 1.21 2024-08-08T21:48:22.2802929 Loot Tables 2 | 6f9531486d9790b5544c2856fd5027443bc91d20 data/dragonmounts/loot_table/blocks/dragon_egg.json 3 | -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/aether/body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/aether/body.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/aether/glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/aether/glow.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/aether/saddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/aether/saddle.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/fire/saddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/fire/saddle.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/forest/body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/forest/body.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/forest/glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/forest/glow.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/forest/saddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/forest/saddle.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/ghost/saddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/ghost/saddle.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/nether/body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/nether/body.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/nether/glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/nether/glow.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/nether/saddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/nether/saddle.png -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/entity/dragon/water/saddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/src/main/resources/assets/dragonmounts/textures/entity/dragon/water/saddle.png -------------------------------------------------------------------------------- /example_addon/assets/example_addon/textures/entity/dragon/example_breed/body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/example_addon/assets/example_addon/textures/entity/dragon/example_breed/body.png -------------------------------------------------------------------------------- /example_addon/assets/example_addon/textures/entity/dragon/example_breed/glow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/example_addon/assets/example_addon/textures/entity/dragon/example_breed/glow.png -------------------------------------------------------------------------------- /example_addon/data/forge/loot_modifiers/global_loot_modifiers.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "entries": [ 4 | "example_addon:egg_chance_by_config.json", 5 | "example_addon:egg_chance_by_random.json" 6 | ] 7 | } -------------------------------------------------------------------------------- /example_addon/assets/example_addon/models/block/dragon_eggs/example_breed_dragon_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/dragon_egg", 3 | "textures": { 4 | "all": "example_addon:block/example_breed_dragon_egg" 5 | } 6 | } -------------------------------------------------------------------------------- /example_addon/assets/example_addon/textures/entity/dragon/example_breed/saddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MWall541/Dragon-Mounts-Legacy/HEAD/example_addon/assets/example_addon/textures/entity/dragon/example_breed/saddle.png -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/tags/block/fire_dragon_habitat_blocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | "minecraft:fire", 4 | "minecraft:lava", 5 | "minecraft:magma_block", 6 | "minecraft:campfire" 7 | ] 8 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- 1 | public net.minecraft.world.entity.LivingEntity f_20899_ # jumping 2 | public net.minecraft.client.Camera m_90568_(FFF)V # move 3 | public net.minecraft.client.Camera m_90566_(F)F # getMaxZoom 4 | -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/fire_dragon_egg.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "animation": { 3 | "frametime": 8, 4 | "interpolate": true, 5 | "frames": [ 6 | 0, 7 | 1, 8 | 2 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/forest_dragon_egg.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "animation": { 3 | "frametime": 64, 4 | "interpolate": true, 5 | "frames": [ 6 | 0, 7 | 1, 8 | 2 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/ghost_dragon_egg.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "animation": { 3 | "frametime": 16, 4 | "interpolate": true, 5 | "frames": [ 6 | 0, 7 | 1, 8 | 2 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/nether_dragon_egg.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "animation": { 3 | "frametime": 8, 4 | "interpolate": true, 5 | "frames": [ 6 | 0, 7 | 1, 8 | 2 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/water_dragon_egg.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "animation": { 3 | "frametime": 8, 4 | "interpolate": true, 5 | "frames": [ 6 | 0, 7 | 1, 8 | 2 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /example_addon/assets/example_addon/textures/block/example_breed_dragon_egg.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "animation": { 3 | "frametime": 14, 4 | "interpolate": true, 5 | "frames": [ 6 | 0, 7 | 1, 8 | 2 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/ice_dragon_egg.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "animation": { 3 | "frametime": 10, 4 | "interpolate": true, 5 | "frames": [ 6 | 0, 7 | 1, 8 | 2, 9 | 3 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/models/block/dragon_eggs/ice_dragon_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/dragon_egg", 3 | "textures": { 4 | "particle": "dragonmounts:block/ice_dragon_egg", 5 | "all": "dragonmounts:block/ice_dragon_egg" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/textures/block/aether_dragon_egg.png.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "animation": { 3 | "frametime": 5, 4 | "interpolate": true, 5 | "frames": [ 6 | 0, 7 | 1, 8 | 2, 9 | 3 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/models/block/dragon_eggs/fire_dragon_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/dragon_egg", 3 | "textures": { 4 | "particle": "dragonmounts:block/fire_dragon_egg", 5 | "all": "dragonmounts:block/fire_dragon_egg" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/models/block/dragon_eggs/ghost_dragon_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/dragon_egg", 3 | "textures": { 4 | "particle": "dragonmounts:block/ghost_dragon_egg", 5 | "all": "dragonmounts:block/ghost_dragon_egg" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/models/block/dragon_eggs/water_dragon_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/dragon_egg", 3 | "textures": { 4 | "particle": "dragonmounts:block/water_dragon_egg", 5 | "all": "dragonmounts:block/water_dragon_egg" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/models/block/dragon_eggs/aether_dragon_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/dragon_egg", 3 | "textures": { 4 | "particle": "dragonmounts:block/aether_dragon_egg", 5 | "all": "dragonmounts:block/aether_dragon_egg" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/models/block/dragon_eggs/forest_dragon_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/dragon_egg", 3 | "textures": { 4 | "particle": "dragonmounts:block/forest_dragon_egg", 5 | "all": "dragonmounts:block/forest_dragon_egg" 6 | } 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/models/block/dragon_eggs/nether_dragon_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "minecraft:block/dragon_egg", 3 | "textures": { 4 | "particle": "dragonmounts:block/nether_dragon_egg", 5 | "all": "dragonmounts:block/nether_dragon_egg" 6 | } 7 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Disable autocrlf on generated files, they always generate with LF 2 | # Add any extra files or paths here to make git stop saying they 3 | # are changed when only line endings change. 4 | src/generated/**/.cache/cache text eol=lf 5 | src/generated/**/*.json text eol=lf 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jul 29 14:53:15 EDT 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/tags/block/forest_dragon_habitat_blocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | "#minecraft:leaves", 4 | "#minecraft:saplings", 5 | "#minecraft:flowers", 6 | "minecraft:moss_block", 7 | "minecraft:moss_carpet", 8 | "minecraft:vine" 9 | ] 10 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/tags/block/reaper_plant_destruction.json: -------------------------------------------------------------------------------- 1 | { 2 | "remove": [ 3 | "minecraft:wither_rose" 4 | ], 5 | "values": [ 6 | "#minecraft:small_flowers", 7 | "minecraft:red_mushroom", 8 | "minecraft:brown_mushroom", 9 | "minecraft:small_dripleaf" 10 | ] 11 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # eclipse 2 | bin 3 | *.launch 4 | .settings 5 | .metadata 6 | .classpath 7 | .project 8 | 9 | # idea 10 | out 11 | *.ipr 12 | *.iws 13 | *.iml 14 | .idea 15 | 16 | # gradle 17 | build 18 | .gradle 19 | 20 | # other 21 | eclipse 22 | run 23 | runs 24 | 25 | # Files from Forge MDK 26 | forge*changelog.txt 27 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | maven { url = 'https://maven.minecraftforge.net/' } 5 | maven { url = 'https://maven.parchmentmc.org' } 6 | } 7 | } 8 | 9 | plugins { 10 | id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0' 11 | } -------------------------------------------------------------------------------- /example_addon/assets/example_addon/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "dragon_breed.example_addon.example_breed": "Example Dragon", 3 | 4 | "item.dragonmounts.spawn_egg.example_addon.example_breed": "Example Dragon Spawn Egg", 5 | 6 | "block.dragonmounts.dragon_egg.example_addon.example_breed": "Example Dragon Egg", 7 | 8 | "subtitles.entity.dragon.ambient.example": "Dragon speaks" 9 | } -------------------------------------------------------------------------------- /src/main/resources/dragonmounts.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "package": "com.github.kay9.dragonmounts.mixins", 4 | "minVersion": "0.8.5", 5 | "compatibilityLevel": "JAVA_21", 6 | "refmap": "dragonmounts.mixins.refmap.json", 7 | "mixins": [ 8 | "EnsureSafeFlyingVehicleMixin", 9 | "ReplenishDragonEggMixin" 10 | ], 11 | "client": [ 12 | "client.ModelPartMixin" 13 | ] 14 | } -------------------------------------------------------------------------------- /example_addon/assets/example_addon/sounds.json: -------------------------------------------------------------------------------- 1 | { 2 | "entity.dragon.ambient.example": { 3 | "sounds": [ 4 | { 5 | "name": "dragonmounts:entity.dragon.ambient", 6 | "type": "event" 7 | }, 8 | { 9 | "name": "minecraft:entity.cat.ambient", 10 | "type": "event" 11 | } 12 | ], 13 | "subtitle": "subtitles.entity.dragon.ambient.example" 14 | } 15 | } -------------------------------------------------------------------------------- /example_addon/data/example_addon/loot_modifiers/egg_chance_by_random.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "dragonmounts:dragon_egg_loot", 3 | "egg_breed": "example_addon:example_breed", 4 | "conditions": [ 5 | { 6 | "condition": "forge:loot_table_id", 7 | "loot_table_id": "minecraft:chests/simple_dungeon" 8 | }, 9 | { 10 | "condition": "minecraft:random_chance", 11 | "chance": 0.25 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/tags/block/water_dragon_habitat_blocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | "#minecraft:corals", 4 | "#minecraft:wall_corals", 5 | "#minecraft:coral_blocks", 6 | "minecraft:seagrass", 7 | "minecraft:tall_seagrass", 8 | "minecraft:kelp", 9 | "minecraft:kelp_plant", 10 | "minecraft:prismarine", 11 | "minecraft:sea_lantern", 12 | "minecraft:sea_pickle" 13 | ] 14 | } -------------------------------------------------------------------------------- /example_addon/data/example_addon/loot_modifiers/replace_first_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "dragonmounts:dragon_egg_loot", 3 | "egg_breed": "example_addon:example_breed", 4 | "replace_first": true, 5 | "conditions": [ 6 | { 7 | "condition": "forge:loot_table_id", 8 | "loot_table_id": "minecraft:archaeology/desert_well" 9 | }, 10 | { 11 | "condition": "minecraft:random_chance", 12 | "chance": 0.25 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /src/generated/resources/.cache/c31184ad3d9826d5a5f417546a2ca23fe387ca08: -------------------------------------------------------------------------------- 1 | // 1.21 2024-08-08T16:35:03.3515765 dragonmounts: Dragon Model Properties 2 | c4534ba1c7656f9186a4e57de389260b6ccb572c assets/dragonmounts/models/entity/dragon/breed/properties/fire.json 3 | c31c62126135a797d15e81501764e10b1d7a34a0 assets/dragonmounts/models/entity/dragon/breed/properties/ghost.json 4 | f19b8d990b065aa3829244bf079f967117bb2031 assets/dragonmounts/models/entity/dragon/breed/properties/water.json 5 | -------------------------------------------------------------------------------- /example_addon/data/example_addon/loot_modifiers/egg_chance_by_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "dragonmounts:dragon_egg_loot", 3 | "egg_breed": "example_addon:example_breed", 4 | "conditions": [ 5 | { 6 | "condition": "forge:loot_table_id", 7 | "loot_table_id": "minecraft:chests/desert_pyramid" 8 | }, 9 | { 10 | "condition": "dragonmounts:random_chance_by_config", 11 | "config_chance_target": "fire_in_desert_pyramid_chance" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/loot_modifiers/dragonmounts/ice/chests/igloo_chest.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "dragonmounts:dragon_egg_loot", 3 | "conditions": [ 4 | { 5 | "condition": "forge:loot_table_id", 6 | "loot_table_id": "minecraft:chests/igloo_chest" 7 | }, 8 | { 9 | "condition": "dragonmounts:random_chance_by_config", 10 | "config_chance_target": "ice_in_igloo_chest_chance" 11 | } 12 | ], 13 | "egg_breed": "dragonmounts:ice" 14 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/loot_modifiers/dragonmounts/fire/chests/desert_pyramid.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "dragonmounts:dragon_egg_loot", 3 | "conditions": [ 4 | { 5 | "condition": "forge:loot_table_id", 6 | "loot_table_id": "minecraft:chests/desert_pyramid" 7 | }, 8 | { 9 | "condition": "dragonmounts:random_chance_by_config", 10 | "config_chance_target": "fire_in_desert_pyramid_chance" 11 | } 12 | ], 13 | "egg_breed": "dragonmounts:fire" 14 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/loot_modifiers/dragonmounts/forest/chests/jungle_temple.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "dragonmounts:dragon_egg_loot", 3 | "conditions": [ 4 | { 5 | "condition": "forge:loot_table_id", 6 | "loot_table_id": "minecraft:chests/jungle_temple" 7 | }, 8 | { 9 | "condition": "dragonmounts:random_chance_by_config", 10 | "config_chance_target": "forest_in_jungle_temple_chance" 11 | } 12 | ], 13 | "egg_breed": "dragonmounts:forest" 14 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/loot_modifiers/dragonmounts/aether/chests/simple_dungeon.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "dragonmounts:dragon_egg_loot", 3 | "conditions": [ 4 | { 5 | "condition": "forge:loot_table_id", 6 | "loot_table_id": "minecraft:chests/simple_dungeon" 7 | }, 8 | { 9 | "condition": "dragonmounts:random_chance_by_config", 10 | "config_chance_target": "aether_in_simple_dungeon_chance" 11 | } 12 | ], 13 | "egg_breed": "dragonmounts:aether" 14 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/loot_modifiers/dragonmounts/water/chests/buried_treasure.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "dragonmounts:dragon_egg_loot", 3 | "conditions": [ 4 | { 5 | "condition": "forge:loot_table_id", 6 | "loot_table_id": "minecraft:chests/buried_treasure" 7 | }, 8 | { 9 | "condition": "dragonmounts:random_chance_by_config", 10 | "config_chance_target": "water_in_buried_treasure_chance" 11 | } 12 | ], 13 | "egg_breed": "dragonmounts:water" 14 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/tags/block/reaper_plant_death.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | "#minecraft:tall_flowers", 4 | "#minecraft:crops", 5 | "#minecraft:saplings", 6 | "minecraft:tall_grass", 7 | "minecraft:short_grass", 8 | "minecraft:sweet_berry_bush", 9 | "minecraft:sugar_cane", 10 | "minecraft:big_dripleaf_stem", 11 | "minecraft:big_dripleaf", 12 | "minecraft:fern", 13 | "minecraft:large_fern", 14 | "minecraft:pitcher_plant" 15 | ] 16 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/loot_modifiers/dragonmounts/ghost/chests/woodland_mansion.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "dragonmounts:dragon_egg_loot", 3 | "conditions": [ 4 | { 5 | "condition": "forge:loot_table_id", 6 | "loot_table_id": "minecraft:chests/woodland_mansion" 7 | }, 8 | { 9 | "condition": "dragonmounts:random_chance_by_config", 10 | "config_chance_target": "ghost_in_woodland_mansion_chance" 11 | } 12 | ], 13 | "egg_breed": "dragonmounts:ghost" 14 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/loot_modifiers/dragonmounts/nether/chests/bastion_treasure.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "dragonmounts:dragon_egg_loot", 3 | "conditions": [ 4 | { 5 | "condition": "forge:loot_table_id", 6 | "loot_table_id": "minecraft:chests/bastion_treasure" 7 | }, 8 | { 9 | "condition": "dragonmounts:random_chance_by_config", 10 | "config_chance_target": "nether_in_bastion_treasure_chance" 11 | } 12 | ], 13 | "egg_breed": "dragonmounts:nether" 14 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/loot_modifiers/dragonmounts/ghost/chests/abandoned_mineshaft.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "dragonmounts:dragon_egg_loot", 3 | "conditions": [ 4 | { 5 | "condition": "forge:loot_table_id", 6 | "loot_table_id": "minecraft:chests/abandoned_mineshaft" 7 | }, 8 | { 9 | "condition": "dragonmounts:random_chance_by_config", 10 | "config_chance_target": "ghost_in_abandoned_mineshaft_chance" 11 | } 12 | ], 13 | "egg_breed": "dragonmounts:ghost" 14 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/dragonmounts/dragon_breeds/aether.json: -------------------------------------------------------------------------------- 1 | { 2 | "attributes": { 3 | "minecraft:generic.flying_speed": 0.3920000076293945 4 | }, 5 | "habitats": [ 6 | { 7 | "type": "dragonmounts:world_height", 8 | "height": 200 9 | } 10 | ], 11 | "immunities": [ 12 | "minecraft:cactus", 13 | "minecraft:sweet_berry_bush", 14 | "minecraft:dragon_breath" 15 | ], 16 | "primary_color": "718aa9", 17 | "reproduction_limit": "config:aether", 18 | "secondary_color": "e6e6e6" 19 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/accessors/ModelPartAccess.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.accessors; 2 | 3 | public interface ModelPartAccess 4 | { 5 | float getXScale(); 6 | 7 | float getYScale(); 8 | 9 | float getZScale(); 10 | 11 | void setXScale(float x); 12 | 13 | void setYScale(float y); 14 | 15 | void setZScale(float z); 16 | 17 | default void setRenderScale(float x, float y, float z) 18 | { 19 | setXScale(x); 20 | setYScale(y); 21 | setZScale(z); 22 | } 23 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/dragonmounts/dragon_breeds/end.json: -------------------------------------------------------------------------------- 1 | { 2 | "attributes": { 3 | "minecraft:generic.max_health": 75.0 4 | }, 5 | "habitats": [ 6 | { 7 | "type": "dragonmounts:dragon_breath" 8 | } 9 | ], 10 | "hatch_particles": { 11 | "type": "minecraft:portal" 12 | }, 13 | "immunities": [ 14 | "minecraft:cactus", 15 | "minecraft:sweet_berry_bush", 16 | "minecraft:dragon_breath" 17 | ], 18 | "primary_color": "161616", 19 | "reproduction_limit": "config:end", 20 | "secondary_color": "ff63e8" 21 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/tags/block/hot_feet_burnables.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | "#minecraft:flowers", 4 | "#minecraft:saplings", 5 | "#minecraft:crops", 6 | "minecraft:short_grass", 7 | "minecraft:tall_grass", 8 | "minecraft:sweet_berry_bush", 9 | "minecraft:dead_bush", 10 | "minecraft:pitcher_plant", 11 | "minecraft:big_dripleaf", 12 | "minecraft:small_dripleaf", 13 | "minecraft:big_dripleaf_stem", 14 | "minecraft:sugar_cane", 15 | "minecraft:fern", 16 | "minecraft:large_fern", 17 | "minecraft:pitcher_plant" 18 | ] 19 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue 3 | about: Make sure your issue hasn't already been reported! 4 | --- 5 | 6 | 7 | 8 | 9 | **Logs:** 10 | 11 | 12 | 13 | **Steps to Reproduce:** 14 | 19 | 20 | **Description of issue:** 21 | -------------------------------------------------------------------------------- /src/generated/resources/data/forge/loot_modifiers/global_loot_modifiers.json: -------------------------------------------------------------------------------- 1 | { 2 | "entries": [ 3 | "dragonmounts:dragonmounts/aether/chests/simple_dungeon", 4 | "dragonmounts:dragonmounts/ghost/chests/woodland_mansion", 5 | "dragonmounts:dragonmounts/water/chests/buried_treasure", 6 | "dragonmounts:dragonmounts/fire/chests/desert_pyramid", 7 | "dragonmounts:dragonmounts/forest/chests/jungle_temple", 8 | "dragonmounts:dragonmounts/ghost/chests/abandoned_mineshaft", 9 | "dragonmounts:dragonmounts/ice/chests/igloo_chest", 10 | "dragonmounts:dragonmounts/nether/chests/bastion_treasure" 11 | ], 12 | "replace": false 13 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/tags/block/nether_dragon_habitat_blocks.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | "#minecraft:soul_fire_base_blocks", 4 | "#minecraft:base_stone_nether", 5 | "#minecraft:warped_stems", 6 | "#minecraft:crimson_stems", 7 | "minecraft:glowstone", 8 | "minecraft:soul_fire", 9 | "minecraft:soul_campfire", 10 | "minecraft:soul_torch", 11 | "minecraft:nether_gold_ore", 12 | "minecraft:nether_quartz_ore", 13 | "minecraft:ancient_debris", 14 | "minecraft:twisting_vines_plant", 15 | "minecraft:weeping_vines_plant", 16 | "minecraft:shroomlight", 17 | "minecraft:nether_wart", 18 | "minecraft:nether_wart_block" 19 | ] 20 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/util/DMLUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.util; 2 | 3 | import com.github.kay9.dragonmounts.DragonMountsLegacy; 4 | import com.mojang.serialization.Codec; 5 | import com.mojang.serialization.DataResult; 6 | 7 | public class DMLUtil 8 | { 9 | public static final Codec HEX_CODEC = Codec.STRING.comapFlatMap(s -> 10 | { 11 | try 12 | { 13 | return DataResult.success(Integer.parseInt(s, 16)); 14 | } 15 | catch (NumberFormatException e) 16 | { 17 | return DataResult.error(() -> String.format("[%s] Hexadecimal Codec error: '%s' is not a valid hex value.", DragonMountsLegacy.MOD_ID, s)); 18 | } 19 | }, Integer::toHexString); 20 | } 21 | -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/dragonmounts/dragon_breeds/forest.json: -------------------------------------------------------------------------------- 1 | { 2 | "abilities": [ 3 | { 4 | "type": "dragonmounts:green_toes" 5 | } 6 | ], 7 | "habitats": [ 8 | { 9 | "type": "dragonmounts:nearby_blocks", 10 | "block_tag": "dragonmounts:forest_dragon_habitat_blocks" 11 | }, 12 | { 13 | "type": "dragonmounts:biome", 14 | "biome_tag": "minecraft:is_jungle" 15 | } 16 | ], 17 | "hatch_particles": { 18 | "type": "minecraft:happy_villager" 19 | }, 20 | "immunities": [ 21 | "minecraft:cactus", 22 | "minecraft:sweet_berry_bush", 23 | "minecraft:dragon_breath" 24 | ], 25 | "primary_color": "54a00", 26 | "reproduction_limit": "config:forest", 27 | "secondary_color": "a9600" 28 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/dragonmounts/dragon_breeds/ice.json: -------------------------------------------------------------------------------- 1 | { 2 | "abilities": [ 3 | { 4 | "type": "dragonmounts:frost_walker", 5 | "radius_multiplier": 3.0 6 | }, 7 | { 8 | "type": "dragonmounts:snow_stepper" 9 | } 10 | ], 11 | "habitats": [ 12 | { 13 | "type": "dragonmounts:nearby_blocks", 14 | "block_tag": "dragonmounts:ice_dragon_habitat_blocks" 15 | } 16 | ], 17 | "hatch_particles": { 18 | "type": "minecraft:snowflake" 19 | }, 20 | "immunities": [ 21 | "minecraft:drown", 22 | "minecraft:freeze", 23 | "minecraft:cactus", 24 | "minecraft:sweet_berry_bush", 25 | "minecraft:dragon_breath" 26 | ], 27 | "primary_color": "ffffff", 28 | "reproduction_limit": "config:ice", 29 | "secondary_color": "e1ff" 30 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/dragonmounts/dragon_breeds/water.json: -------------------------------------------------------------------------------- 1 | { 2 | "abilities": [ 3 | { 4 | "type": "dragonmounts:hydro_step" 5 | } 6 | ], 7 | "habitats": [ 8 | { 9 | "type": "dragonmounts:in_fluid", 10 | "fluid_tag": "minecraft:water", 11 | "point_multiplier": 1.0 12 | }, 13 | { 14 | "type": "dragonmounts:nearby_blocks", 15 | "block_tag": "dragonmounts:water_dragon_habitat_blocks" 16 | } 17 | ], 18 | "hatch_particles": { 19 | "type": "minecraft:dripping_water" 20 | }, 21 | "immunities": [ 22 | "minecraft:drown", 23 | "minecraft:cactus", 24 | "minecraft:sweet_berry_bush", 25 | "minecraft:dragon_breath" 26 | ], 27 | "primary_color": "62ff", 28 | "reproduction_limit": "config:water", 29 | "secondary_color": "5999ff" 30 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/dragonmounts/dragon_breeds/nether.json: -------------------------------------------------------------------------------- 1 | { 2 | "attributes": { 3 | "minecraft:generic.armor": 8.0 4 | }, 5 | "habitats": [ 6 | { 7 | "type": "dragonmounts:nearby_blocks", 8 | "block_tag": "dragonmounts:nether_dragon_habitat_blocks" 9 | }, 10 | { 11 | "type": "dragonmounts:biome", 12 | "biome_tag": "minecraft:is_nether", 13 | "points": 3 14 | } 15 | ], 16 | "hatch_particles": { 17 | "type": "minecraft:soul_fire_flame" 18 | }, 19 | "immunities": [ 20 | "minecraft:on_fire", 21 | "minecraft:in_fire", 22 | "minecraft:lava", 23 | "minecraft:hot_floor", 24 | "minecraft:cactus", 25 | "minecraft:sweet_berry_bush", 26 | "minecraft:dragon_breath" 27 | ], 28 | "primary_color": "912400", 29 | "reproduction_limit": "config:nether", 30 | "secondary_color": "2e0b00" 31 | } -------------------------------------------------------------------------------- /src/generated/resources/.cache/103d9f3f36b01595f1aa5172191e60eff02e6924: -------------------------------------------------------------------------------- 1 | // 1.21 2024-08-08T17:55:25.4113023 Registries 2 | a0523a795690407ca91edb526b9b61953916981c data/dragonmounts/dragonmounts/dragon_breeds/aether.json 3 | cb452eb1b152bc9179c87b2180e8fbdcba41c4ed data/dragonmounts/dragonmounts/dragon_breeds/end.json 4 | 87809597e84b063c4dd0e0e5658d9f433529ec67 data/dragonmounts/dragonmounts/dragon_breeds/fire.json 5 | bc85671eebb56353df84da398a1f35e325928ed0 data/dragonmounts/dragonmounts/dragon_breeds/forest.json 6 | b670737d08363babd22b07312fe505193b27151f data/dragonmounts/dragonmounts/dragon_breeds/ghost.json 7 | c18cf8c66ceff1f2f3a7b96d83aae5e7f5270834 data/dragonmounts/dragonmounts/dragon_breeds/ice.json 8 | 115077afac718460ec9c0d37244d82efd5a1bf65 data/dragonmounts/dragonmounts/dragon_breeds/nether.json 9 | 30d94ce6badc78ab9777a8f73195b492c645b4e9 data/dragonmounts/dragonmounts/dragon_breeds/water.json 10 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Sets default memory used for gradle commands. Can be overridden by user or command line properties. 2 | # This is required to provide enough memory for the Minecraft decompilation process. 3 | org.gradle.jvmargs=-Xmx3G 4 | org.gradle.daemon=false 5 | 6 | mod_id = dragonmounts 7 | mod_name = Dragon Mounts: Legacy 8 | # Default to 999. 9 | # Actual release versions are acquired from the git tag and passed into the build args. 10 | mod_version = 999 11 | # https://minecraft.fandom.com/wiki/Tutorials/Creating_a_data_pack#%22pack_format%22 12 | game_pack_format = 48 13 | # https://github.com/ParchmentMC/Parchment/tags 14 | mappings_version = 2024.07.28-1.21 15 | 16 | java_version = 21 17 | 18 | game_version = 1.21 19 | game_version_range = [1.21,1.22) 20 | 21 | forge_version = 51.0.33 22 | forge_version_range = [51.0.33,) 23 | loader_version_range = [51,) 24 | 25 | mixin_processor_version = 0.8.5 26 | 27 | jei_version = 19.7.0.79 -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/dragonmounts/dragon_breeds/fire.json: -------------------------------------------------------------------------------- 1 | { 2 | "abilities": [ 3 | { 4 | "type": "dragonmounts:hot_feet" 5 | } 6 | ], 7 | "habitats": [ 8 | { 9 | "type": "dragonmounts:nearby_blocks", 10 | "block_tag": "dragonmounts:fire_dragon_habitat_blocks", 11 | "point_multiplier": 1.0 12 | }, 13 | { 14 | "type": "dragonmounts:in_fluid", 15 | "fluid_tag": "minecraft:lava", 16 | "point_multiplier": 3.0 17 | } 18 | ], 19 | "hatch_particles": { 20 | "type": "minecraft:flame" 21 | }, 22 | "immunities": [ 23 | "minecraft:on_fire", 24 | "minecraft:in_fire", 25 | "minecraft:lava", 26 | "minecraft:hot_floor", 27 | "minecraft:cactus", 28 | "minecraft:sweet_berry_bush", 29 | "minecraft:dragon_breath" 30 | ], 31 | "primary_color": "912400", 32 | "reproduction_limit": "config:fire", 33 | "secondary_color": "ff9819" 34 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/dragonmounts/dragon_breeds/ghost.json: -------------------------------------------------------------------------------- 1 | { 2 | "abilities": [ 3 | { 4 | "type": "dragonmounts:reaper_step" 5 | } 6 | ], 7 | "ambient_sound": "dragonmounts:entity.dragon.ambient.ghost", 8 | "habitats": [ 9 | { 10 | "type": "dragonmounts:picky", 11 | "required_habitats": [ 12 | { 13 | "type": "dragonmounts:world_height", 14 | "below": true, 15 | "height": 0, 16 | "points": 1 17 | }, 18 | { 19 | "type": "dragonmounts:light", 20 | "below": true, 21 | "light": 3, 22 | "points": 2 23 | } 24 | ] 25 | } 26 | ], 27 | "immunities": [ 28 | "minecraft:drown", 29 | "minecraft:cactus", 30 | "minecraft:sweet_berry_bush", 31 | "minecraft:dragon_breath" 32 | ], 33 | "primary_color": "c4c4c4", 34 | "reproduction_limit": "config:ghost", 35 | "secondary_color": "c2f8ff" 36 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/ai/DragonBodyController.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.ai; 2 | 3 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 4 | import net.minecraft.util.Mth; 5 | import net.minecraft.world.entity.ai.control.BodyRotationControl; 6 | 7 | public class DragonBodyController extends BodyRotationControl 8 | { 9 | private final TameableDragon dragon; 10 | 11 | public DragonBodyController(TameableDragon dragon) 12 | { 13 | super(dragon); 14 | this.dragon = dragon; 15 | } 16 | 17 | @Override 18 | public void clientTick() 19 | { 20 | // sync the body to the yRot; no reason to have any other random rotations. 21 | dragon.yBodyRot = dragon.getYRot(); 22 | 23 | // clamp head rotations so necks don't fucking turn inside out 24 | dragon.yHeadRot = Mth.rotateIfNecessary(dragon.yHeadRot, dragon.yBodyRot, dragon.getMaxHeadYRot()); 25 | } 26 | } -------------------------------------------------------------------------------- /src/generated/resources/data/dragonmounts/loot_table/blocks/dragon_egg.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "bonus_rolls": 0.0, 6 | "entries": [ 7 | { 8 | "type": "minecraft:item", 9 | "functions": [ 10 | { 11 | "function": "minecraft:copy_components", 12 | "include": [ 13 | "dragonmounts:dragon_breed", 14 | "minecraft:custom_name" 15 | ], 16 | "source": "block_entity" 17 | }, 18 | { 19 | "block": "dragonmounts:dragon_egg", 20 | "function": "minecraft:copy_state", 21 | "properties": [ 22 | "hatch_stage" 23 | ] 24 | } 25 | ], 26 | "name": "dragonmounts:dragon_egg" 27 | } 28 | ], 29 | "rolls": 1.0 30 | } 31 | ], 32 | "random_sequence": "dragonmounts:blocks/dragon_egg" 33 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader="javafml" 2 | loaderVersion="${loader_version_range}" 3 | license="MIT" 4 | issueTrackerURL="https://github.com/Kay9Unit/Dragon-Mounts-Legacy/issues" 5 | [[mods]] 6 | modId="${mod_id}" 7 | version="${mod_version}" 8 | displayName="${mod_name}" 9 | #logoFile="examplemod.png" 10 | credits=''' 11 | ata4/barracuda - original project and idea. 12 | Mojang - Base game and sound assets. 13 | ''' 14 | authors="Kay9" 15 | description='''This is a mod that allows you to hatch previously useless dragon eggs. 16 | Once fostered and tamed, they'll be your faithful companion in all situations and, of course, can be used for a ride!''' 17 | [[dependencies.${mod_id}]] 18 | modId="forge" 19 | mandatory=true 20 | versionRange="${forge_version_range}" 21 | ordering="NONE" 22 | side="BOTH" 23 | 24 | [[dependencies.${mod_id}]] 25 | modId="minecraft" 26 | mandatory=true 27 | versionRange="${minecraft_version_range}" 28 | ordering="NONE" 29 | side="BOTH" 30 | -------------------------------------------------------------------------------- /.github/workflows/publish-artifact.yml: -------------------------------------------------------------------------------- 1 | name: Java-Gradle CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - neoforge 8 | - 'mc/**' 9 | pull_request: 10 | branches: 11 | - master 12 | - neoforge 13 | - 'mc/**' 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | 19 | permissions: 20 | contents: read 21 | packages: write 22 | 23 | steps: 24 | - uses: actions/checkout@v4 25 | 26 | - name: Set up JDK 27 | uses: actions/setup-java@v4 28 | with: 29 | java-version: '21' 30 | distribution: 'temurin' 31 | 32 | - name: Setup Gradle 33 | uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 34 | 35 | - name: Build with Gradle 36 | run: ./gradlew build -Pmod_version=${{ github.run_id }}-artifact 37 | 38 | - name: Publish Artifacts 39 | uses: actions/upload-artifact@v4 40 | with: 41 | name: ${{ github.ref_name }} 42 | path: build/libs/ -------------------------------------------------------------------------------- /src/generated/resources/.cache/93b1357e1c4c69ccdbad96ca186d77c71b1b8ae5: -------------------------------------------------------------------------------- 1 | // 1.21 2024-08-08T16:35:03.3500684 Tags for minecraft:block mod id dragonmounts 2 | 4ef8e409b5180a94fdb0ff052b94214e533c9bd2 data/dragonmounts/tags/block/fire_dragon_habitat_blocks.json 3 | b07f550717543f4109ec2ccd99a783d4af9abb89 data/dragonmounts/tags/block/forest_dragon_habitat_blocks.json 4 | e4db2cd1f9577a428dffa7dee7d1249d46dbcf46 data/dragonmounts/tags/block/hot_feet_burnables.json 5 | 41297dc5d6cb4f38f3432511574a09cf7562b002 data/dragonmounts/tags/block/ice_dragon_habitat_blocks.json 6 | c31361b6a894b18d2cfa61f07ef0ddc0a51d6a3b data/dragonmounts/tags/block/nether_dragon_habitat_blocks.json 7 | e20d63cec88365c67f1d69890edca5392f13af45 data/dragonmounts/tags/block/reaper_plant_death.json 8 | 22d2a6dc9ecf65c5181bd3e09c44d9c2e587652d data/dragonmounts/tags/block/reaper_plant_destruction.json 9 | ff22ba9824374b4e9051491299ad94880fcb1703 data/dragonmounts/tags/block/reaper_transform.json 10 | d2608214a9a5f0937ea0364e49ba75cf9912f152 data/dragonmounts/tags/block/water_dragon_habitat_blocks.json 11 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/egg/habitats/DragonBreathHabitat.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.egg.habitats; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.MapCodec; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.core.particles.ParticleTypes; 7 | import net.minecraft.resources.ResourceLocation; 8 | import net.minecraft.world.entity.EntityType; 9 | import net.minecraft.world.level.Level; 10 | import net.minecraft.world.phys.AABB; 11 | 12 | public enum DragonBreathHabitat implements Habitat 13 | { 14 | INSTANCE; 15 | 16 | public static final MapCodec CODEC = MapCodec.unit(INSTANCE); 17 | 18 | @Override 19 | public int getHabitatPoints(Level level, BlockPos pos) 20 | { 21 | return !level.getEntities(EntityType.AREA_EFFECT_CLOUD, 22 | new AABB(pos), 23 | c -> c.getParticle() == ParticleTypes.DRAGON_BREATH).isEmpty()? 10 : 0; 24 | } 25 | 26 | @Override 27 | public MapCodec codec() 28 | { 29 | return CODEC; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](logo-banner.png) 2 | 3 | ___ 4 | 5 | [![CurseForge](https://img.shields.io/curseforge/dt/375088?logo=curseforge&label=Curseforge&labelColor=333333&color=%23ff6a00)](https://www.curseforge.com/minecraft/mc-mods/dragon-mounts-legacy) 6 | [![Modrinth](https://img.shields.io/modrinth/dt/G3EPcczP?logo=modrinth&label=%20Modrinth&labelColor=333333)](https://modrinth.com/mod/dragon-mounts-legacy) 7 | [![Support me on Patreon](https://img.shields.io/badge/dynamic/json?logo=Patreon&logoColor=f96854&style=flat&color=f96854&label=Patreon&labelColor=052d49&query=data.attributes.patron_count&url=https%3A%2F%2Fwww.patreon.com%2Fapi%2Fcampaigns%2F5686478?)](https://patreon.com/kaynien) 8 | 9 | GitHub Repository for the Dragon Mounts: Legacy Minecraft Mod. 10 | 11 | > "A Minecraft mod that allows you to breed dragon eggs and foster them to ridable dragons." 12 | > 13 | > BarracudaATA 14 | 15 | > Dragon Mounts by Barracuda can be found [here](https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/minecraft-mods/wip-mods/1439594-dragon-mounts-r46-wip), 16 | with the github repo [here](https://github.com/ata4/dragon-mounts). 17 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release CI 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | permissions: 13 | contents: write 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Set up JDK 19 | uses: actions/setup-java@v4 20 | with: 21 | java-version: '21' 22 | distribution: 'temurin' 23 | 24 | - name: Setup Gradle 25 | uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0 26 | 27 | - name: Build with Gradle 28 | # `#*-` : strip out the game version from the ref tag 29 | run: | 30 | ./gradlew build -Pmod_version=${GITHUB_REF_NAME#*-} 31 | 32 | - name: Publish jar 33 | uses: Kir-Antipov/mc-publish@v3.3 34 | with: 35 | modrinth-id: G3EPcczP 36 | modrinth-token: ${{ secrets.MODRINTH_TOKEN }} 37 | 38 | curseforge-id: 375088 39 | curseforge-token: ${{ secrets.CURSEFORGE_TOKEN }} 40 | 41 | github-token: ${{ secrets.GH_TOKEN }} -------------------------------------------------------------------------------- /src/generated/resources/.cache/61b45c1b2a664e662355af098e0fe46142bb878b: -------------------------------------------------------------------------------- 1 | // 1.21 2024-08-08T16:35:03.351074 Global Loot Modifiers : dragonmounts 2 | 9db0a1d1399942791641f193af4c4015b896f0c4 data/dragonmounts/loot_modifiers/dragonmounts/aether/chests/simple_dungeon.json 3 | 842092289f27244a03f79ffbbf3ae45a0ca6d8ad data/dragonmounts/loot_modifiers/dragonmounts/fire/chests/desert_pyramid.json 4 | 49c6e5e4af4cf14896f7d4fddb76aaa5fd421a1f data/dragonmounts/loot_modifiers/dragonmounts/forest/chests/jungle_temple.json 5 | 69e0a5928bc14d00722a57cfae637564ac9174a7 data/dragonmounts/loot_modifiers/dragonmounts/ghost/chests/abandoned_mineshaft.json 6 | 1cf8e2bf637d65032653c74aedac7737cf54490f data/dragonmounts/loot_modifiers/dragonmounts/ghost/chests/woodland_mansion.json 7 | 5e961f72347887ca7af37eccaf3ba24b59e17c49 data/dragonmounts/loot_modifiers/dragonmounts/ice/chests/igloo_chest.json 8 | 6bdd65bbc1b1fe768735b71221ab50225c2bf797 data/dragonmounts/loot_modifiers/dragonmounts/nether/chests/bastion_treasure.json 9 | 8ca22e3a903a78b5a7c8a11a8773fb306bf581fb data/dragonmounts/loot_modifiers/dragonmounts/water/chests/buried_treasure.json 10 | 7276bec72c2baaeed283a0e7ffdb2d5d1b0828f5 data/forge/loot_modifiers/global_loot_modifiers.json 11 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/egg/habitats/HeightHabitat.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.egg.habitats; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.MapCodec; 5 | import com.mojang.serialization.codecs.RecordCodecBuilder; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.world.level.Level; 8 | 9 | public record HeightHabitat(int points, boolean below, int height) implements Habitat 10 | { 11 | public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(func -> func.group( 12 | Habitat.withPoints(3, HeightHabitat::points), 13 | Codec.BOOL.optionalFieldOf("below", false).forGetter(HeightHabitat::below), 14 | Codec.INT.fieldOf("height").forGetter(HeightHabitat::height) 15 | ).apply(func, HeightHabitat::new)); 16 | 17 | @Override 18 | public int getHabitatPoints(Level level, BlockPos pos) 19 | { 20 | int y = pos.getY(); 21 | int max = height; 22 | return (below? (y < max && !level.canSeeSky(pos)) : y > max)? points : 0; 23 | } 24 | 25 | @Override 26 | public MapCodec codec() 27 | { 28 | return CODEC; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/egg/habitats/PickyHabitat.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.egg.habitats; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.MapCodec; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.resources.ResourceLocation; 7 | import net.minecraft.world.level.Level; 8 | 9 | import java.util.List; 10 | 11 | public record PickyHabitat(List habitats) implements Habitat 12 | { 13 | public static final MapCodec CODEC = Habitat.CODEC 14 | .listOf() 15 | .fieldOf("required_habitats") 16 | .xmap(PickyHabitat::new, PickyHabitat::habitats); 17 | 18 | @Override 19 | public int getHabitatPoints(Level level, BlockPos pos) 20 | { 21 | int points = 0; 22 | for (var habitat : habitats) 23 | { 24 | int i = habitat.getHabitatPoints(level, pos); 25 | if (i == 0) return 0; // ALL habitat conditions must be met. Otherwise, nope. 26 | points += i; 27 | } 28 | return points; 29 | } 30 | 31 | @Override 32 | public MapCodec codec() 33 | { 34 | return CODEC; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/egg/habitats/BiomeHabitat.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.egg.habitats; 2 | 3 | import com.mojang.serialization.MapCodec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.core.registries.Registries; 7 | import net.minecraft.resources.ResourceLocation; 8 | import net.minecraft.tags.TagKey; 9 | import net.minecraft.world.level.Level; 10 | import net.minecraft.world.level.biome.Biome; 11 | 12 | public record BiomeHabitat(int points, TagKey biomeTag) implements Habitat 13 | { 14 | public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( 15 | Habitat.withPoints(2, BiomeHabitat::points), 16 | TagKey.codec(Registries.BIOME).fieldOf("biome_tag").forGetter(BiomeHabitat::biomeTag) 17 | ).apply(instance, BiomeHabitat::new)); 18 | 19 | @Override 20 | public int getHabitatPoints(Level level, BlockPos pos) 21 | { 22 | return level.getBiome(pos).is(biomeTag)? points : 0; 23 | } 24 | 25 | @Override 26 | public MapCodec codec() 27 | { 28 | return CODEC; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/egg/habitats/LightHabitat.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.egg.habitats; 2 | 3 | import com.mojang.serialization.Codec; 4 | import com.mojang.serialization.MapCodec; 5 | import com.mojang.serialization.codecs.RecordCodecBuilder; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.resources.ResourceLocation; 8 | import net.minecraft.world.level.Level; 9 | 10 | public record LightHabitat(int points, boolean below, int light) implements Habitat 11 | { 12 | public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(func -> func.group( 13 | Habitat.withPoints(3, LightHabitat::points), 14 | Codec.BOOL.optionalFieldOf("below", false).forGetter(LightHabitat::below), 15 | Codec.INT.fieldOf("light").forGetter(LightHabitat::light) 16 | ).apply(func, LightHabitat::new)); 17 | 18 | @Override 19 | public int getHabitatPoints(Level level, BlockPos pos) 20 | { 21 | int lightEmission = level.getLightEmission(pos); 22 | return (below? lightEmission < light : lightEmission > light)? points : 0; 23 | } 24 | 25 | @Override 26 | public MapCodec codec() 27 | { 28 | return CODEC; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/util/CircularBuffer.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.util; 2 | 3 | import net.minecraft.util.Mth; 4 | 5 | import java.util.Arrays; 6 | 7 | /** 8 | * Very simple fixed size circular buffer implementation for animation purposes. 9 | * 10 | * @author Nico Bergemann 11 | */ 12 | public class CircularBuffer 13 | { 14 | private final float[] buffer; 15 | private int index = 0; 16 | 17 | public CircularBuffer(int size) 18 | { 19 | buffer = new float[size]; 20 | } 21 | 22 | public void fill(float value) 23 | { 24 | Arrays.fill(buffer, value); 25 | } 26 | 27 | public void update(float value) 28 | { 29 | // move forward 30 | index++; 31 | 32 | // restart pointer at the end to form a virtual ring 33 | index %= buffer.length; 34 | 35 | buffer[index] = value; 36 | } 37 | 38 | public float get(float x, int offset) 39 | { 40 | int i = index - offset; 41 | int len = buffer.length - 1; 42 | return Mth.clampedLerp(buffer[i - 1 & len], buffer[i & len], x); 43 | } 44 | 45 | public float get(float x, int offset1, int offset2) 46 | { 47 | return get(x, offset2) - get(x, offset1); 48 | } 49 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/mixins/EnsureSafeFlyingVehicleMixin.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.mixins; 2 | 3 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 4 | import net.minecraft.world.entity.animal.FlyingAnimal; 5 | import org.objectweb.asm.Opcodes; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Redirect; 10 | 11 | @Mixin(ServerGamePacketListenerImpl.class) 12 | public class EnsureSafeFlyingVehicleMixin 13 | { 14 | @Shadow 15 | private boolean clientVehicleIsFloating; 16 | 17 | /** 18 | * Purpose: to ensure players who ride flight-capable vehicles don't get kicked for flying 19 | * without needing to compromise server security by disabling "Kick for flying" 20 | */ 21 | @Redirect(method = "handleMoveVehicle(Lnet/minecraft/network/protocol/game/ServerboundMoveVehiclePacket;)V", at = @At(value = "FIELD", target = "Lnet/minecraft/server/network/ServerGamePacketListenerImpl;clientVehicleIsFloating:Z", opcode = Opcodes.PUTFIELD)) 22 | private void dragonmounts_ensureSafeFlyingVehicle(ServerGamePacketListenerImpl impl, boolean flag) 23 | { 24 | clientVehicleIsFloating = (!(impl.getPlayer().getRootVehicle() instanceof FlyingAnimal a) || !a.isFlying()) && flag; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/egg/habitats/FluidHabitat.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.egg.habitats; 2 | 3 | import com.mojang.serialization.MapCodec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.core.registries.Registries; 7 | import net.minecraft.resources.ResourceLocation; 8 | import net.minecraft.tags.TagKey; 9 | import net.minecraft.world.level.Level; 10 | import net.minecraft.world.level.material.Fluid; 11 | 12 | public record FluidHabitat(float multiplier, TagKey fluidType) implements Habitat 13 | { 14 | public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( 15 | Habitat.withMultiplier(0.5f, FluidHabitat::multiplier), 16 | TagKey.codec(Registries.FLUID).fieldOf("fluid_tag").forGetter(FluidHabitat::fluidType) 17 | ).apply(instance, FluidHabitat::new)); 18 | 19 | @Override 20 | public int getHabitatPoints(Level level, BlockPos pos) 21 | { 22 | return (int) (BlockPos.betweenClosedStream(pos.offset(1, 1, 1), pos.offset(-1, -1, -1)) 23 | .filter(p -> level.getFluidState(p).is(fluidType)) 24 | .count() * multiplier); 25 | } 26 | 27 | @Override 28 | public MapCodec codec() 29 | { 30 | return CODEC; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/egg/habitats/NearbyBlocksHabitat.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.egg.habitats; 2 | 3 | import com.mojang.serialization.MapCodec; 4 | import com.mojang.serialization.codecs.RecordCodecBuilder; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.core.registries.Registries; 7 | import net.minecraft.resources.ResourceLocation; 8 | import net.minecraft.tags.TagKey; 9 | import net.minecraft.world.level.Level; 10 | import net.minecraft.world.level.block.Block; 11 | 12 | public record NearbyBlocksHabitat(float multiplier, TagKey tag) implements Habitat 13 | { 14 | public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( 15 | Habitat.withMultiplier(0.5f, NearbyBlocksHabitat::multiplier), 16 | TagKey.codec(Registries.BLOCK).fieldOf("block_tag").forGetter(NearbyBlocksHabitat::tag) 17 | ).apply(instance, NearbyBlocksHabitat::new)); 18 | 19 | @Override 20 | public int getHabitatPoints(Level level, BlockPos basePos) 21 | { 22 | return (int) (BlockPos.betweenClosedStream(basePos.offset(1, 1, 1), basePos.offset(-1, -1, -1)) 23 | .filter(p -> level.getBlockState(p).is(tag)) 24 | .count() * multiplier); 25 | } 26 | 27 | @Override 28 | public MapCodec codec() 29 | { 30 | return CODEC; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/mixins/ReplenishDragonEggMixin.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.mixins; 2 | 3 | import com.github.kay9.dragonmounts.DMLConfig; 4 | import net.minecraft.world.level.dimension.end.EndDragonFight; 5 | import org.objectweb.asm.Opcodes; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Redirect; 9 | 10 | @Mixin(EndDragonFight.class) 11 | public class ReplenishDragonEggMixin 12 | { 13 | /** 14 | * Purpose: To implement a way to replenish dragon eggs after death of the ender dragon 15 | *
16 | * This mixin redirects the 'if' statement in setDragonKilled that tests if the ender dragon was previously killed 17 | * essentially, instead of {@code if (!previouslyKilled) {...}}, 18 | * we do {@code if (!dragonmounts_replenishDragonEgg) {...}} 19 | */ 20 | @Redirect(method = "setDragonKilled(Lnet/minecraft/world/entity/boss/enderdragon/EnderDragon;)V", at = @At(value = "FIELD", target = "Lnet/minecraft/world/level/dimension/end/EndDragonFight;previouslyKilled:Z", opcode = Opcodes.GETFIELD)) 21 | private boolean dragonmounts_replenishDragonEgg(EndDragonFight instance) 22 | { 23 | return !(!instance.hasPreviouslyKilledDragon() || DMLConfig.replenishEggs()); // return the inverse of what we want because the target check inverts the result... yeah. 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/data/providers/DataProvider.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.data.providers; 2 | 3 | import com.github.kay9.dragonmounts.DragonMountsLegacy; 4 | import net.minecraftforge.data.event.GatherDataEvent; 5 | import net.minecraftforge.eventbus.api.SubscribeEvent; 6 | import net.minecraftforge.fml.common.Mod; 7 | 8 | @Mod.EventBusSubscriber(modid = DragonMountsLegacy.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) 9 | public class DataProvider 10 | { 11 | @SubscribeEvent 12 | public static void gather(GatherDataEvent evt) 13 | { 14 | var gen = evt.getGenerator(); 15 | var output = evt.getGenerator().getPackOutput(); 16 | var lookup = evt.getLookupProvider(); 17 | var fileHelper = evt.getExistingFileHelper(); 18 | 19 | var breedProvider = new DragonBreedProvider(output, lookup); // necessary to grab the newly populated registry provider for loot tables 20 | lookup = breedProvider.getFullRegistries(); 21 | gen.addProvider(evt.includeServer(), breedProvider); 22 | gen.addProvider(evt.includeServer(), new BlockTagProvider(output, lookup, DragonMountsLegacy.MOD_ID, fileHelper)); 23 | gen.addProvider(evt.includeServer(), new LootModifierProvider(output, DragonMountsLegacy.MOD_ID, lookup)); 24 | gen.addProvider(evt.includeServer(), new LootTableProvider(output, lookup)); 25 | 26 | gen.addProvider(evt.includeClient(), new ModelPropertiesProvider(gen)); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/abilities/FootprintAbility.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.abilities; 2 | 3 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraftforge.event.ForgeEventFactory; 6 | 7 | public abstract class FootprintAbility implements Ability 8 | { 9 | @Override 10 | public void onMove(TameableDragon dragon) 11 | { 12 | if (dragon.getAgeProgress() < 0.5 || !dragon.onGround()) return; 13 | if (!ForgeEventFactory.getMobGriefingEvent(dragon.level(), dragon)) return; 14 | 15 | var chance = getFootprintChance(dragon); 16 | if (chance == 0) return; 17 | 18 | for (int i = 0; i < 4; i++) 19 | { 20 | // place only if randomly selected 21 | if (dragon.getRandom().nextFloat() > chance) continue; 22 | 23 | // get footprint position 24 | int bx = (int) (dragon.getX() + (i % 2 * 2 - 1) * dragon.getAgeScale()); 25 | int by = (int) dragon.getY(); 26 | int bz = (int) (dragon.getZ() + (i / 2f % 2 * 2 - 1) * dragon.getAgeScale()); 27 | var pos = new BlockPos(bx, by, bz); 28 | 29 | placeFootprint(dragon, pos); 30 | } 31 | } 32 | 33 | protected float getFootprintChance(TameableDragon dragon) 34 | { 35 | return 0.05f; 36 | } 37 | 38 | protected abstract void placeFootprint(TameableDragon dragon, BlockPos pos); 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/data/loot/conditions/RandomChanceByConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.data.loot.conditions; 2 | 3 | import com.github.kay9.dragonmounts.DMLConfig; 4 | import com.github.kay9.dragonmounts.DMLRegistry; 5 | import com.mojang.serialization.Codec; 6 | import com.mojang.serialization.MapCodec; 7 | import net.minecraft.world.level.storage.loot.LootContext; 8 | import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; 9 | import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType; 10 | 11 | public class RandomChanceByConfig implements LootItemCondition 12 | { 13 | public static final MapCodec CODEC = Codec.STRING.xmap(RandomChanceByConfig::new, t -> t.configTargetID) 14 | .fieldOf("config_chance_target"); 15 | 16 | private final String configTargetID; 17 | 18 | public RandomChanceByConfig(String forTarget) 19 | { 20 | this.configTargetID = forTarget; 21 | } 22 | 23 | @Override 24 | public LootItemConditionType getType() 25 | { 26 | return DMLRegistry.RANDOM_CHANCE_CONFIG_CONDITION.get(); 27 | } 28 | 29 | @Override 30 | public boolean test(LootContext lootContext) 31 | { 32 | if (!DMLConfig.useLootTables()) return false; 33 | 34 | // non-existing config targets fail silently with probability -1f 35 | return lootContext.getRandom().nextFloat() < DMLConfig.getEggChanceFor(configTargetID); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /example_addon/data/example_addon/dragonmounts/dragon_breeds/example_breed.json: -------------------------------------------------------------------------------- 1 | { 2 | "primary_color": "24773E", 3 | "secondary_color": "3FDCDB", 4 | "hatch_particles": { 5 | "type": "minecraft:heart" 6 | }, 7 | "attributes": { 8 | "minecraft:generic.armor": 10.0 9 | }, 10 | "abilities": [ 11 | { 12 | "type": "dragonmounts:snow_stepper" 13 | }, 14 | { 15 | "type": "dragonmounts:green_toes" 16 | } 17 | ], 18 | "habitats": [ 19 | { 20 | "type": "dragonmounts:light", 21 | "points": 2, 22 | "light": 10, 23 | "below": false 24 | }, 25 | { 26 | "type": "dragonmounts:world_height", 27 | "points": 1, 28 | "height": 60, 29 | "below": false 30 | }, 31 | { 32 | "type": "dragonmounts:picky", 33 | "required_habitats": [ 34 | { 35 | "type": "dragonmounts:biome", 36 | "points": 1, 37 | "biome_tag": "minecraft:is_beach" 38 | }, 39 | { 40 | "type": "dragonmounts:nearby_blocks", 41 | "point_multiplier": 0.5, 42 | "block_tag": "minecraft:sand" 43 | } 44 | ] 45 | } 46 | ], 47 | "immunities": [ 48 | "minecraft:cactus", 49 | "minecraft:sweet_berry_bush", 50 | "minecraft:dragon_breath", 51 | "minecraft:magic" 52 | ], 53 | "ambient_sound": "example_addon:entity.dragon.ambient.example", 54 | "death_loot": "minecraft:chests/bastion_treasure", 55 | "growth_time": 120, 56 | "hatch_chance": 1.0, 57 | "size_modifier": 2, 58 | "taming_items": "#forge:eggs", 59 | "breeding_items": "#forge:ender_pearls", 60 | "reproduction_limit": 12 61 | } -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/sounds.json: -------------------------------------------------------------------------------- 1 | { 2 | "entity.dragon.ambient": { 3 | "sounds": [ 4 | { 5 | "name": "dragonmounts:entity/dragon/breathe1", 6 | "volume": 0.5, 7 | "pitch": 0.5, 8 | "weight": 3 9 | }, 10 | { 11 | "name": "dragonmounts:entity/dragon/breathe2", 12 | "volume": 0.5, 13 | "pitch": 0.5, 14 | "weight": 3 15 | }, 16 | { 17 | "name": "minecraft:entity.ender_dragon.growl", 18 | "type": "event" 19 | } 20 | ], 21 | "subtitle": "subtitles.entity.dragon.ambient" 22 | }, 23 | "entity.dragon.step": { 24 | "sounds": [ 25 | { 26 | "name": "dragonmounts:entity/dragon/step1" 27 | }, 28 | { 29 | "name": "dragonmounts:entity/dragon/step2" 30 | }, 31 | { 32 | "name": "dragonmounts:entity/dragon/step3" 33 | }, 34 | { 35 | "name": "dragonmounts:entity/dragon/step4" 36 | } 37 | ], 38 | "subtitle": "subtitles.entity.dragon.step" 39 | }, 40 | "entity.dragon.death": { 41 | "sounds": [ 42 | { 43 | "name": "dragonmounts:entity/dragon/death" 44 | } 45 | ], 46 | "subtitle": "subtitles.entity.dragon.death" 47 | }, 48 | "entity.dragon.ambient.ghost": { 49 | "sounds": [ 50 | { 51 | "name": "dragonmounts:entity.dragon.ambient", 52 | "type": "event" 53 | }, 54 | { 55 | "name": "minecraft:entity.skeleton.ambient", 56 | "volume": 0.35, 57 | "pitch": 0.5, 58 | "type": "event" 59 | } 60 | ], 61 | "subtitle": "subtitles.entity.dragon.ambient.ghost" 62 | } 63 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/client/MountCameraManager.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.client; 2 | 3 | import com.github.kay9.dragonmounts.DMLConfig; 4 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 5 | import net.minecraft.client.Camera; 6 | import net.minecraft.client.CameraType; 7 | import net.minecraft.client.Minecraft; 8 | 9 | public class MountCameraManager 10 | { 11 | private static CameraType previousPerspective = CameraType.FIRST_PERSON; 12 | 13 | public static void onDragonMount() 14 | { 15 | if (DMLConfig.thirdPersonOnMount()) 16 | { 17 | previousPerspective = Minecraft.getInstance().options.getCameraType(); 18 | Minecraft.getInstance().options.setCameraType(CameraType.THIRD_PERSON_BACK); 19 | } 20 | } 21 | 22 | public static void onDragonDismount() 23 | { 24 | if (DMLConfig.thirdPersonOnMount()) 25 | Minecraft.getInstance().options.setCameraType(previousPerspective); 26 | } 27 | 28 | @SuppressWarnings("ConstantConditions") // player should never be null at time of calling 29 | public static void setMountCameraAngles(Camera camera) 30 | { 31 | if (Minecraft.getInstance().player.getVehicle() instanceof TameableDragon && !Minecraft.getInstance().options.getCameraType().isFirstPerson()) 32 | { 33 | var offsets = DMLConfig.getCameraPerspectiveOffset(Minecraft.getInstance().options.getCameraType() == CameraType.THIRD_PERSON_BACK); 34 | camera.move(0, offsets[1].get().floatValue(), offsets[2].get().floatValue()); 35 | camera.move(-camera.getMaxZoom(offsets[0].get().floatValue()), 0, 0); // do distance calcs AFTER our new position is set 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/mixins/client/ModelPartMixin.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.mixins.client; 2 | 3 | import com.github.kay9.dragonmounts.accessors.ModelPartAccess; 4 | import com.mojang.blaze3d.vertex.PoseStack; 5 | import net.minecraft.client.model.geom.ModelPart; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Unique; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | @Mixin(ModelPart.class) 13 | public class ModelPartMixin implements ModelPartAccess 14 | { 15 | @Unique public float dm_xScale = 1; 16 | @Unique public float dm_yScale = 1; 17 | @Unique public float dm_zScale = 1; 18 | 19 | @Inject(method = "translateAndRotate(Lcom/mojang/blaze3d/vertex/PoseStack;)V", at = @At(value = "TAIL")) 20 | public void dragonmounts_scalePoseStack(PoseStack pPoseStack, CallbackInfo cbi) 21 | { 22 | pPoseStack.scale(dm_xScale, dm_yScale, dm_zScale); 23 | } 24 | 25 | @Override 26 | public float getXScale() 27 | { 28 | return dm_xScale; 29 | } 30 | 31 | @Override 32 | public float getYScale() 33 | { 34 | return dm_yScale; 35 | } 36 | 37 | @Override 38 | public float getZScale() 39 | { 40 | return dm_zScale; 41 | } 42 | 43 | @Override 44 | public void setXScale(float x) 45 | { 46 | this.dm_xScale = x; 47 | } 48 | 49 | @Override 50 | public void setYScale(float y) 51 | { 52 | this.dm_yScale = y; 53 | } 54 | 55 | @Override 56 | public void setZScale(float z) 57 | { 58 | this.dm_zScale = z; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/abilities/HotFeetAbility.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.abilities; 2 | 3 | import com.github.kay9.dragonmounts.DragonMountsLegacy; 4 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 5 | import com.mojang.serialization.MapCodec; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.core.particles.ParticleTypes; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.sounds.SoundEvents; 10 | import net.minecraft.tags.BlockTags; 11 | import net.minecraft.tags.TagKey; 12 | import net.minecraft.world.level.block.Block; 13 | 14 | public class HotFeetAbility extends FootprintAbility implements Ability.Factory 15 | { 16 | public static final HotFeetAbility INSTANCE = new HotFeetAbility(); 17 | public static final MapCodec CODEC = MapCodec.unit(INSTANCE); 18 | 19 | public static final TagKey BURNABLES_TAG = BlockTags.create(DragonMountsLegacy.id("hot_feet_burnables")); 20 | 21 | @Override 22 | protected void placeFootprint(TameableDragon dragon, BlockPos pos) 23 | { 24 | var level = dragon.level(); 25 | var steppingOn = level.getBlockState(pos); 26 | if (steppingOn.is(BURNABLES_TAG)) 27 | { 28 | level.removeBlock(pos, false); 29 | level.playSound(null, pos, SoundEvents.FIRE_EXTINGUISH, dragon.getSoundSource(), 0.1f, 2f); 30 | ((ServerLevel) level).sendParticles(ParticleTypes.LARGE_SMOKE, pos.getX(), pos.getY(), pos.getZ(), 0, 0, 1, 0, 0.05); 31 | } 32 | } 33 | 34 | @Override 35 | public HotFeetAbility create() 36 | { 37 | return this; 38 | } 39 | 40 | @Override 41 | public MapCodec> codec() 42 | { 43 | return CODEC; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/egg/habitats/Habitat.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.egg.habitats; 2 | 3 | import com.github.kay9.dragonmounts.DragonMountsLegacy; 4 | import com.google.common.base.Suppliers; 5 | import com.mojang.serialization.Codec; 6 | import com.mojang.serialization.MapCodec; 7 | import com.mojang.serialization.codecs.RecordCodecBuilder; 8 | import net.minecraft.core.BlockPos; 9 | import net.minecraft.core.Registry; 10 | import net.minecraft.resources.ResourceKey; 11 | import net.minecraft.resources.ResourceLocation; 12 | import net.minecraft.world.level.Level; 13 | import net.minecraftforge.registries.IForgeRegistry; 14 | import net.minecraftforge.registries.RegistryManager; 15 | 16 | import java.util.function.Function; 17 | import java.util.function.Supplier; 18 | 19 | public interface Habitat 20 | { 21 | ResourceKey>> REGISTRY_KEY = ResourceKey.createRegistryKey(DragonMountsLegacy.id("habitat_type")); 22 | Supplier>> REGISTRY = Suppliers.memoize(() -> RegistryManager.ACTIVE.getRegistry(REGISTRY_KEY)); 23 | Codec CODEC = Codec.lazyInitialized(() -> REGISTRY.get().getCodec().dispatch(Habitat::codec, Function.identity())); 24 | 25 | static RecordCodecBuilder withPoints(int defaultTo, Function getter) 26 | { 27 | return Codec.INT.optionalFieldOf("points", defaultTo).forGetter(getter); 28 | } 29 | 30 | static RecordCodecBuilder withMultiplier(float defaultTo, Function getter) 31 | { 32 | return Codec.FLOAT.optionalFieldOf("point_multiplier", defaultTo).forGetter(getter); 33 | } 34 | 35 | int getHabitatPoints(Level level, BlockPos pos); 36 | 37 | MapCodec codec(); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/abilities/SnowStepperAbility.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.abilities; 2 | 3 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 4 | import com.mojang.serialization.MapCodec; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.core.particles.ParticleTypes; 7 | import net.minecraft.server.level.ServerLevel; 8 | import net.minecraft.world.level.block.Blocks; 9 | 10 | public class SnowStepperAbility extends FootprintAbility implements Ability.Factory 11 | { 12 | public static final SnowStepperAbility INSTANCE = new SnowStepperAbility(); 13 | public static final MapCodec CODEC = MapCodec.unit(INSTANCE); 14 | 15 | @Override 16 | protected void placeFootprint(TameableDragon dragon, BlockPos pos) 17 | { 18 | var level = dragon.level(); 19 | var state = Blocks.SNOW.defaultBlockState(); 20 | if (level.getBlockState(pos).isAir() && state.canSurvive(level, pos)) 21 | { 22 | level.setBlockAndUpdate(pos, state); 23 | ((ServerLevel) level).sendParticles(ParticleTypes.SNOWFLAKE, 24 | pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, 25 | dragon.getRandom().nextInt(6) + 2, 26 | 0.5, 0.5, 0.5, 0); 27 | } 28 | } 29 | 30 | @Override 31 | protected float getFootprintChance(TameableDragon dragon) 32 | { 33 | var pos = dragon.blockPosition(); 34 | return dragon.level().getBiome(pos).value().coldEnoughToSnow(pos)? 0.5f : 0; 35 | } 36 | 37 | @Override 38 | public SnowStepperAbility create() 39 | { 40 | return this; 41 | } 42 | 43 | @Override 44 | public MapCodec> codec() 45 | { 46 | return CODEC; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/client/MountControlsMessenger.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.client; 2 | 3 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.network.chat.Component; 6 | 7 | /** 8 | * A heavily hardcoded class to display a message after the dismount controls display when a player mounts a dragon. 9 | * {@link MountControlsMessenger#sendControlsMessage()} is called from a dragon when the LocalPlayer mounts. 10 | * Messages include information about flight controls, such as how to ascend/descend. 11 | * A hardcoded design was decided as an expanded functionality doesn't really make any sense for the 12 | * direction of the mod, and would instead be wasted resources. 13 | */ 14 | public class MountControlsMessenger 15 | { 16 | private static int delay = 0; 17 | 18 | public static void sendControlsMessage() 19 | { 20 | // the length the initial "dismount" message is displayed for, in ticks. 21 | // Our message displays after 60 ticks (after the dismount message.) 22 | // taken from Gui#setOverlayMessage. 23 | delay = 60; 24 | } 25 | 26 | @SuppressWarnings("ConstantConditions") 27 | public static void tick() 28 | { 29 | if (delay > 0) 30 | { 31 | var player = Minecraft.getInstance().player; 32 | if (!(player.getVehicle() instanceof TameableDragon)) 33 | { 34 | delay = 0; 35 | return; 36 | } 37 | 38 | --delay; 39 | 40 | if (delay == 0) 41 | player.displayClientMessage(Component.translatable("mount.dragon.vertical_controls", 42 | Minecraft.getInstance().options.keyJump.getTranslatedKeyMessage(), 43 | KeyMappings.FLIGHT_DESCENT_KEY.getTranslatedKeyMessage()), true); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/data/providers/ModelPropertiesProvider.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.data.providers; 2 | 3 | import com.github.kay9.dragonmounts.DragonMountsLegacy; 4 | import com.github.kay9.dragonmounts.client.DragonModel; 5 | import com.github.kay9.dragonmounts.dragon.DragonBreed; 6 | import com.google.gson.JsonParseException; 7 | import com.mojang.serialization.JsonOps; 8 | import net.minecraft.data.CachedOutput; 9 | import net.minecraft.data.DataGenerator; 10 | import net.minecraft.data.DataProvider; 11 | 12 | import java.util.concurrent.CompletableFuture; 13 | 14 | public class ModelPropertiesProvider implements DataProvider 15 | { 16 | private final DataGenerator gen; 17 | 18 | public ModelPropertiesProvider(DataGenerator gen) 19 | { 20 | this.gen = gen; 21 | } 22 | 23 | @Override 24 | public CompletableFuture run(CachedOutput pOutput) 25 | { 26 | return CompletableFuture.allOf( 27 | save(pOutput, DragonBreed.BuiltIn.FIRE.location().getPath(), new DragonModel.Properties(false, false, false)), 28 | save(pOutput, DragonBreed.BuiltIn.GHOST.location().getPath(), new DragonModel.Properties(true, false, true)), 29 | save(pOutput, DragonBreed.BuiltIn.WATER.location().getPath(), new DragonModel.Properties(true, true, false))); 30 | } 31 | 32 | private CompletableFuture save(CachedOutput cache, String id, DragonModel.Properties instance) 33 | { 34 | var jsonObj = DragonModel.Properties.CODEC.encodeStart(JsonOps.INSTANCE, instance).getOrThrow(JsonParseException::new); 35 | var path = gen.getPackOutput().getOutputFolder().resolve("assets/" + DragonMountsLegacy.MOD_ID + "/models/entity/dragon/breed/properties/" + id + ".json"); 36 | return DataProvider.saveStable(cache, jsonObj, path); 37 | } 38 | 39 | @Override 40 | public String getName() 41 | { 42 | return DragonMountsLegacy.MOD_ID + ": Dragon Model Properties"; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/client/KeyMappings.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.client; 2 | 3 | import com.github.kay9.dragonmounts.DMLConfig; 4 | import com.github.kay9.dragonmounts.DragonMountsLegacy; 5 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 6 | import net.minecraft.client.KeyMapping; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.player.LocalPlayer; 9 | import net.minecraft.network.chat.Component; 10 | import org.lwjgl.glfw.GLFW; 11 | 12 | import java.util.function.Consumer; 13 | 14 | public class KeyMappings 15 | { 16 | private static final String KEY_CATEGORY = "key.category." + DragonMountsLegacy.MOD_ID; 17 | 18 | public static final KeyMapping FLIGHT_DESCENT_KEY = keymap("flight_descent", GLFW.GLFW_KEY_Z); 19 | public static final KeyMapping CAMERA_CONTROLS = keymap("camera_flight", GLFW.GLFW_KEY_F6); 20 | 21 | @SuppressWarnings({"ConstantConditions"}) 22 | private static KeyMapping keymap(String name, int defaultMapping) 23 | { 24 | return new KeyMapping(String.format("key.%s.%s", DragonMountsLegacy.MOD_ID, name), defaultMapping, KEY_CATEGORY); 25 | } 26 | 27 | public static void registerKeybinds(Consumer registrar) 28 | { 29 | registrar.accept(FLIGHT_DESCENT_KEY); 30 | registrar.accept(CAMERA_CONTROLS); 31 | } 32 | 33 | public static void handleKeyPress(int key, int action) 34 | { 35 | LocalPlayer player = Minecraft.getInstance().player; 36 | if (key == CAMERA_CONTROLS.getKey().getValue() 37 | && action == GLFW.GLFW_PRESS 38 | && player != null 39 | && player.getVehicle() instanceof TameableDragon d) 40 | { 41 | DMLConfig.CAMERA_DRIVEN_FLIGHT.set(!DMLConfig.cameraDrivenFlight()); 42 | player.displayClientMessage(Component.translatable("mount.dragon.camera_controls." + (DMLConfig.cameraDrivenFlight()? "enabled" : "disabled"), d.getDisplayName()), true); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/ai/DragonMoveController.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.ai; 2 | 3 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 4 | import net.minecraft.util.Mth; 5 | import net.minecraft.world.entity.ai.attributes.Attributes; 6 | import net.minecraft.world.entity.ai.control.MoveControl; 7 | 8 | public class DragonMoveController extends MoveControl 9 | { 10 | private final TameableDragon dragon; 11 | 12 | public DragonMoveController(TameableDragon dragon) 13 | { 14 | super(dragon); 15 | this.dragon = dragon; 16 | } 17 | 18 | @Override 19 | public void tick() 20 | { 21 | // original movement behavior if the entity isn't flying 22 | if (!dragon.isFlying()) 23 | { 24 | super.tick(); 25 | return; 26 | } 27 | 28 | if (operation == MoveControl.Operation.MOVE_TO) 29 | { 30 | operation = MoveControl.Operation.WAIT; 31 | double xDif = wantedX - mob.getX(); 32 | double yDif = wantedY - mob.getY(); 33 | double zDif = wantedZ - mob.getZ(); 34 | double sq = xDif * xDif + yDif * yDif + zDif * zDif; 35 | if (sq < (double) 2.5000003E-7F) 36 | { 37 | mob.setYya(0.0F); 38 | mob.setZza(0.0F); 39 | return; 40 | } 41 | 42 | float speed = (float) (speedModifier * mob.getAttributeValue(Attributes.FLYING_SPEED)); 43 | double distSq = Math.sqrt(xDif * xDif + zDif * zDif); 44 | mob.setSpeed(speed); 45 | if (Math.abs(yDif) > (double) 1.0E-5F || Math.abs(distSq) > (double) 1.0E-5F) 46 | mob.setYya((float) yDif * speed); 47 | 48 | float yaw = (float) (Mth.atan2(zDif, xDif) * (double) (180F / (float) Math.PI)) - 90.0F; 49 | mob.setYRot(rotlerp(mob.getYRot(), yaw, 6)); 50 | } 51 | else 52 | { 53 | mob.setYya(0); 54 | mob.setZza(0); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/data/providers/LootModifierProvider.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.data.providers; 2 | 3 | import com.github.kay9.dragonmounts.DMLConfig; 4 | import com.github.kay9.dragonmounts.data.loot.DragonEggLootMod; 5 | import com.github.kay9.dragonmounts.data.loot.conditions.RandomChanceByConfig; 6 | import com.github.kay9.dragonmounts.dragon.DragonBreed; 7 | import net.minecraft.core.Holder; 8 | import net.minecraft.core.HolderLookup; 9 | import net.minecraft.data.PackOutput; 10 | import net.minecraft.resources.ResourceKey; 11 | import net.minecraft.world.level.storage.loot.LootTable; 12 | import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; 13 | import net.minecraftforge.common.data.GlobalLootModifierProvider; 14 | import net.minecraftforge.common.loot.LootTableIdCondition; 15 | 16 | import java.util.concurrent.CompletableFuture; 17 | 18 | class LootModifierProvider extends GlobalLootModifierProvider 19 | { 20 | LootModifierProvider(PackOutput output, String modid, CompletableFuture registries) 21 | { 22 | super(output, modid, registries); 23 | } 24 | 25 | @Override 26 | protected void start(HolderLookup.Provider registry) 27 | { 28 | for (var target : DragonEggLootMod.BUILT_IN_CHANCES) 29 | addWithConfigChance(target.forBreed(), target.target(), registry); 30 | } 31 | 32 | private void addWithConfigChance(ResourceKey breedId, ResourceKey table, HolderLookup.Provider registry) 33 | { 34 | var conditions = new LootItemCondition[]{ 35 | LootTableIdCondition.builder(table.location()).build(), 36 | new RandomChanceByConfig(DMLConfig.formatEggTargetAsPath(breedId, table)) // Automatically formats the given inputs to point the chance values to the corresponding config entry 37 | }; 38 | 39 | var path = String.join("/", breedId.location().getNamespace(), breedId.location().getPath(), table.location().getPath()); 40 | Holder breed = DragonBreed.get(breedId, registry); 41 | super.add(path, new DragonEggLootMod(conditions, breed, false)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/lang/ja_jp.json: -------------------------------------------------------------------------------- 1 | { 2 | "entity.dragonmounts.dragon": "ドラゴン", 3 | 4 | "dragon_breed.dragonmounts.aether": "エーテルドラゴン", 5 | "dragon_breed.dragonmounts.fire": "ファイアドラゴン", 6 | "dragon_breed.dragonmounts.end": "エンドドラゴン", 7 | "dragon_breed.dragonmounts.forest": "フォレスとドラゴン", 8 | "dragon_breed.dragonmounts.ghost": "ゴーストドラゴン", 9 | "dragon_breed.dragonmounts.ice": "アイスドラゴン", 10 | "dragon_breed.dragonmounts.nether": "ネザードラゴン", 11 | "dragon_breed.dragonmounts.water": "ウォータードラゴン", 12 | 13 | "item.dragonmounts.spawn_egg.dragonmounts.aether": "エーテルドラゴンのスポーンエッグ", 14 | "item.dragonmounts.spawn_egg.dragonmounts.fire": "ファイアドラゴンのスポーンエッグ", 15 | "item.dragonmounts.spawn_egg.dragonmounts.end": "エンドドラゴンのスポーンエッグ", 16 | "item.dragonmounts.spawn_egg.dragonmounts.forest": "フォレストドラゴンのスポーンエッグ", 17 | "item.dragonmounts.spawn_egg.dragonmounts.ghost": "ゴーストドラゴンのスポーンエッグ", 18 | "item.dragonmounts.spawn_egg.dragonmounts.ice": "アイスドラゴンのスポーンエッグ", 19 | "item.dragonmounts.spawn_egg.dragonmounts.nether": "ネザードラゴンのスポーンエッグ", 20 | "item.dragonmounts.spawn_egg.dragonmounts.water": "ウォータードラゴンのスポーンエッグ", 21 | 22 | "block.dragonmounts.dragon_egg.remaining_time": "%s秒以内に孵化します。", 23 | "block.dragonmounts.dragon_egg.change_breeds": "クリエイティブモード:ドラゴンと対話することでドラゴンの品種がこの卵の品種に代わります。", 24 | 25 | "block.dragonmounts.dragon_egg.dragonmounts.aether": "エーテルドラゴンの卵", 26 | "block.dragonmounts.dragon_egg.dragonmounts.fire": "ファイアドラゴンの卵", 27 | "block.dragonmounts.dragon_egg.dragonmounts.end": "エンドドラゴンの卵", 28 | "block.dragonmounts.dragon_egg.dragonmounts.forest": "フォレストドラゴンの卵", 29 | "block.dragonmounts.dragon_egg.dragonmounts.ghost": "ゴーストドラゴンの卵", 30 | "block.dragonmounts.dragon_egg.dragonmounts.ice": "アイスドラゴンの卵", 31 | "block.dragonmounts.dragon_egg.dragonmounts.nether": "ネザードラゴンの卵", 32 | "block.dragonmounts.dragon_egg.dragonmounts.water": "ウォータードラゴンの卵", 33 | 34 | "key.dragonmounts.flight_descent": "Dragon Mount フライト降下", 35 | "key.dragonmounts.camera_flight": "Dragon Mount フライトコマンド", 36 | 37 | "mount.dragon.vertical_controls": "%sで上昇し%sで降下します", 38 | "mount.dragon.camera_controls.enabled": "%sはあなたの方へ向かって上昇していきます", 39 | "mount.dragon.camera_controls.disabled": "%sはあなたのコマンドでのみ上昇するようになりました", 40 | 41 | "subtitles.entity.dragon.ambient": "ドラゴンが話す", 42 | "subtitles.entity.dragon.step": "ドラゴンが踏み鳴らす", 43 | "subtitles.entity.dragon.death": "ドラゴンの金切り声", 44 | "subtitles.entity.dragon.ambient.ghost": "ドラゴンが話す" 45 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/data/model/DragonModelPropertiesListener.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.data.model; 2 | 3 | import com.github.kay9.dragonmounts.client.DragonModel; 4 | import com.github.kay9.dragonmounts.client.DragonRenderer; 5 | import com.google.gson.GsonBuilder; 6 | import com.google.gson.JsonElement; 7 | import com.google.gson.JsonParseException; 8 | import com.mojang.serialization.JsonOps; 9 | import net.minecraft.client.model.geom.ModelLayerLocation; 10 | import net.minecraft.resources.ResourceLocation; 11 | import net.minecraft.server.packs.resources.ResourceManager; 12 | import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; 13 | import net.minecraft.util.profiling.ProfilerFiller; 14 | import net.minecraftforge.client.ForgeHooksClient; 15 | 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | public class DragonModelPropertiesListener extends SimpleJsonResourceReloadListener 20 | { 21 | public static final DragonModelPropertiesListener INSTANCE = new DragonModelPropertiesListener(); 22 | 23 | private static final String FOLDER = "models/entity/dragon/breed/properties"; 24 | 25 | private final Map definitions = new HashMap<>(3); 26 | 27 | public DragonModelPropertiesListener() 28 | { 29 | super(new GsonBuilder().create(), FOLDER); 30 | } 31 | 32 | @Override 33 | protected void apply(Map map, ResourceManager pResourceManager, ProfilerFiller pProfiler) 34 | { 35 | definitions.clear(); 36 | 37 | for (var entry : map.entrySet()) 38 | { 39 | var breedId = entry.getKey(); 40 | var properties = DragonModel.Properties.CODEC.parse(JsonOps.INSTANCE, entry.getValue()).getOrThrow(JsonParseException::new); 41 | var modelLoc = new ModelLayerLocation(DragonRenderer.MODEL_LOCATION.getModel(), breedId.toString()); 42 | ForgeHooksClient.registerLayerDefinition(modelLoc, () -> DragonModel.createBodyLayer(properties)); 43 | definitions.put(entry.getKey(), modelLoc); 44 | } 45 | } 46 | 47 | /** 48 | * Gets and clears this listener's model definitions. 49 | */ 50 | public Map pollDefinitions() 51 | { 52 | var map = Map.copyOf(definitions); 53 | definitions.clear(); 54 | return map; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/ai/DragonBreedGoal.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.ai; 2 | 3 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 4 | import net.minecraft.server.level.ServerLevel; 5 | import net.minecraft.world.entity.ExperienceOrb; 6 | import net.minecraft.world.entity.ai.goal.BreedGoal; 7 | import net.minecraft.world.level.GameRules; 8 | import net.minecraftforge.common.MinecraftForge; 9 | import net.minecraftforge.event.entity.living.BabyEntitySpawnEvent; 10 | 11 | import java.util.List; 12 | 13 | public class DragonBreedGoal extends BreedGoal 14 | { 15 | private final TameableDragon dragon; 16 | 17 | public DragonBreedGoal(TameableDragon animal) 18 | { 19 | super(animal, 1); 20 | this.dragon = animal; 21 | } 22 | 23 | @Override 24 | public boolean canUse() 25 | { 26 | if (!dragon.isAdult()) return false; 27 | if (!dragon.isInLove()) return false; 28 | else return (partner = getNearbyMate()) != null; 29 | } 30 | 31 | public TameableDragon getNearbyMate() 32 | { 33 | List list = level.getEntitiesOfClass(TameableDragon.class, dragon.getBoundingBox().inflate(8d)); 34 | double dist = Double.MAX_VALUE; 35 | TameableDragon closest = null; 36 | 37 | for (TameableDragon entity : list) 38 | { 39 | if (dragon.canMate(entity) && dragon.distanceToSqr(entity) < dist) 40 | { 41 | closest = entity; 42 | dist = dragon.distanceToSqr(entity); 43 | } 44 | } 45 | 46 | return closest; 47 | } 48 | 49 | @Override 50 | protected void breed() 51 | { 52 | // Respect Mod compatibility 53 | if (MinecraftForge.EVENT_BUS.post(new BabyEntitySpawnEvent(animal, partner, null))) 54 | { 55 | // Reset the "inLove" state for the animals 56 | animal.setAge(6000); 57 | partner.setAge(6000); 58 | return; 59 | } 60 | 61 | animal.resetLove(); 62 | partner.resetLove(); 63 | dragon.spawnChildFromBreeding((ServerLevel) level, partner); 64 | level.broadcastEntityEvent(this.animal, (byte) 18); 65 | if (level.getGameRules().getBoolean(GameRules.RULE_DOMOBLOOT)) 66 | level.addFreshEntity(new ExperienceOrb(level, animal.getX(), animal.getY(), animal.getZ(), animal.getRandom().nextInt(7) + 1)); 67 | } 68 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/util/LerpedFloat.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.util; 2 | 3 | import net.minecraft.util.Mth; 4 | 5 | public class LerpedFloat 6 | { 7 | protected float current; 8 | protected float previous; 9 | 10 | public LerpedFloat() 11 | { 12 | current = previous = 0; 13 | } 14 | 15 | public LerpedFloat(float start) 16 | { 17 | current = previous = start; 18 | } 19 | 20 | public float get(float x) 21 | { 22 | return Mth.clampedLerp(previous, current, x); 23 | } 24 | 25 | public float get() 26 | { 27 | return current; 28 | } 29 | 30 | public void set(float value) 31 | { 32 | sync(); 33 | current = value; 34 | } 35 | 36 | public void add(float value) 37 | { 38 | sync(); 39 | current += value; 40 | } 41 | 42 | public void sync() 43 | { 44 | previous = current; 45 | } 46 | 47 | public float getPrevious() 48 | { 49 | return previous; 50 | } 51 | 52 | public static LerpedFloat.Clamped unit() 53 | { 54 | return new Clamped(0, 1); 55 | } 56 | 57 | /** 58 | * Clamped Implementation. 59 | * Basically just ensure that the value stays clamped within the specified {@link Clamped#min}-{@link Clamped#max} bounds. 60 | */ 61 | public static class Clamped extends LerpedFloat 62 | { 63 | private final float min; 64 | private final float max; 65 | 66 | public Clamped(float start, float min, float max) 67 | { 68 | super(Mth.clamp(start, min, max)); 69 | this.min = min; 70 | this.max = max; 71 | } 72 | 73 | public Clamped(float min, float max) 74 | { 75 | this(0, min, max); 76 | } 77 | 78 | @Override 79 | public void set(float value) 80 | { 81 | super.set(Mth.clamp(value, min, max)); 82 | } 83 | 84 | @Override 85 | public void add(float value) 86 | { 87 | super.add(value); 88 | current = Mth.clamp(current, min, max); 89 | } 90 | 91 | public float getMin() 92 | { 93 | return min; 94 | } 95 | 96 | public float getMax() 97 | { 98 | return max; 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /example_addon/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader="lowcodefml" #mandatory 2 | loaderVersion="[51,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. 3 | license="All rights reserved" 4 | [[mods]] #mandatory 5 | # The modid of the mod 6 | modId="example_addon" #mandatory 7 | # The version number of the mod - there's a few well known ${} variables useable here or just hardcode it 8 | # ${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata 9 | # see the associated build.gradle script for how to populate this completely automatically during a build 10 | version="1.0.0" #mandatory 11 | # A display name for the mod 12 | displayName="Example Breed Addon" #mandatory 13 | # A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/ 14 | #updateJSONURL="https://change.me.example.invalid/updates.json" #optional 15 | # A URL for the "homepage" for this mod, displayed in the mod UI 16 | #displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional 17 | # A file name (in the root of the mod JAR) containing a logo for display 18 | #logoFile="example.png" #optional 19 | # A text field displayed in the mod UI 20 | credits="Thanks for this example mod goes to Java" #optional 21 | # A text field displayed in the mod UI 22 | authors="Love, Cheese and small house plants" #optional 23 | # The description text for the mod (multi line!) (#mandatory) 24 | description=''' 25 | This is a long form description of the mod. You can write whatever you want here 26 | 27 | Have some lorem ipsum. 28 | 29 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed mollis lacinia magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed sagittis luctus odio eu tempus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque volutpat ligula eget lacus auctor sagittis. In hac habitasse platea dictumst. Nunc gravida elit vitae sem vehicula efficitur. Donec mattis ipsum et arcu lobortis, eleifend sagittis sem rutrum. Cras pharetra quam eget posuere fermentum. Sed id tincidunt justo. Lorem ipsum dolor sit amet, consectetur adipiscing elit. 30 | ''' 31 | # Dependencies. Update these alongside the mod. 32 | [[dependencies.example_addon]] 33 | modId="forge" 34 | mandatory=true 35 | versionRange="[51,)" 36 | ordering="NONE" 37 | side="BOTH" 38 | [[dependencies.example_addon]] 39 | modId="minecraft" 40 | mandatory=true 41 | versionRange="[1.21]" 42 | ordering="NONE" 43 | side="BOTH" 44 | [[dependencies.example_addon]] 45 | modId="dragonmounts" 46 | mandatory=true 47 | versionRange="[1.2,)" 48 | ordering="NONE" 49 | side="BOTH" 50 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "entity.dragonmounts.dragon": "Dragon", 3 | 4 | "dragon_breed.dragonmounts.aether": "Aether Dragon", 5 | "dragon_breed.dragonmounts.fire": "Fire Dragon", 6 | "dragon_breed.dragonmounts.end": "End Dragon", 7 | "dragon_breed.dragonmounts.forest": "Forest Dragon", 8 | "dragon_breed.dragonmounts.ghost": "Ghost Dragon", 9 | "dragon_breed.dragonmounts.ice": "Ice Dragon", 10 | "dragon_breed.dragonmounts.nether": "Nether Dragon", 11 | "dragon_breed.dragonmounts.water": "Water Dragon", 12 | 13 | "item.dragonmounts.spawn_egg.dragonmounts.aether": "Aether Dragon Spawn Egg", 14 | "item.dragonmounts.spawn_egg.dragonmounts.fire": "Fire Dragon Spawn Egg", 15 | "item.dragonmounts.spawn_egg.dragonmounts.end": "End Dragon Spawn Egg", 16 | "item.dragonmounts.spawn_egg.dragonmounts.forest": "Forest Dragon Spawn Egg", 17 | "item.dragonmounts.spawn_egg.dragonmounts.ghost": "Ghost Dragon Spawn Egg", 18 | "item.dragonmounts.spawn_egg.dragonmounts.ice": "Ice Dragon Spawn Egg", 19 | "item.dragonmounts.spawn_egg.dragonmounts.nether": "Nether Dragon Spawn Egg", 20 | "item.dragonmounts.spawn_egg.dragonmounts.water": "Water Dragon Spawn Egg", 21 | 22 | "block.dragonmounts.dragon_egg.remaining_time": "Hatches in %s seconds.", 23 | "block.dragonmounts.dragon_egg.hatch_stage.0": "It looks like this Egg will take a long time to hatch.", 24 | "block.dragonmounts.dragon_egg.hatch_stage.1": "What will hatch from this? It doesn't seem close to hatching.", 25 | "block.dragonmounts.dragon_egg.hatch_stage.2": "It appears to move occasionally. It may be close to hatching.", 26 | "block.dragonmounts.dragon_egg.hatch_stage.3": "Sounds can be heard coming from inside! It will hatch soon!", 27 | 28 | "block.dragonmounts.dragon_egg.dragonmounts.aether": "Aether Dragon Egg", 29 | "block.dragonmounts.dragon_egg.dragonmounts.fire": "Fire Dragon Egg", 30 | "block.dragonmounts.dragon_egg.dragonmounts.end": "End Dragon Egg", 31 | "block.dragonmounts.dragon_egg.dragonmounts.forest": "Forest Dragon Egg", 32 | "block.dragonmounts.dragon_egg.dragonmounts.ghost": "Ghost Dragon Egg", 33 | "block.dragonmounts.dragon_egg.dragonmounts.ice": "Ice Dragon Egg", 34 | "block.dragonmounts.dragon_egg.dragonmounts.nether": "Nether Dragon Egg", 35 | "block.dragonmounts.dragon_egg.dragonmounts.water": "Water Dragon Egg", 36 | 37 | "key.category.dragonmounts": "Dragon Mounts", 38 | "key.dragonmounts.flight_descent": "Flight Descent", 39 | "key.dragonmounts.camera_flight": "Flight Commands", 40 | 41 | "mount.dragon.vertical_controls": "Press %s to Ascend and %s to Descend", 42 | "mount.dragon.camera_controls.enabled": "%s will now Ascend Toward Your Direction", 43 | "mount.dragon.camera_controls.disabled": "%s will now only Ascend on your Command", 44 | 45 | "subtitles.entity.dragon.ambient": "Dragon speaks", 46 | "subtitles.entity.dragon.step": "Dragon stomps", 47 | "subtitles.entity.dragon.death": "Dragon screeches", 48 | "subtitles.entity.dragon.ambient.ghost": "Dragon speaks" 49 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/abilities/HydroStepAbility.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.abilities; 2 | 3 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 4 | import com.mojang.serialization.MapCodec; 5 | import net.minecraft.core.BlockPos; 6 | import net.minecraft.core.particles.ParticleTypes; 7 | import net.minecraft.resources.ResourceLocation; 8 | import net.minecraft.server.level.ServerLevel; 9 | import net.minecraft.tags.BlockTags; 10 | import net.minecraft.world.level.block.Blocks; 11 | import net.minecraft.world.level.block.FarmBlock; 12 | import net.minecraft.world.level.block.WeatheringCopper; 13 | 14 | public class HydroStepAbility extends FootprintAbility implements Ability.Factory 15 | { 16 | public static final HydroStepAbility INSTANCE = new HydroStepAbility(); 17 | public static final MapCodec CODEC = MapCodec.unit(INSTANCE); 18 | 19 | @Override 20 | protected void placeFootprint(TameableDragon dragon, BlockPos pos) 21 | { 22 | var level = dragon.level(); 23 | var groundPos = pos.below(); 24 | var steppingOn = level.getBlockState(groundPos); 25 | 26 | ((ServerLevel) level).sendParticles(ParticleTypes.FALLING_WATER, pos.getX(), pos.getY(), pos.getZ(), 10, 0.25, 0, 0.25, 0); 27 | 28 | // moisten farmland 29 | // soak sponges 30 | // extinguish fire 31 | // magmablock -> blackstone 32 | // copper -> rust 33 | 34 | if (steppingOn.is(Blocks.FARMLAND)) 35 | { 36 | level.setBlockAndUpdate(groundPos, steppingOn.setValue(FarmBlock.MOISTURE, FarmBlock.MAX_MOISTURE)); 37 | return; 38 | } 39 | 40 | if (steppingOn.is(Blocks.SPONGE)) 41 | { 42 | level.setBlockAndUpdate(groundPos, Blocks.WET_SPONGE.defaultBlockState()); 43 | return; 44 | } 45 | 46 | if (steppingOn.is(Blocks.MAGMA_BLOCK)) 47 | { 48 | level.setBlockAndUpdate(groundPos, Blocks.BLACKSTONE.defaultBlockState()); 49 | return; 50 | } 51 | 52 | var steppingOnName = steppingOn.getBlock().builtInRegistryHolder().key().location(); 53 | if (steppingOnName.getNamespace().equals("minecraft") && steppingOnName.getPath().contains("copper")) // yeah fuck that copper complex this game's got going on 54 | { 55 | WeatheringCopper.getNext(steppingOn.getBlock()).ifPresent(b -> level.setBlockAndUpdate(groundPos, b.withPropertiesOf(steppingOn))); 56 | return; 57 | } 58 | 59 | if (level.getBlockState(pos).is(BlockTags.FIRE)) 60 | { 61 | level.removeBlock(pos, false); 62 | return; 63 | } 64 | } 65 | 66 | @Override 67 | protected float getFootprintChance(TameableDragon dragon) 68 | { 69 | return 1f; // guaranteed 70 | } 71 | 72 | @Override 73 | public HydroStepAbility create() 74 | { 75 | return this; 76 | } 77 | 78 | @Override 79 | public MapCodec> codec() 80 | { 81 | return CODEC; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/data/loot/DragonEggLootMod.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.data.loot; 2 | 3 | import com.github.kay9.dragonmounts.dragon.DragonBreed; 4 | import com.github.kay9.dragonmounts.dragon.egg.HatchableEggBlock; 5 | import com.mojang.serialization.Codec; 6 | import com.mojang.serialization.MapCodec; 7 | import com.mojang.serialization.codecs.RecordCodecBuilder; 8 | import it.unimi.dsi.fastutil.objects.ObjectArrayList; 9 | import net.minecraft.core.Holder; 10 | import net.minecraft.resources.ResourceKey; 11 | import net.minecraft.world.item.ItemStack; 12 | import net.minecraft.world.level.storage.loot.BuiltInLootTables; 13 | import net.minecraft.world.level.storage.loot.LootContext; 14 | import net.minecraft.world.level.storage.loot.LootTable; 15 | import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; 16 | import net.minecraftforge.common.loot.IGlobalLootModifier; 17 | import net.minecraftforge.common.loot.LootModifier; 18 | import org.jetbrains.annotations.NotNull; 19 | 20 | import static com.github.kay9.dragonmounts.dragon.DragonBreed.BuiltIn.*; 21 | 22 | public class DragonEggLootMod extends LootModifier 23 | { 24 | public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(i -> codecStart(i) 25 | .and(DragonBreed.CODEC.fieldOf("egg_breed").forGetter(m -> m.breed)) 26 | .and(Codec.BOOL.optionalFieldOf("replace_first", false).forGetter(m -> m.replaceFirst)) 27 | .apply(i, DragonEggLootMod::new)); 28 | 29 | public record Target(ResourceKey forBreed, ResourceKey target, double chance) {} 30 | public static Target[] BUILT_IN_CHANCES = new Target[]{ 31 | new Target(AETHER, BuiltInLootTables.SIMPLE_DUNGEON, 0.15), 32 | new Target(FIRE, BuiltInLootTables.DESERT_PYRAMID, 0.075), 33 | new Target(FOREST, BuiltInLootTables.JUNGLE_TEMPLE, 0.3), 34 | new Target(GHOST, BuiltInLootTables.WOODLAND_MANSION, 0.2), 35 | new Target(GHOST, BuiltInLootTables.ABANDONED_MINESHAFT, 0.095), 36 | new Target(ICE, BuiltInLootTables.IGLOO_CHEST, 0.2), 37 | new Target(NETHER, BuiltInLootTables.BASTION_TREASURE, 0.35), 38 | new Target(WATER, BuiltInLootTables.BURIED_TREASURE, 0.175) 39 | }; 40 | 41 | private final Holder breed; 42 | private final boolean replaceFirst; 43 | 44 | public DragonEggLootMod(LootItemCondition[] conditions, Holder breed, boolean replaceFirst) 45 | { 46 | super(conditions); 47 | this.breed = breed; 48 | this.replaceFirst = replaceFirst; 49 | } 50 | 51 | @Override 52 | protected @NotNull ObjectArrayList doApply(ObjectArrayList generatedLoot, LootContext context) 53 | { 54 | var egg = HatchableEggBlock.Item.create(breed); 55 | 56 | if (replaceFirst) generatedLoot.set(0, egg); 57 | else generatedLoot.add(egg); 58 | 59 | return generatedLoot; 60 | } 61 | 62 | @Override 63 | public MapCodec codec() 64 | { 65 | return CODEC; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/data/providers/LootTableProvider.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.data.providers; 2 | 3 | import com.github.kay9.dragonmounts.DMLRegistry; 4 | import com.github.kay9.dragonmounts.dragon.egg.HatchableEggBlock; 5 | import com.google.common.collect.ImmutableList; 6 | import net.minecraft.core.HolderLookup; 7 | import net.minecraft.core.Registry; 8 | import net.minecraft.core.component.DataComponents; 9 | import net.minecraft.data.PackOutput; 10 | import net.minecraft.data.loot.BlockLootSubProvider; 11 | import net.minecraft.util.ProblemReporter; 12 | import net.minecraft.world.flag.FeatureFlags; 13 | import net.minecraft.world.level.block.Block; 14 | import net.minecraft.world.level.storage.loot.LootPool; 15 | import net.minecraft.world.level.storage.loot.LootTable; 16 | import net.minecraft.world.level.storage.loot.ValidationContext; 17 | import net.minecraft.world.level.storage.loot.entries.LootItem; 18 | import net.minecraft.world.level.storage.loot.functions.CopyBlockState; 19 | import net.minecraft.world.level.storage.loot.functions.CopyComponentsFunction; 20 | import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; 21 | import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; 22 | 23 | import java.util.Set; 24 | import java.util.concurrent.CompletableFuture; 25 | 26 | public class LootTableProvider extends net.minecraft.data.loot.LootTableProvider 27 | { 28 | public LootTableProvider(PackOutput pOutput, CompletableFuture registries) 29 | { 30 | super(pOutput, Set.of(), ImmutableList.of( 31 | new SubProviderEntry(Blocks::new, LootContextParamSets.BLOCK) 32 | ), registries); 33 | } 34 | 35 | @Override 36 | protected void validate(Registry map, ValidationContext validationcontext, ProblemReporter report) 37 | { 38 | } 39 | 40 | private static class Blocks extends BlockLootSubProvider 41 | { 42 | public Blocks(HolderLookup.Provider registry) 43 | { 44 | super(Set.of(DMLRegistry.EGG_BLOCK.get().asItem()), FeatureFlags.REGISTRY.allFlags(), registry); 45 | } 46 | 47 | @Override 48 | protected void generate() 49 | { 50 | add(DMLRegistry.EGG_BLOCK.get(), b -> LootTable.lootTable() 51 | .withPool(applyExplosionCondition(b, LootPool.lootPool() 52 | .setRolls(ConstantValue.exactly(1)) 53 | .add(LootItem.lootTableItem(b) 54 | .apply(CopyComponentsFunction.copyComponents(CopyComponentsFunction.Source.BLOCK_ENTITY) 55 | .include(DMLRegistry.DRAGON_BREED_COMPONENT.get()) 56 | .include(DataComponents.CUSTOM_NAME)) 57 | .apply(CopyBlockState.copyState(b) 58 | .copy(HatchableEggBlock.HATCH_STAGE) 59 | ) 60 | ) 61 | )) 62 | ); 63 | } 64 | 65 | @Override 66 | protected Iterable getKnownBlocks() 67 | { 68 | return ImmutableList.of(DMLRegistry.EGG_BLOCK.get()); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/resources/assets/dragonmounts/lang/uk_ua.json: -------------------------------------------------------------------------------- 1 | { 2 | "entity.dragonmounts.dragon": "Дракон", 3 | 4 | "dragon_breed.dragonmounts.aether": "Дракон Етеру", 5 | "dragon_breed.dragonmounts.fire": "Вогневий дракон", 6 | "dragon_breed.dragonmounts.end": "Дракон Енду", 7 | "dragon_breed.dragonmounts.forest": "Лісовий дракон", 8 | "dragon_breed.dragonmounts.ghost": "Примарний дракон", 9 | "dragon_breed.dragonmounts.ice": "Крижаний дракон", 10 | "dragon_breed.dragonmounts.nether": "Незерський дракон", 11 | "dragon_breed.dragonmounts.water": "Водяний дракон", 12 | 13 | "item.dragonmounts.spawn_egg.dragonmounts.aether": "Яйце виклику дракона Етеру", 14 | "item.dragonmounts.spawn_egg.dragonmounts.fire": "Яйце виклику вогневого дракона", 15 | "item.dragonmounts.spawn_egg.dragonmounts.end": "Яйце виклику дракона Енду", 16 | "item.dragonmounts.spawn_egg.dragonmounts.forest": "Яйце виклику лісового дракона", 17 | "item.dragonmounts.spawn_egg.dragonmounts.ghost": "Яйце виклику примарного дракона", 18 | "item.dragonmounts.spawn_egg.dragonmounts.ice": "Яйце виклику крижаного дракона", 19 | "item.dragonmounts.spawn_egg.dragonmounts.nether": "Яйце виклику незерського дракона", 20 | "item.dragonmounts.spawn_egg.dragonmounts.water": "Яйце виклику водяного дракона", 21 | 22 | "block.dragonmounts.dragon_egg.change_breeds": "Режим творчості: взаємодія з драконом змінитить його породу на породу цього яйця.", 23 | "block.dragonmounts.dragon_egg.desc1": "Взаємодія з драконом:", 24 | "block.dragonmounts.dragon_egg.desc2": "Змінює тип породи", 25 | "block.dragonmounts.dragon_egg.hatch_stage.0": "Схоже, цьому яйцю знадобиться багато часу, щоб вилупитися.", 26 | "block.dragonmounts.dragon_egg.hatch_stage.1": "Як гадаєш, що може вилупитися з цього? Не схоже, що воно близьке до вилуплення.", 27 | "block.dragonmounts.dragon_egg.hatch_stage.2": "Здається, воно ворушиться час від часу. Можливо, воно близьке до вилуплення.", 28 | "block.dragonmounts.dragon_egg.hatch_stage.3": "Чути звуки, що долинають зсередини! Воно скоро вилупиться!", 29 | "block.dragonmounts.dragon_egg.remaining_time": "Вилупиться через %s секунд.", 30 | 31 | "block.dragonmounts.dragon_egg.dragonmounts.aether": "Яйце дракона Етеру", 32 | "block.dragonmounts.dragon_egg.dragonmounts.fire": "Яйце вогневого дракона", 33 | "block.dragonmounts.dragon_egg.dragonmounts.end": "Яйце дракона Енду", 34 | "block.dragonmounts.dragon_egg.dragonmounts.forest": "Яйце лісового дракона", 35 | "block.dragonmounts.dragon_egg.dragonmounts.ghost": "Яйце примарного дракона", 36 | "block.dragonmounts.dragon_egg.dragonmounts.ice": "Яйце крижаного дракона", 37 | "block.dragonmounts.dragon_egg.dragonmounts.nether": "Яйце незерського дракона", 38 | "block.dragonmounts.dragon_egg.dragonmounts.water": "Яйце водяного дракона", 39 | 40 | "key.dragonmounts.camera_flight": "Керування польотом верхового дракона", 41 | "key.dragonmounts.flight_descent": "Зниження польоту верхового дракона", 42 | 43 | "mount.dragon.vertical_controls": "Натисніть %s, щоб здійнятися та %s, щоб спуститися", 44 | "mount.dragon.camera_controls.enabled": "%s тепер здіймається в напрямку вашого погляду", 45 | "mount.dragon.camera_controls.disabled": "%s тепер здіймається лише по вашій команді", 46 | 47 | "subtitles.entity.dragon.ambient": "Дракон ричить", 48 | "subtitles.entity.dragon.step": "Дракон тупотить", 49 | "subtitles.entity.dragon.death": "Дракон гарчить", 50 | "subtitles.entity.dragon.ambient.ghost": "Дракон ричить" 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/abilities/FrostWalkerAbility.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.abilities; 2 | 3 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 4 | import com.mojang.serialization.Codec; 5 | import com.mojang.serialization.MapCodec; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.core.Direction; 8 | import net.minecraft.util.Mth; 9 | import net.minecraft.world.level.block.Blocks; 10 | import net.minecraft.world.level.block.FrostedIceBlock; 11 | import net.minecraft.world.level.pathfinder.PathType; 12 | import net.minecraft.world.phys.shapes.CollisionContext; 13 | import net.minecraftforge.common.util.BlockSnapshot; 14 | import net.minecraftforge.event.ForgeEventFactory; 15 | 16 | 17 | public class FrostWalkerAbility implements Ability, Ability.Factory 18 | { 19 | public static final MapCodec CODEC = Codec.FLOAT 20 | .xmap(FrostWalkerAbility::new, a -> a.radiusMultiplier) 21 | .fieldOf("radius_multiplier"); 22 | 23 | private final float radiusMultiplier; 24 | 25 | protected FrostWalkerAbility(float radiusMultiplier) 26 | { 27 | this.radiusMultiplier = radiusMultiplier; 28 | } 29 | 30 | public static FrostWalkerAbility create(float radiusMultiplier) 31 | { 32 | return new FrostWalkerAbility(radiusMultiplier); 33 | } 34 | 35 | @Override 36 | public void initialize(TameableDragon dragon) 37 | { 38 | dragon.setPathfindingMalus(PathType.WATER, 0); 39 | } 40 | 41 | @Override 42 | public void tick(TameableDragon dragon) 43 | { 44 | var level = dragon.level(); 45 | 46 | if (dragon.tickCount % 3 != 0) return; // no need for expensive calcs EVERY tick 47 | if (level.isClientSide() || dragon.getAgeProgress() < 0.5) 48 | return; // only juveniles and older can frost walk 49 | 50 | // taken from and modified of FrostWalkerEnchantment#onEntityMoved 51 | var radius = (int) (Math.max(radiusMultiplier * dragon.getAgeScale(), 1) + 3); 52 | var pos = dragon.blockPosition(); 53 | 54 | for (BlockPos carat : BlockPos.betweenClosed(pos.offset((-radius), -2, (-radius)), pos.offset(radius, -1, radius))) 55 | { 56 | if (!carat.closerToCenterThan(dragon.position(), radius)) 57 | continue; // circle 58 | 59 | var currentState = level.getBlockState(carat); 60 | 61 | if (currentState != FrostedIceBlock.meltsInto()) 62 | continue; 63 | if (ForgeEventFactory.onBlockPlace(dragon, BlockSnapshot.create(level.dimension(), level, carat), Direction.UP)) 64 | continue; 65 | 66 | var ice = Blocks.FROSTED_ICE.defaultBlockState(); 67 | 68 | if (!ice.canSurvive(level, carat) || !level.isUnobstructed(ice, carat, CollisionContext.empty())) 69 | continue; 70 | 71 | var mPos = carat.mutable().move(0, 1, 0); 72 | 73 | if (!level.getBlockState(mPos).isAir()) 74 | continue; 75 | 76 | level.setBlockAndUpdate(carat, ice); 77 | level.scheduleTick(carat, Blocks.FROSTED_ICE, Mth.nextInt(dragon.getRandom(), 60, 120)); 78 | } 79 | } 80 | 81 | @Override 82 | public FrostWalkerAbility create() 83 | { 84 | return this; 85 | } 86 | 87 | @Override 88 | public MapCodec> codec() 89 | { 90 | return CODEC; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/client/ModelPartProxy.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.client; 2 | 3 | import com.github.kay9.dragonmounts.accessors.ModelPartAccess; 4 | import com.mojang.blaze3d.vertex.PoseStack; 5 | import com.mojang.blaze3d.vertex.VertexConsumer; 6 | import net.minecraft.client.model.geom.ModelPart; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Proxy for a model part that is used to project one model renderer on multiple 12 | * visible instances. 13 | * 14 | * @author Nico Bergemann 15 | */ 16 | @SuppressWarnings("DataFlowIssue") 17 | public class ModelPartProxy 18 | { 19 | // projected parts and part childs 20 | public final ModelPart part; 21 | private final List children; 22 | // scale multiplier 23 | public float scaleX = 1; 24 | public float scaleY = 1; 25 | public float scaleZ = 1; 26 | // rotation points 27 | private float x; 28 | private float y; 29 | private float z; 30 | // rotation angles 31 | private float xRot; 32 | private float yRot; 33 | private float zRot; 34 | // misc meta data 35 | private boolean visible; 36 | 37 | /** 38 | * Constructs a new proxy for the given model part. 39 | * 40 | * @param part model part to project on this proxy 41 | */ 42 | public ModelPartProxy(ModelPart part) 43 | { 44 | this.part = part; 45 | 46 | children = part.getAllParts().skip(1).map(ModelPartProxy::new).toList(); 47 | 48 | update(); 49 | } 50 | 51 | public void copy(ModelPartProxy other) 52 | { 53 | other.x = x; 54 | other.y = y; 55 | other.z = z; 56 | 57 | other.xRot = xRot; 58 | other.yRot = yRot; 59 | other.zRot = zRot; 60 | 61 | other.scaleX = scaleX; 62 | other.scaleY = scaleY; 63 | other.scaleZ = scaleZ; 64 | 65 | other.visible = visible; 66 | 67 | if (children.size() != other.children.size()) 68 | throw new IllegalArgumentException("Proxies do not share the same children."); 69 | for (int i = 0; i < children.size(); i++) children.get(i).copy(other.children.get(i)); 70 | } 71 | 72 | /** 73 | * Saves the properties of the model part to this proxy with the default 74 | * rendering scale. 75 | */ 76 | public final void update() 77 | { 78 | x = part.x; 79 | y = part.y; 80 | z = part.z; 81 | 82 | xRot = part.xRot; 83 | yRot = part.yRot; 84 | zRot = part.zRot; 85 | 86 | ModelPartAccess mixinPart = (ModelPartAccess)(Object) part; 87 | 88 | scaleX = mixinPart.getXScale(); 89 | scaleY = mixinPart.getYScale(); 90 | scaleZ = mixinPart.getZScale(); 91 | 92 | visible = part.visible; 93 | 94 | for (ModelPartProxy child : children) child.update(); 95 | } 96 | 97 | /** 98 | * Restores the properties from this proxy to the model part. 99 | */ 100 | public final void apply() 101 | { 102 | part.x = x; 103 | part.y = y; 104 | part.z = z; 105 | 106 | part.xRot = xRot; 107 | part.yRot = yRot; 108 | part.zRot = zRot; 109 | 110 | ((ModelPartAccess)(Object) part).setRenderScale(scaleX, scaleY, scaleZ); 111 | 112 | part.visible = visible; 113 | 114 | for (ModelPartProxy child : children) child.apply(); 115 | } 116 | 117 | public void render(PoseStack ps, VertexConsumer vertices, int packedLightIn, int packedOverlayIn, int color) 118 | { 119 | apply(); 120 | part.render(ps, vertices, packedLightIn, packedOverlayIn, color); 121 | } 122 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/data/CrossBreedingManager.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.data; 2 | 3 | import com.github.kay9.dragonmounts.dragon.DragonBreed; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.JsonElement; 6 | import com.mojang.serialization.Codec; 7 | import com.mojang.serialization.JsonOps; 8 | import com.mojang.serialization.codecs.RecordCodecBuilder; 9 | import net.minecraft.core.Holder; 10 | import net.minecraft.core.HolderLookup; 11 | import net.minecraft.resources.ResourceKey; 12 | import net.minecraft.resources.ResourceLocation; 13 | import net.minecraft.server.packs.resources.ResourceManager; 14 | import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; 15 | import net.minecraft.util.profiling.ProfilerFiller; 16 | 17 | import javax.annotation.Nullable; 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | public class CrossBreedingManager extends SimpleJsonResourceReloadListener 22 | { 23 | public static final CrossBreedingManager INSTANCE = new CrossBreedingManager(); 24 | private static final String PATH = "dragonmounts/cross_breeding"; // data/[pack_name]/dragonmounts/cross_breeds/whatever.json 25 | 26 | private final Map> crosses = new HashMap<>(); 27 | 28 | private CrossBreedingManager() 29 | { 30 | super(new GsonBuilder().create(), PATH); 31 | } 32 | 33 | @Override 34 | protected void apply(Map entries, ResourceManager pResourceManager, ProfilerFiller pProfiler) 35 | { 36 | crosses.clear(); 37 | 38 | for (var entry : entries.entrySet()) 39 | { 40 | var id = entry.getKey(); 41 | var json = entry.getValue(); 42 | var cross = CrossBreedResult.CODEC.parse(JsonOps.INSTANCE, json) 43 | .getOrThrow(s -> new IllegalStateException("Unable to parse Cross Breeding result for: " + id + ", " + s)); 44 | crosses.put(new Couple(cross.parent1(), cross.parent2()), cross.child()); 45 | } 46 | } 47 | 48 | @Nullable 49 | public Holder getCrossBreed(Holder parent, Holder mate, HolderLookup.Provider ra) 50 | { 51 | ResourceKey result = crosses.get(new Couple(parent.unwrapKey().orElseThrow(), mate.unwrapKey().orElseThrow())); 52 | 53 | return result == null? null : DragonBreed.get(result, ra); 54 | } 55 | 56 | public record CrossBreedResult(ResourceKey parent1, ResourceKey parent2, ResourceKey child) 57 | { 58 | public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( 59 | ResourceKey.codec(DragonBreed.REGISTRY_KEY).fieldOf("parent1").forGetter(CrossBreedResult::parent1), 60 | ResourceKey.codec(DragonBreed.REGISTRY_KEY).fieldOf("parent2").forGetter(CrossBreedResult::parent2), 61 | ResourceKey.codec(DragonBreed.REGISTRY_KEY).fieldOf("child").forGetter(CrossBreedResult::child) 62 | ).apply(instance, CrossBreedResult::new)); 63 | } 64 | 65 | private record Couple(ResourceKey parent1, ResourceKey parent2) 66 | { 67 | @Override 68 | public boolean equals(Object o) 69 | { 70 | if (this == o) return true; 71 | if (o == null || getClass() != o.getClass()) return false; 72 | Couple couple = (Couple) o; 73 | return (parent1 == couple.parent1 && parent2 == couple.parent2) || 74 | (parent1 == couple.parent2 && parent2 == couple.parent1); 75 | } 76 | 77 | @Override 78 | public int hashCode() 79 | { 80 | return parent1.hashCode() + parent2.hashCode(); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/abilities/ReaperStepAbility.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.abilities; 2 | 3 | import com.github.kay9.dragonmounts.DragonMountsLegacy; 4 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 5 | import com.mojang.serialization.MapCodec; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.core.particles.ParticleTypes; 8 | import net.minecraft.resources.ResourceLocation; 9 | import net.minecraft.server.level.ServerLevel; 10 | import net.minecraft.sounds.SoundEvents; 11 | import net.minecraft.tags.BlockTags; 12 | import net.minecraft.tags.TagKey; 13 | import net.minecraft.world.entity.item.ItemEntity; 14 | import net.minecraft.world.item.ItemStack; 15 | import net.minecraft.world.item.Items; 16 | import net.minecraft.world.level.Level; 17 | import net.minecraft.world.level.block.Block; 18 | import net.minecraft.world.level.block.Blocks; 19 | import net.minecraft.world.level.block.state.BlockState; 20 | 21 | public class ReaperStepAbility extends FootprintAbility implements Ability.Factory 22 | { 23 | public static final ReaperStepAbility INSTANCE = new ReaperStepAbility(); 24 | public static final MapCodec CODEC = MapCodec.unit(INSTANCE); 25 | 26 | public static final TagKey PLANT_DEATH_TAG = BlockTags.create(DragonMountsLegacy.id("reaper_plant_death")); 27 | public static final TagKey PLANT_DESTRUCTION_TAG = BlockTags.create(DragonMountsLegacy.id("reaper_plant_destruction")); 28 | public static final TagKey REAPER_TRANSFORM = BlockTags.create(DragonMountsLegacy.id("reaper_transform")); 29 | 30 | @Override 31 | protected void placeFootprint(TameableDragon dragon, BlockPos pos) 32 | { 33 | var level = dragon.level(); 34 | var steppingOn = level.getBlockState(pos); 35 | if (steppingOn.is(PLANT_DEATH_TAG)) 36 | { 37 | level.removeBlock(pos, false); 38 | level.playSound(null, pos, SoundEvents.FIRE_EXTINGUISH, dragon.getSoundSource(), 0.1f, 2f); 39 | ((ServerLevel) level).sendParticles(ParticleTypes.SOUL, pos.getX(), pos.getY(), pos.getZ(), 0, 0, 1, 0, 0.05); 40 | 41 | var bs = (dragon.getRandom().nextDouble() < 0.05? Blocks.WITHER_ROSE : Blocks.DEAD_BUSH).defaultBlockState(); 42 | level.setBlock(pos, bs, Block.UPDATE_ALL); 43 | } 44 | else if (steppingOn.is(PLANT_DESTRUCTION_TAG)) 45 | { 46 | level.destroyBlock(pos, false); 47 | level.playSound(null, pos, SoundEvents.FIRE_EXTINGUISH, dragon.getSoundSource(), 0.1f, 2f); 48 | ((ServerLevel) level).sendParticles(ParticleTypes.SOUL, pos.getX(), pos.getY(), pos.getZ(), 0, 0, 1, 0, 0.05); 49 | 50 | var sticks = new ItemEntity(level, pos.getX(), pos.getY(), pos.getZ(), new ItemStack(Items.STICK)); 51 | sticks.setPickUpDelay(40); 52 | level.addFreshEntity(sticks); 53 | } 54 | else if ((steppingOn = level.getBlockState(pos = pos.below())).is(REAPER_TRANSFORM)) // todo: this isn't very customizable... 55 | { 56 | if (steppingOn.is(Blocks.GRASS_BLOCK)) 57 | destroyAndReplace(level, Blocks.DIRT.defaultBlockState(), pos); 58 | else if (steppingOn.is(BlockTags.SAND)) 59 | destroyAndReplace(level, Blocks.SOUL_SAND.defaultBlockState(), pos); 60 | else if (steppingOn.is(BlockTags.DIRT)) 61 | destroyAndReplace(level, Blocks.SOUL_SOIL.defaultBlockState(), pos); 62 | 63 | } 64 | } 65 | 66 | @Override 67 | protected float getFootprintChance(TameableDragon dragon) 68 | { 69 | return 0.025f; 70 | } 71 | 72 | private static void destroyAndReplace(Level level, BlockState state, BlockPos pos) 73 | { 74 | level.destroyBlock(pos, false); 75 | level.setBlock(pos, state, Block.UPDATE_ALL); 76 | } 77 | 78 | @Override 79 | public ReaperStepAbility create() 80 | { 81 | return this; 82 | } 83 | 84 | @Override 85 | public MapCodec> codec() 86 | { 87 | return CODEC; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/data/providers/BlockTagProvider.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.data.providers; 2 | 3 | import com.github.kay9.dragonmounts.DragonMountsLegacy; 4 | import com.github.kay9.dragonmounts.dragon.abilities.HotFeetAbility; 5 | import com.github.kay9.dragonmounts.dragon.abilities.ReaperStepAbility; 6 | import net.minecraft.core.HolderLookup; 7 | import net.minecraft.data.PackOutput; 8 | import net.minecraft.tags.BlockTags; 9 | import net.minecraft.tags.TagKey; 10 | import net.minecraft.world.level.block.Block; 11 | import net.minecraft.world.level.block.Blocks; 12 | import net.minecraftforge.common.data.BlockTagsProvider; 13 | import net.minecraftforge.common.data.ExistingFileHelper; 14 | 15 | import java.util.concurrent.CompletableFuture; 16 | 17 | class BlockTagProvider extends BlockTagsProvider 18 | { 19 | static final TagKey FIRE_DRAGON_HABITAT_BLOCKS = BlockTags.create(DragonMountsLegacy.id("fire_dragon_habitat_blocks")); 20 | static final TagKey FOREST_DRAGON_HABITAT_BLOCKS = BlockTags.create(DragonMountsLegacy.id("forest_dragon_habitat_blocks")); 21 | static final TagKey ICE_DRAGON_HABITAT_BLOCKS = BlockTags.create(DragonMountsLegacy.id("ice_dragon_habitat_blocks")); 22 | static final TagKey NETHER_DRAGON_HABITAT_BLOCKS = BlockTags.create(DragonMountsLegacy.id("nether_dragon_habitat_blocks")); 23 | static final TagKey WATER_DRAGON_HABITAT_BLOCKS = BlockTags.create(DragonMountsLegacy.id("water_dragon_habitat_blocks")); 24 | 25 | BlockTagProvider(PackOutput output, CompletableFuture lookup, String modid, ExistingFileHelper existingFileHelper) 26 | { 27 | super(output, lookup, modid, existingFileHelper); 28 | } 29 | 30 | @Override 31 | @SuppressWarnings("unchecked") 32 | protected void addTags(HolderLookup.Provider pProvider) 33 | { 34 | tag(FIRE_DRAGON_HABITAT_BLOCKS) 35 | .add(Blocks.FIRE, Blocks.LAVA, Blocks.MAGMA_BLOCK, Blocks.CAMPFIRE); 36 | 37 | tag(FOREST_DRAGON_HABITAT_BLOCKS) 38 | .addTags(BlockTags.LEAVES, BlockTags.SAPLINGS, BlockTags.FLOWERS) 39 | .add(Blocks.MOSS_BLOCK, Blocks.MOSS_CARPET, Blocks.VINE); 40 | 41 | tag(ICE_DRAGON_HABITAT_BLOCKS) 42 | .addTags(BlockTags.ICE, BlockTags.SNOW); 43 | 44 | tag(NETHER_DRAGON_HABITAT_BLOCKS) 45 | .addTags(BlockTags.SOUL_FIRE_BASE_BLOCKS, BlockTags.BASE_STONE_NETHER, BlockTags.WARPED_STEMS, BlockTags.CRIMSON_STEMS) 46 | .add(Blocks.GLOWSTONE, Blocks.SOUL_FIRE, Blocks.SOUL_CAMPFIRE, Blocks.SOUL_TORCH, Blocks.NETHER_GOLD_ORE, 47 | Blocks.NETHER_QUARTZ_ORE, Blocks.ANCIENT_DEBRIS, Blocks.TWISTING_VINES_PLANT, Blocks.WEEPING_VINES_PLANT, 48 | Blocks.SHROOMLIGHT, Blocks.NETHER_WART, Blocks.NETHER_WART_BLOCK); 49 | 50 | tag(WATER_DRAGON_HABITAT_BLOCKS) 51 | .addTags(BlockTags.CORALS, BlockTags.WALL_CORALS, BlockTags.CORAL_BLOCKS) 52 | .add(Blocks.SEAGRASS, Blocks.TALL_SEAGRASS, Blocks.KELP, Blocks.KELP_PLANT, Blocks.PRISMARINE, 53 | Blocks.SEA_LANTERN, Blocks.SEA_PICKLE); 54 | 55 | 56 | tag(HotFeetAbility.BURNABLES_TAG) 57 | .addTags(BlockTags.FLOWERS, BlockTags.SAPLINGS, BlockTags.CROPS) 58 | .add(Blocks.SHORT_GRASS, Blocks.TALL_GRASS, Blocks.SWEET_BERRY_BUSH, Blocks.DEAD_BUSH, Blocks.PITCHER_PLANT, 59 | Blocks.BIG_DRIPLEAF, Blocks.SMALL_DRIPLEAF, Blocks.BIG_DRIPLEAF_STEM, Blocks.SUGAR_CANE, 60 | Blocks.FERN, Blocks.LARGE_FERN, Blocks.PITCHER_PLANT); 61 | 62 | tag(ReaperStepAbility.PLANT_DEATH_TAG) 63 | .addTags(BlockTags.TALL_FLOWERS, BlockTags.CROPS, BlockTags.SAPLINGS) 64 | .add(Blocks.TALL_GRASS, Blocks.SHORT_GRASS, Blocks.SWEET_BERRY_BUSH, Blocks.SUGAR_CANE, Blocks.BIG_DRIPLEAF_STEM, 65 | Blocks.BIG_DRIPLEAF, Blocks.FERN, Blocks.LARGE_FERN, Blocks.PITCHER_PLANT); 66 | 67 | tag(ReaperStepAbility.PLANT_DESTRUCTION_TAG) 68 | .addTags(BlockTags.SMALL_FLOWERS) 69 | .add(Blocks.RED_MUSHROOM, Blocks.BROWN_MUSHROOM, Blocks.SMALL_DRIPLEAF) 70 | .remove(Blocks.WITHER_ROSE); 71 | 72 | tag(ReaperStepAbility.REAPER_TRANSFORM) 73 | .addTags(BlockTags.SAND, BlockTags.DIRT) 74 | .add(Blocks.GRASS_BLOCK); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/ForgeModImpl.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts; 2 | 3 | import com.github.kay9.dragonmounts.client.MountCameraManager; 4 | import com.github.kay9.dragonmounts.dragon.abilities.Ability; 5 | import com.github.kay9.dragonmounts.dragon.egg.habitats.Habitat; 6 | import net.minecraftforge.api.distmarker.Dist; 7 | import net.minecraftforge.client.event.*; 8 | import net.minecraftforge.common.MinecraftForge; 9 | import net.minecraftforge.event.AddReloadListenerEvent; 10 | import net.minecraftforge.event.BuildCreativeModeTabContentsEvent; 11 | import net.minecraftforge.event.TickEvent; 12 | import net.minecraftforge.event.entity.EntityAttributeCreationEvent; 13 | import net.minecraftforge.event.entity.player.PlayerInteractEvent; 14 | import net.minecraftforge.fml.ModLoadingContext; 15 | import net.minecraftforge.fml.common.Mod; 16 | import net.minecraftforge.fml.config.ModConfig; 17 | import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; 18 | import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent; 19 | import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; 20 | import net.minecraftforge.fml.loading.FMLLoader; 21 | import net.minecraftforge.network.Channel; 22 | import net.minecraftforge.network.ChannelBuilder; 23 | import net.minecraftforge.network.SimpleChannel; 24 | import net.minecraftforge.registries.DataPackRegistryEvent; 25 | import net.minecraftforge.registries.NewRegistryEvent; 26 | import net.minecraftforge.registries.RegistryBuilder; 27 | 28 | import static com.github.kay9.dragonmounts.DragonMountsLegacy.*; 29 | 30 | @Mod(DragonMountsLegacy.MOD_ID) 31 | public class ForgeModImpl 32 | { 33 | public static final SimpleChannel NETWORK; 34 | 35 | public ForgeModImpl() 36 | { 37 | var bus = FMLJavaModLoadingContext.get().getModEventBus(); 38 | 39 | DMLRegistry.init(bus); 40 | 41 | ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, DMLConfig.COMMON_SPEC); 42 | ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, DMLConfig.CLIENT_SPEC); 43 | 44 | setupEvents(); 45 | } 46 | 47 | static 48 | { 49 | var PROTOCOL_VERSION = 1; 50 | NETWORK = ChannelBuilder.named(DragonMountsLegacy.id("network")) 51 | .clientAcceptedVersions(Channel.VersionTest.exact(PROTOCOL_VERSION)) 52 | .serverAcceptedVersions(Channel.VersionTest.exact(PROTOCOL_VERSION)) 53 | .networkProtocolVersion(PROTOCOL_VERSION) 54 | .simpleChannel(); 55 | } 56 | 57 | private static void setupEvents() 58 | { 59 | var modBus = FMLJavaModLoadingContext.get().getModEventBus(); 60 | var bus = MinecraftForge.EVENT_BUS; 61 | 62 | bus.addListener((PlayerInteractEvent.RightClickBlock e) -> e.setCanceled(overrideVanillaDragonEgg(e.getLevel(), e.getPos(), e.getEntity()))); 63 | bus.addListener((AddReloadListenerEvent e) -> registerReloadListeners(e::addListener)); 64 | 65 | modBus.addListener((EntityAttributeCreationEvent e) -> registerEntityAttributes(e::put)); 66 | modBus.addListener((DataPackRegistryEvent.NewRegistry e) -> registerDatapacks(e::dataPackRegistry)); 67 | modBus.addListener((NewRegistryEvent e) -> e.create(RegistryBuilder.of(Ability.REGISTRY_KEY.location()))); 68 | modBus.addListener((NewRegistryEvent e) -> e.create(RegistryBuilder.of(Habitat.REGISTRY_KEY.location()))); 69 | modBus.addListener((FMLCommonSetupEvent e) -> registerEntityDataSerializers()); 70 | 71 | if (FMLLoader.getDist() == Dist.CLIENT) // Client Events 72 | { 73 | bus.addListener((TickEvent.ClientTickEvent e) -> clientTick(e.phase == TickEvent.Phase.START)); 74 | bus.addListener((ViewportEvent.ComputeCameraAngles e) -> MountCameraManager.setMountCameraAngles(e.getCamera())); 75 | bus.addListener((InputEvent.Key e) -> onKeyPress(e.getKey(), e.getAction(), e.getModifiers())); 76 | 77 | modBus.addListener((ModelEvent.RegisterGeometryLoaders e) -> registerEggModelLoader(e::register)); 78 | modBus.addListener((BuildCreativeModeTabContentsEvent e) -> registerCreativeTabItems(e.getTabKey(), e::accept)); 79 | modBus.addListener((EntityRenderersEvent.RegisterRenderers e) -> registerRenderers()); 80 | modBus.addListener((RegisterColorHandlersEvent.Item e) -> registerItemColors(e.getItemColors())); 81 | modBus.addListener((FMLConstructModEvent e) -> e.enqueueWork(DragonMountsLegacy::registerReloadListenersEarly)); 82 | modBus.addListener((RegisterKeyMappingsEvent e) -> registerKeyBindings(e::register)); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/abilities/GreenToesAbility.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.abilities; 2 | 3 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 4 | import com.mojang.serialization.Codec; 5 | import com.mojang.serialization.MapCodec; 6 | import net.minecraft.core.BlockPos; 7 | import net.minecraft.core.Holder; 8 | import net.minecraft.core.registries.Registries; 9 | import net.minecraft.resources.ResourceLocation; 10 | import net.minecraft.server.level.ServerLevel; 11 | import net.minecraft.tags.BlockTags; 12 | import net.minecraft.world.level.block.Blocks; 13 | import net.minecraft.world.level.block.BonemealableBlock; 14 | import net.minecraft.world.level.block.LevelEvent; 15 | import net.minecraft.world.level.block.state.BlockState; 16 | 17 | public class GreenToesAbility extends FootprintAbility implements Ability.Factory 18 | { 19 | public static final GreenToesAbility INSTANCE = new GreenToesAbility(); 20 | public static final MapCodec CODEC = MapCodec.unit(INSTANCE); 21 | 22 | protected GreenToesAbility() {} 23 | 24 | // grow mushrooms and plants 25 | @Override 26 | protected void placeFootprint(TameableDragon dragon, BlockPos pos) 27 | { 28 | var level = dragon.level(); 29 | var groundPos = pos.below(); 30 | var steppingOn = level.getBlockState(groundPos); 31 | var steppingOver = level.getBlockState(pos); 32 | 33 | if (steppingOn.is(Blocks.DIRT)) // regrow grass on dirt 34 | { 35 | level.setBlockAndUpdate(groundPos, Blocks.GRASS_BLOCK.defaultBlockState()); 36 | level.levelEvent(LevelEvent.PARTICLES_AND_SOUND_PLANT_GROWTH, groundPos, 2); 37 | return; 38 | } 39 | 40 | if (steppingOver.isAir()) // manually place flowers, mushrooms, etc. 41 | { 42 | BlockState placing = null; 43 | 44 | if (steppingOn.is(BlockTags.MUSHROOM_GROW_BLOCK)) 45 | placing = (level.getRandom().nextBoolean()? Blocks.RED_MUSHROOM : Blocks.BROWN_MUSHROOM).defaultBlockState(); 46 | else if (steppingOn.is(BlockTags.DIRT) && !steppingOn.is(Blocks.MOSS_BLOCK)) // different from the actual dirt block, could be grass or podzol. 47 | { 48 | // while grass blocks etc. do have defined bone meal behavior, I think our own is more viable. 49 | 50 | //noinspection deprecation 51 | placing = level.registryAccess().registryOrThrow(Registries.BLOCK) 52 | .getTag(BlockTags.SMALL_FLOWERS) 53 | .flatMap(tag -> tag.getRandomElement(dragon.getRandom())) 54 | .map(Holder::value) 55 | .filter(b -> b != Blocks.WITHER_ROSE) 56 | .orElse(Blocks.DANDELION) 57 | .defaultBlockState(); 58 | } 59 | 60 | if (placing != null && placing.canSurvive(level, pos)) 61 | { 62 | level.setBlockAndUpdate(pos, placing); 63 | level.levelEvent(LevelEvent.PARTICLES_AND_SOUND_PLANT_GROWTH, pos, 0); 64 | return; 65 | } 66 | } 67 | 68 | if (steppingOn.is(BlockTags.SAPLINGS) || 69 | steppingOver.is(BlockTags.SAPLINGS) || 70 | steppingOver.is(Blocks.BROWN_MUSHROOM) || 71 | steppingOver.is(Blocks.RED_MUSHROOM) || 72 | steppingOver.is(Blocks.WARPED_FUNGUS) || 73 | steppingOver.is(Blocks.CRIMSON_FUNGUS)) 74 | { 75 | return; // if these structures grow on the dragon they could hurt it... 76 | } 77 | 78 | // perform standard bone meal behavior on steppingOn or steppingOver block. 79 | var caret = pos; 80 | for (int i = 0; i < 2; caret = groundPos) 81 | { 82 | i++; 83 | var state = level.getBlockState(caret); 84 | if (!(state.getBlock() instanceof BonemealableBlock b) || !b.isValidBonemealTarget(level, caret, state)) 85 | continue; 86 | 87 | if (b.isBonemealSuccess(level, dragon.getRandom(), caret, state)) 88 | { 89 | b.performBonemeal((ServerLevel) level, level.getRandom(), caret, state); 90 | level.levelEvent(LevelEvent.PARTICLES_AND_SOUND_PLANT_GROWTH, caret, 0); 91 | return; 92 | } 93 | } 94 | } 95 | 96 | @Override 97 | public GreenToesAbility create() 98 | { 99 | return this; 100 | } 101 | 102 | @Override 103 | public MapCodec> codec() 104 | { 105 | return CODEC; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/abilities/Ability.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.abilities; 2 | 3 | import com.github.kay9.dragonmounts.DragonMountsLegacy; 4 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 5 | import com.google.common.base.Suppliers; 6 | import com.mojang.serialization.Codec; 7 | import com.mojang.serialization.MapCodec; 8 | import net.minecraft.core.Registry; 9 | import net.minecraft.nbt.CompoundTag; 10 | import net.minecraft.resources.ResourceKey; 11 | import net.minecraftforge.registries.IForgeRegistry; 12 | import net.minecraftforge.registries.RegistryManager; 13 | 14 | import java.util.function.Function; 15 | import java.util.function.Supplier; 16 | 17 | /** 18 | * Each ability type has its own number of factories. One type can have multiple factories 19 | * to specify behavior. 20 | * Factories create instances that are unique to each dragon entity 21 | * this is due to the fact that sometimes an ability needs entity-specific actions, 22 | * and sometimes we don't (so we memoize the instance instead of making dozens of copies) 23 | * For example: 24 | * - Ability X is defined in the datapack to place blocks in Y radius for Dragon Breed A. 25 | * - Ability X is defined in the datapack to place blocks in Z radius for Dragon Breed B. 26 | * - Ability X also requires a cooldown timer that has to be specific to each entity. 27 | * Breed A has: 28 | * - Ability X: 29 | * - final int radius = Y; // finals are Breed Specific variables 30 | * - int cooldown = 50 // other variables are Dragon-entity specific. 31 | * Breed B has: 32 | * - Ability X: 33 | * - final int radius = Z; 34 | * - int cooldown = 1000 35 | *
36 | * Same ability type, different constants, instance-based variables. 37 | *
38 | * If the ability DOES NOT require per-entity data, it may be better to off to implement {@link Factory} directly and 39 | * return itself. 40 | */ 41 | //todo registry based 42 | public interface Ability 43 | { 44 | ResourceKey>>> REGISTRY_KEY = ResourceKey.createRegistryKey(DragonMountsLegacy.id("ability_type")); 45 | Supplier>>> REGISTRY = Suppliers.memoize(() -> RegistryManager.ACTIVE.getRegistry(REGISTRY_KEY)); 46 | Codec> CODEC = Codec.lazyInitialized(() -> REGISTRY.get().getCodec().dispatch(Factory::codec, Function.identity())); 47 | 48 | default void initialize(TameableDragon dragon) {} 49 | 50 | default void write(TameableDragon dragon, CompoundTag nbt) {} 51 | 52 | default void read(TameableDragon dragon, CompoundTag nbt) {} 53 | 54 | default void tick(TameableDragon dragon) {} 55 | 56 | /** 57 | * Only called on the server 58 | */ 59 | default void onMove(TameableDragon dragon) {} 60 | 61 | /** 62 | * The Ability Factory is responsible for creating the instances of an ability. 63 | * If an ability is meant to be breed specific and needs no per-entity data, this interface can be implemented 64 | * directly by the ability class, and return itself in {@link Factory#create}. This effectively removes the need for 65 | * a new factory type instance that just returns a new 'effective singleton', which is unnecessary for memory. 66 | * Most abilities in DML are simplistic in nature and follow this approach. {@link FrostWalkerAbility} is a good 67 | * example. 68 | *

69 | * It is crucially important that if you are extending a class already implementing {@link Factory} that 70 | * you override {@link Factory#create} and {@link Factory#codec}.
71 | * If the parent ability class is a breed specific type, and your type is an entity specific, {@link Factory#create} and 72 | * {@link Factory#codec} do not matter since your codec SHOULD NOT USE THEM! You MUST create a new factory type for per-entity 73 | * implementations, or use {@link Ability#simpleFactory} 74 | */ 75 | interface Factory 76 | { 77 | /** 78 | * 79 | * @return an ability instance of type T 80 | */ 81 | T create(); 82 | 83 | MapCodec> codec(); 84 | } 85 | 86 | // /** 87 | // * While this is here, I don't necessarily recommend using it because the type has to be created to serialize 88 | // * its values, but it's not exactly a deal-breaker at data-generation. Just... impractical.
89 | // * Example usage: 90 | // *
 91 | //     * {@code Codec> CODEC = Codec.FLOAT
 92 | //     *  .xmap(myFloat -> Ability.simpleFactory(() -> new MyAbility(myfloat)), myFactory -> myFactory.create().myFloat)
 93 | //     *  .fieldOf("my_float")
 94 | //     *  .codec();}
 95 | //     * 
96 | // */ 97 | // static Factory simpleFactory(ResourceLocation id, Supplier factory) 98 | // { 99 | // return new Factory<>() 100 | // { 101 | // @Override 102 | // public T create() 103 | // { 104 | // return factory.get(); 105 | // } 106 | // 107 | // @Override 108 | // public ResourceLocation codec() 109 | // { 110 | // return id; 111 | // } 112 | // }; 113 | // } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/DragonSpawnEgg.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon; 2 | 3 | import com.github.kay9.dragonmounts.DMLRegistry; 4 | import com.mojang.serialization.Codec; 5 | import io.netty.buffer.ByteBuf; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.core.Holder; 8 | import net.minecraft.core.RegistryAccess; 9 | import net.minecraft.core.component.DataComponents; 10 | import net.minecraft.nbt.CompoundTag; 11 | import net.minecraft.nbt.NbtOps; 12 | import net.minecraft.nbt.Tag; 13 | import net.minecraft.network.chat.Component; 14 | import net.minecraft.network.codec.ByteBufCodecs; 15 | import net.minecraft.network.codec.StreamCodec; 16 | import net.minecraft.resources.RegistryOps; 17 | import net.minecraft.resources.ResourceLocation; 18 | import net.minecraft.server.MinecraftServer; 19 | import net.minecraft.server.level.ServerLevel; 20 | import net.minecraft.util.RandomSource; 21 | import net.minecraft.world.entity.EntityType; 22 | import net.minecraft.world.entity.Mob; 23 | import net.minecraft.world.entity.player.Player; 24 | import net.minecraft.world.item.Item; 25 | import net.minecraft.world.item.ItemStack; 26 | import net.minecraft.world.item.component.CustomData; 27 | import net.minecraft.world.phys.Vec3; 28 | import net.minecraftforge.common.ForgeSpawnEggItem; 29 | import net.minecraftforge.fml.DistExecutor; 30 | import net.minecraftforge.server.ServerLifecycleHooks; 31 | import org.apache.commons.lang3.StringUtils; 32 | 33 | import java.util.Optional; 34 | import java.util.function.Consumer; 35 | 36 | public class DragonSpawnEgg extends ForgeSpawnEggItem 37 | { 38 | public DragonSpawnEgg() 39 | { 40 | super(DMLRegistry.DRAGON, 0, 0, new Item.Properties()); 41 | } 42 | 43 | public static ItemStack create(Holder breed) 44 | { 45 | ItemStack stack = new ItemStack(DMLRegistry.SPAWN_EGG.get()); 46 | setBreed(stack, breed); 47 | return stack; 48 | } 49 | 50 | @Override 51 | public void verifyComponentsAfterLoad(ItemStack stack) 52 | { 53 | super.verifyComponentsAfterLoad(stack); 54 | 55 | // ensure a breed exists for this egg. if not, assign a random one. 56 | // possible cause is through commands, or other unnatural means. 57 | // todo: find a better way 58 | RegistryAccess reg = DistExecutor.safeRunForDist(() -> Minecraft.getInstance().level::registryAccess, () -> ServerLifecycleHooks.getCurrentServer()::registryAccess); 59 | RegistryOps ops = reg.createSerializationContext(NbtOps.INSTANCE); 60 | Holder breed = stack.getOrDefault(DataComponents.ENTITY_DATA, CustomData.EMPTY) 61 | .read(ops, DragonBreed.CODEC.fieldOf(TameableDragon.NBT_BREED)) 62 | .result() 63 | .orElse(null); 64 | if (breed == null) 65 | setBreed(stack, DragonBreed.getRandom(reg, RandomSource.create())); 66 | } 67 | 68 | private static void setBreed(ItemStack stack, Holder breed) 69 | { 70 | // add breed data, used by entity type spawning in general. annoying, but necessary. 71 | CustomData entityBreedId = stack.getOrDefault(DataComponents.ENTITY_DATA, CustomData.EMPTY) 72 | .update(t -> 73 | { 74 | t.putString("id", DMLRegistry.DRAGON.getId().toString()); // necessary otherwise CustomData throws an exception... 75 | t.putString(TameableDragon.NBT_BREED, breed.getRegisteredName()); 76 | }); 77 | stack.set(DataComponents.ENTITY_DATA, entityBreedId); 78 | 79 | // for colors and item name 80 | stack.set(DMLRegistry.DRAGON_BREED_COMPONENT.get(), breed); 81 | } 82 | 83 | @Override 84 | public Component getName(ItemStack stack) 85 | { 86 | Holder breed = stack.get(DMLRegistry.DRAGON_BREED_COMPONENT.get()); 87 | 88 | if (breed == null) return super.getName(stack); 89 | return Component.translatable(String.join(".", stack.getDescriptionId(), breed.getRegisteredName().replace(':', '.'))); 90 | } 91 | 92 | @Override 93 | public Optional spawnOffspringFromSpawnEgg(Player pPlayer, Mob pMob, EntityType pEntityType, ServerLevel server, Vec3 pPos, ItemStack stack) 94 | { 95 | // don't spawn an offspring if the breed doesn't match! 96 | 97 | CompoundTag tag = stack.getOrDefault(DataComponents.ENTITY_DATA, CustomData.EMPTY).copyTag(); 98 | Holder.Reference breed = DragonBreed.parse(tag.getString(TameableDragon.NBT_BREED), server.registryAccess()); 99 | if (breed == null || !breed.is(((TameableDragon) pMob).getBreedHolder())) 100 | return Optional.empty(); 101 | 102 | return super.spawnOffspringFromSpawnEgg(pPlayer, pMob, pEntityType, server, pPos, stack); 103 | } 104 | 105 | public static void populateTab(Consumer registrar) 106 | { 107 | if (Minecraft.getInstance().level != null) 108 | { 109 | var reg = Minecraft.getInstance().level.registryAccess(); 110 | DragonBreed.registry(reg).holders().forEach(breed -> registrar.accept(create(breed))); 111 | } 112 | } 113 | 114 | @SuppressWarnings("ConstantConditions") // ensured by item properties. 115 | public static int getColor(ItemStack stack, int tintIndex) 116 | { 117 | Holder breed = stack.get(DMLRegistry.DRAGON_BREED_COMPONENT.get()); 118 | if (breed == null || !breed.isBound()) return 0xff; 119 | return (tintIndex == 0? breed.get().primaryColor() : breed.get().secondaryColor()) | (255 << 24); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/DMLRegistry.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts; 2 | 3 | import com.github.kay9.dragonmounts.data.loot.DragonEggLootMod; 4 | import com.github.kay9.dragonmounts.data.loot.conditions.RandomChanceByConfig; 5 | import com.github.kay9.dragonmounts.dragon.DragonBreed; 6 | import com.github.kay9.dragonmounts.dragon.DragonSpawnEgg; 7 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 8 | import com.github.kay9.dragonmounts.dragon.abilities.*; 9 | import com.github.kay9.dragonmounts.dragon.egg.HatchableEggBlock; 10 | import com.github.kay9.dragonmounts.dragon.egg.HatchableEggBlockEntity; 11 | import com.github.kay9.dragonmounts.dragon.egg.habitats.*; 12 | import com.mojang.serialization.MapCodec; 13 | import net.minecraft.core.Holder; 14 | import net.minecraft.core.Registry; 15 | import net.minecraft.core.component.DataComponentType; 16 | import net.minecraft.core.registries.Registries; 17 | import net.minecraft.resources.ResourceKey; 18 | import net.minecraft.resources.ResourceLocation; 19 | import net.minecraft.sounds.SoundEvent; 20 | import net.minecraft.world.entity.EntityType; 21 | import net.minecraft.world.entity.MobCategory; 22 | import net.minecraft.world.item.Item; 23 | import net.minecraft.world.level.block.Block; 24 | import net.minecraft.world.level.block.entity.BlockEntityType; 25 | import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType; 26 | import net.minecraftforge.eventbus.api.IEventBus; 27 | import net.minecraftforge.registries.DeferredRegister; 28 | import net.minecraftforge.registries.RegistryManager; 29 | import net.minecraftforge.registries.RegistryObject; 30 | 31 | import java.util.HashMap; 32 | import java.util.Map; 33 | import java.util.function.Supplier; 34 | 35 | import static net.minecraftforge.registries.ForgeRegistries.Keys; 36 | 37 | public class DMLRegistry 38 | { 39 | protected static void init(IEventBus bus) 40 | { 41 | REGISTRIES.values().forEach(r -> r.register(bus)); 42 | REGISTRIES.clear(); // Registration happens once, no need to stick around. 43 | } 44 | 45 | private static final Map>, DeferredRegister> REGISTRIES = new HashMap<>(); 46 | 47 | public static final RegistryObject EGG_BLOCK = register("dragon_egg", Registries.BLOCK, HatchableEggBlock::new); 48 | 49 | public static final RegistryObject EGG_BLOCK_ITEM = register(EGG_BLOCK.getId().getPath(), Registries.ITEM, HatchableEggBlock.Item::new); 50 | public static final RegistryObject SPAWN_EGG = register("spawn_egg", Registries.ITEM, DragonSpawnEgg::new); 51 | 52 | public static final RegistryObject DRAGON_AMBIENT_SOUND = sound("entity.dragon.ambient"); 53 | public static final RegistryObject DRAGON_STEP_SOUND = sound("entity.dragon.step"); 54 | public static final RegistryObject DRAGON_DEATH_SOUND = sound("entity.dragon.death"); 55 | public static final RegistryObject GHOST_DRAGON_AMBIENT = sound("entity.dragon.ambient.ghost"); 56 | 57 | public static final RegistryObject> DRAGON = register("dragon", Registries.ENTITY_TYPE, () -> EntityType.Builder.of(TameableDragon::new, MobCategory.CREATURE).sized(TameableDragon.BASE_WIDTH, TameableDragon.BASE_HEIGHT).eyeHeight(3.375f).clientTrackingRange(10).updateInterval(3).build(DragonMountsLegacy.MOD_ID + ":dragon")); 58 | 59 | public static final RegistryObject> EGG_BLOCK_ENTITY = register("dragon_egg", Registries.BLOCK_ENTITY_TYPE, () -> BlockEntityType.Builder.of(HatchableEggBlockEntity::new, EGG_BLOCK.get()).build(null)); 60 | 61 | public static final RegistryObject> EGG_LOOT_MODIFIER = register("dragon_egg_loot", Keys.GLOBAL_LOOT_MODIFIER_SERIALIZERS, () -> DragonEggLootMod.CODEC); 62 | 63 | public static final RegistryObject RANDOM_CHANCE_CONFIG_CONDITION = register("random_chance_by_config", Registries.LOOT_CONDITION_TYPE, () -> new LootItemConditionType(RandomChanceByConfig.CODEC)); 64 | 65 | public static final RegistryObject>> DRAGON_BREED_COMPONENT = register("dragon_breed", Registries.DATA_COMPONENT_TYPE, () -> DataComponentType.>builder().persistent(DragonBreed.CODEC).networkSynchronized(DragonBreed.STREAM_CODEC).build()); 66 | 67 | // Dragon ability types 68 | // unnecessary to store these 69 | static 70 | { 71 | register("frost_walker", Ability.REGISTRY_KEY, () -> FrostWalkerAbility.CODEC); 72 | register("green_toes", Ability.REGISTRY_KEY, () -> GreenToesAbility.CODEC); 73 | register("snow_stepper", Ability.REGISTRY_KEY, () -> SnowStepperAbility.CODEC); 74 | register("hot_feet", Ability.REGISTRY_KEY, () -> HotFeetAbility.CODEC); 75 | register("reaper_step", Ability.REGISTRY_KEY, () -> ReaperStepAbility.CODEC); 76 | register("hydro_step", Ability.REGISTRY_KEY, () -> HydroStepAbility.CODEC); 77 | } 78 | 79 | // Dragon egg habitat types 80 | // unnecessary to store these 81 | static 82 | { 83 | register("picky", Habitat.REGISTRY_KEY, () -> PickyHabitat.CODEC); 84 | register("biome", Habitat.REGISTRY_KEY, () -> BiomeHabitat.CODEC); 85 | register("in_fluid", Habitat.REGISTRY_KEY, () -> FluidHabitat.CODEC); 86 | register("world_height", Habitat.REGISTRY_KEY, () -> HeightHabitat.CODEC); 87 | register("light", Habitat.REGISTRY_KEY, () -> LightHabitat.CODEC); 88 | register("nearby_blocks", Habitat.REGISTRY_KEY, () -> NearbyBlocksHabitat.CODEC); 89 | register("dragon_breath", Habitat.REGISTRY_KEY, () -> DragonBreathHabitat.CODEC); 90 | 91 | } 92 | 93 | private static RegistryObject sound(String name) 94 | { 95 | return register(name, Registries.SOUND_EVENT, () -> SoundEvent.createVariableRangeEvent(DragonMountsLegacy.id(name))); 96 | } 97 | 98 | @SuppressWarnings("unchecked") 99 | private static RegistryObject register(String name, ResourceKey> forType, Supplier sup) 100 | { 101 | var registry = (DeferredRegister) REGISTRIES.computeIfAbsent(forType, t -> 102 | { 103 | var fr = RegistryManager.ACTIVE.getRegistry(forType); 104 | if (fr == null) return DeferredRegister.create(forType, DragonMountsLegacy.MOD_ID); 105 | return DeferredRegister.create(fr, DragonMountsLegacy.MOD_ID); 106 | }); 107 | return registry.register(name, sup); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/DragonMountsLegacy.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts; 2 | 3 | import com.github.kay9.dragonmounts.client.*; 4 | import com.github.kay9.dragonmounts.data.CrossBreedingManager; 5 | import com.github.kay9.dragonmounts.data.model.DragonModelPropertiesListener; 6 | import com.github.kay9.dragonmounts.dragon.DragonBreed; 7 | import com.github.kay9.dragonmounts.dragon.DragonSpawnEgg; 8 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 9 | import com.github.kay9.dragonmounts.dragon.egg.HatchableEggBlock; 10 | import com.mojang.serialization.Codec; 11 | import net.minecraft.client.KeyMapping; 12 | import net.minecraft.client.Minecraft; 13 | import net.minecraft.client.color.item.ItemColors; 14 | import net.minecraft.client.renderer.entity.EntityRenderers; 15 | import net.minecraft.core.BlockPos; 16 | import net.minecraft.core.Registry; 17 | import net.minecraft.network.syncher.EntityDataSerializers; 18 | import net.minecraft.resources.ResourceKey; 19 | import net.minecraft.resources.ResourceLocation; 20 | import net.minecraft.server.level.ServerLevel; 21 | import net.minecraft.server.packs.resources.PreparableReloadListener; 22 | import net.minecraft.server.packs.resources.ReloadableResourceManager; 23 | import net.minecraft.world.InteractionHand; 24 | import net.minecraft.world.entity.EntityType; 25 | import net.minecraft.world.entity.LivingEntity; 26 | import net.minecraft.world.entity.ai.attributes.AttributeSupplier; 27 | import net.minecraft.world.entity.player.Player; 28 | import net.minecraft.world.item.CreativeModeTab; 29 | import net.minecraft.world.item.CreativeModeTabs; 30 | import net.minecraft.world.item.ItemStack; 31 | import net.minecraft.world.level.Level; 32 | import net.minecraft.world.level.block.Blocks; 33 | import net.minecraftforge.client.ForgeHooksClient; 34 | import net.minecraftforge.client.model.geometry.IGeometryLoader; 35 | import org.apache.logging.log4j.LogManager; 36 | import org.apache.logging.log4j.Logger; 37 | import org.apache.logging.log4j.util.TriConsumer; 38 | 39 | import java.util.function.BiConsumer; 40 | import java.util.function.Consumer; 41 | 42 | /** 43 | * Dragon Mounts Legacy 44 | *
45 | * Main mod information like the ID and logger is found here. 46 | * Events that pertain to the game are also present here. 47 | * Everything in the mod is a network with this at the core of it all. 48 | * Load events register our custom content into the game, 49 | * Game events are the way the mod interacts with the game's behavior. 50 | * Event methods can be triggered by: 51 | * - Mod loader event dispatchers ({@link ForgeModImpl}) 52 | * - Mixins that inject callbacks to here 53 | */ 54 | public class DragonMountsLegacy 55 | { 56 | public static final String MOD_ID = "dragonmounts"; 57 | public static final Logger LOG = LogManager.getLogger(MOD_ID); 58 | 59 | public static ResourceLocation id(String path) 60 | { 61 | return ResourceLocation.tryBuild(MOD_ID, path); 62 | } 63 | 64 | // ======================== 65 | // Load Events 66 | // ======================== 67 | 68 | static void registerRenderers() 69 | { 70 | EntityRenderers.register(DMLRegistry.DRAGON.get(), DragonRenderer::new); 71 | ForgeHooksClient.registerLayerDefinition(DragonRenderer.MODEL_LOCATION, () -> DragonModel.createBodyLayer(DragonModel.Properties.STANDARD)); 72 | } 73 | 74 | static void registerEggModelLoader(BiConsumer> registrar) 75 | { 76 | registrar.accept("dragon_egg", DragonEggModel.Loader.INSTANCE); 77 | } 78 | 79 | static void registerItemColors(ItemColors colors) 80 | { 81 | colors.register(DragonSpawnEgg::getColor, DMLRegistry.SPAWN_EGG.get()); 82 | } 83 | 84 | @SuppressWarnings("ConstantConditions") // client instance is null on data gen 85 | static void registerReloadListenersEarly() 86 | { 87 | if (Minecraft.getInstance() != null) 88 | { 89 | ((ReloadableResourceManager) Minecraft.getInstance().getResourceManager()).registerReloadListener(DragonModelPropertiesListener.INSTANCE); // Dragon Model Properties need to be reloaded before Entity Models are! 90 | } 91 | } 92 | 93 | static void registerKeyBindings(Consumer registrar) 94 | { 95 | KeyMappings.registerKeybinds(registrar); 96 | } 97 | 98 | static void registerReloadListeners(Consumer registrar) 99 | { 100 | registrar.accept(CrossBreedingManager.INSTANCE); 101 | } 102 | 103 | static void registerDatapacks(TriConsumer>, Codec, Codec> registrar) 104 | { 105 | registrar.accept(DragonBreed.REGISTRY_KEY, DragonBreed.DIRECT_CODEC, DragonBreed.NETWORK_CODEC); 106 | } 107 | 108 | static void registerCreativeTabItems(ResourceKey tab, Consumer registrar) 109 | { 110 | if (tab == CreativeModeTabs.SPAWN_EGGS) DragonSpawnEgg.populateTab(registrar); 111 | if (tab == CreativeModeTabs.FUNCTIONAL_BLOCKS) HatchableEggBlock.populateTab(registrar); 112 | } 113 | 114 | static void registerEntityAttributes(BiConsumer, AttributeSupplier> registrar) 115 | { 116 | registrar.accept(DMLRegistry.DRAGON.get(), TameableDragon.createAttributes().build()); 117 | } 118 | 119 | static void registerEntityDataSerializers() 120 | { 121 | EntityDataSerializers.registerSerializer(TameableDragon.DRAGON_BREED_SERIALIZER); 122 | } 123 | 124 | // ======================== 125 | // Game Events 126 | // ======================== 127 | 128 | static boolean overrideVanillaDragonEgg(Level level, BlockPos pos, Player player) 129 | { 130 | if (DMLConfig.allowEggOverride() && level.getBlockState(pos).is(Blocks.DRAGON_EGG)) 131 | { 132 | var end = DragonBreed.registry(level.registryAccess()).getHolder(DragonBreed.BuiltIn.END); 133 | if (end.isPresent()) 134 | { 135 | if (level.isClientSide) player.swing(InteractionHand.MAIN_HAND); 136 | else 137 | { 138 | var state = DMLRegistry.EGG_BLOCK.get().defaultBlockState().setValue(HatchableEggBlock.HATCHING, true); 139 | HatchableEggBlock.place((ServerLevel) level, pos, state, end.get()); 140 | } 141 | return true; 142 | } 143 | } 144 | return false; 145 | } 146 | 147 | static void clientTick(boolean head) 148 | { 149 | if (!head) MountControlsMessenger.tick(); 150 | } 151 | 152 | static void onKeyPress(int key, int action, int modifiers) 153 | { 154 | KeyMappings.handleKeyPress(key, action); 155 | } 156 | } -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/dragon/ai/DragonFollowOwnerGoal.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.dragon.ai; 2 | 3 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 4 | import net.minecraft.core.BlockPos; 5 | import net.minecraft.world.entity.LivingEntity; 6 | import net.minecraft.world.entity.ai.goal.Goal; 7 | import net.minecraft.world.level.LevelReader; 8 | import net.minecraft.world.level.block.LeavesBlock; 9 | import net.minecraft.world.level.block.state.BlockState; 10 | import net.minecraft.world.level.pathfinder.PathType; 11 | import net.minecraft.world.level.pathfinder.WalkNodeEvaluator; 12 | import net.minecraft.world.phys.AABB; 13 | 14 | import java.util.EnumSet; 15 | 16 | /** 17 | * Goal for dragon to follow its owner. 18 | *

19 | * Mostly copied from FollowOwnerGoal, but with some modifications to fix an issue. 20 | * Also allows dragon to tp to owner in the air, so they don't get stuck until the owner lands. 21 | * 22 | * @author AnimalsWritingCode 23 | * 24 | * @see net.minecraft.world.entity.ai.goal.FollowOwnerGoal 25 | */ 26 | public class DragonFollowOwnerGoal extends Goal 27 | { 28 | private static final int MIN_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING = 2; 29 | private static final int MAX_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING = 3; 30 | private static final int MIN_VERTICAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING = 0; 31 | private static final int MAX_VERTICAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING = 1; 32 | private final TameableDragon dragon; 33 | private LivingEntity owner; 34 | private final LevelReader level; 35 | private final double speedModifier; 36 | private int timeToRecalcPath; 37 | private final float stopDistance; 38 | private final float startDistance; 39 | private final float teleportDistance; 40 | private float oldWaterCost; 41 | 42 | public DragonFollowOwnerGoal(TameableDragon dragon, double speedModifier, float startDistance, float stopDistance, float teleportDistance) 43 | { 44 | this.dragon = dragon; 45 | this.level = dragon.level(); 46 | this.speedModifier = speedModifier; 47 | this.startDistance = startDistance; 48 | this.stopDistance = stopDistance; 49 | this.teleportDistance = teleportDistance; 50 | setFlags(EnumSet.of(Goal.Flag.MOVE, Goal.Flag.LOOK)); 51 | } 52 | 53 | public boolean canUse() 54 | { 55 | LivingEntity livingentity = dragon.getOwner(); 56 | if (livingentity == null) { 57 | return false; 58 | } 59 | if (livingentity.isSpectator()) 60 | { 61 | return false; 62 | } 63 | if (dragon.isOrderedToSit()) 64 | { 65 | return false; 66 | } 67 | if (this.dragon.distanceToSqr(livingentity) < (double)(this.startDistance * this.startDistance)) 68 | { 69 | return false; 70 | } 71 | 72 | this.owner = livingentity; 73 | return true; 74 | } 75 | 76 | public boolean canContinueToUse() 77 | { 78 | if (dragon.getNavigation().isDone()) 79 | { 80 | return false; 81 | } 82 | if (dragon.isOrderedToSit()) 83 | { 84 | return false; 85 | } 86 | return this.dragon.distanceToSqr(this.owner) >= (double)(this.stopDistance * this.stopDistance); 87 | } 88 | 89 | public void start() 90 | { 91 | timeToRecalcPath = 0; 92 | oldWaterCost = dragon.getPathfindingMalus(PathType.WATER); 93 | dragon.setPathfindingMalus(PathType.WATER, 0.0F); 94 | } 95 | 96 | public void stop() 97 | { 98 | dragon.getNavigation().stop(); 99 | dragon.setPathfindingMalus(PathType.WATER, oldWaterCost); 100 | } 101 | 102 | public void tick() 103 | { 104 | this.dragon.getLookControl().setLookAt(this.owner, 10.0F, (float)this.dragon.getMaxHeadXRot()); 105 | if (--this.timeToRecalcPath <= 0) 106 | { 107 | timeToRecalcPath = adjustedTickDelay(10); 108 | if (!dragon.isLeashed() && !dragon.isPassenger()) 109 | { 110 | if (dragon.distanceToSqr(this.owner) >= (teleportDistance * teleportDistance)) 111 | { 112 | teleportToOwner(); 113 | } 114 | else if ( 115 | !dragon.isFlying() 116 | && dragon.canFly() 117 | && (this.owner.blockPosition().getY() - dragon.blockPosition().getY()) >= startDistance) 118 | { 119 | dragon.liftOff(); 120 | } 121 | else 122 | { 123 | dragon.getNavigation().moveTo(this.owner, speedModifier); 124 | } 125 | 126 | } 127 | } 128 | } 129 | 130 | private void teleportToOwner() 131 | { 132 | BlockPos ownerPos = this.owner.blockPosition(); 133 | 134 | for(int i = 0; i < 10; ++i) 135 | { 136 | BlockPos target = randomBlockPosNearPos( 137 | ownerPos, 138 | MIN_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING, 139 | MAX_HORIZONTAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING, 140 | MIN_VERTICAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING, 141 | MAX_VERTICAL_DISTANCE_FROM_PLAYER_WHEN_TELEPORTING 142 | ); 143 | boolean flag = maybeTeleportTo(target); 144 | if (flag) 145 | { 146 | return; 147 | } 148 | } 149 | 150 | } 151 | 152 | private boolean maybeTeleportTo(BlockPos pos) 153 | { 154 | if (this.owner.blockPosition().closerThan(pos, 2.0D)) 155 | { 156 | return false; 157 | } 158 | if (!canTeleportTo(pos)) 159 | { 160 | return false; 161 | } 162 | dragon.moveTo(pos.getX() + 0.5D, pos.getY(), pos.getZ(), dragon.getYRot(), dragon.getXRot()); 163 | dragon.getNavigation().stop(); 164 | return true; 165 | } 166 | 167 | private boolean canTeleportTo(BlockPos pos) 168 | { 169 | if (!dragon.canFly()) 170 | { 171 | PathType blockpathtypes = WalkNodeEvaluator.getPathTypeStatic(dragon, pos); 172 | if (blockpathtypes != PathType.WALKABLE) 173 | { 174 | return false; 175 | } 176 | 177 | BlockState blockstate = level.getBlockState(pos.below()); 178 | if (blockstate.getBlock() instanceof LeavesBlock) 179 | { 180 | return false; 181 | } 182 | } 183 | 184 | BlockPos blockPos = pos.subtract(dragon.blockPosition()); 185 | AABB targetBoundingBox = dragon.getBoundingBox().move(blockPos); 186 | return level.noCollision(dragon, targetBoundingBox) 187 | && !level.containsAnyLiquid(targetBoundingBox); 188 | } 189 | 190 | private int randomIntInclusive(int min, int max) 191 | { 192 | return dragon.getRandom().nextInt(max - min + 1) + min; 193 | } 194 | 195 | private int randomIntInclusive(int farLow, int nearLow, int nearHigh, int farHigh) 196 | { 197 | if (nearLow == nearHigh) 198 | { 199 | return randomIntInclusive(farLow, farHigh); 200 | } 201 | 202 | return dragon.getRandom().nextBoolean() ? 203 | randomIntInclusive(farLow, nearLow) : 204 | randomIntInclusive(nearHigh, farHigh); 205 | } 206 | 207 | private BlockPos randomBlockPosNearPos(BlockPos origin, int minDist, int maxDist, int minYDist, int maxYDist) 208 | { 209 | int x = randomIntInclusive(-maxDist, -minDist, minDist, maxDist); 210 | int y = randomIntInclusive(-maxYDist, -minYDist, minYDist, maxYDist); 211 | int z = randomIntInclusive(-maxDist, -minDist, minDist, maxDist); 212 | return origin.offset(x, y, z); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/client/DragonEggModel.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.client; 2 | 3 | import com.github.kay9.dragonmounts.DMLRegistry; 4 | import com.github.kay9.dragonmounts.dragon.egg.HatchableEggBlockEntity; 5 | import com.google.common.base.Suppliers; 6 | import com.google.common.collect.ImmutableMap; 7 | import com.google.gson.JsonDeserializationContext; 8 | import com.google.gson.JsonObject; 9 | import com.google.gson.JsonParseException; 10 | import com.mojang.blaze3d.vertex.PoseStack; 11 | import net.minecraft.client.Minecraft; 12 | import net.minecraft.client.multiplayer.ClientLevel; 13 | import net.minecraft.client.renderer.RenderType; 14 | import net.minecraft.client.renderer.block.model.BakedQuad; 15 | import net.minecraft.client.renderer.block.model.BlockModel; 16 | import net.minecraft.client.renderer.block.model.ItemOverrides; 17 | import net.minecraft.client.renderer.texture.TextureAtlasSprite; 18 | import net.minecraft.client.resources.model.BakedModel; 19 | import net.minecraft.client.resources.model.Material; 20 | import net.minecraft.client.resources.model.ModelBaker; 21 | import net.minecraft.client.resources.model.ModelState; 22 | import net.minecraft.core.BlockPos; 23 | import net.minecraft.core.Direction; 24 | import net.minecraft.util.RandomSource; 25 | import net.minecraft.world.entity.LivingEntity; 26 | import net.minecraft.world.item.ItemDisplayContext; 27 | import net.minecraft.world.item.ItemStack; 28 | import net.minecraft.world.level.BlockAndTintGetter; 29 | import net.minecraft.world.level.block.Blocks; 30 | import net.minecraft.world.level.block.state.BlockState; 31 | import net.minecraftforge.client.model.IDynamicBakedModel; 32 | import net.minecraftforge.client.model.data.ModelData; 33 | import net.minecraftforge.client.model.data.ModelProperty; 34 | import net.minecraftforge.client.model.geometry.IGeometryBakingContext; 35 | import net.minecraftforge.client.model.geometry.IGeometryLoader; 36 | import net.minecraftforge.client.model.geometry.IUnbakedGeometry; 37 | import org.jetbrains.annotations.NotNull; 38 | import org.jetbrains.annotations.Nullable; 39 | 40 | import java.io.IOException; 41 | import java.util.List; 42 | import java.util.function.Function; 43 | import java.util.function.Supplier; 44 | 45 | /** 46 | * A dynamic BakedModel which returns quads based on the given breed of the tile entity. 47 | */ 48 | public class DragonEggModel implements IUnbakedGeometry 49 | { 50 | private final ImmutableMap models; 51 | 52 | public DragonEggModel(ImmutableMap models) 53 | { 54 | this.models = models; 55 | } 56 | 57 | @Override 58 | public BakedModel bake(IGeometryBakingContext context, ModelBaker baker, Function spriteGetter, ModelState modelState, ItemOverrides overrides) 59 | { 60 | var baked = ImmutableMap.builder(); 61 | for (var entry : models.entrySet()) 62 | { 63 | var unbaked = entry.getValue(); 64 | unbaked.resolveParents(baker::getModel); 65 | baked.put(entry.getKey(), unbaked.bake(baker, unbaked, spriteGetter, modelState, true)); 66 | } 67 | return new Baked(baked.build(), overrides); 68 | } 69 | 70 | private record Data(String breedId) 71 | { 72 | private static final ModelProperty PROPERTY = new ModelProperty<>(); 73 | } 74 | 75 | public static class Baked implements IDynamicBakedModel 76 | { 77 | private static final Supplier FALLBACK = Suppliers.memoize(() -> Minecraft.getInstance().getBlockRenderer().getBlockModel(Blocks.DRAGON_EGG.defaultBlockState())); 78 | 79 | private final ImmutableMap models; 80 | private final ItemOverrides overrides; 81 | 82 | public Baked(ImmutableMap models, ItemOverrides overrides) 83 | { 84 | this.models = models; 85 | this.overrides = new ItemModelResolver(this, overrides); 86 | } 87 | 88 | @Override 89 | public @NotNull List getQuads(@Nullable BlockState state, @Nullable Direction side, @NotNull RandomSource rand, @NotNull ModelData extraData, @Nullable RenderType renderType) 90 | { 91 | var data = extraData.get(Data.PROPERTY); 92 | if (data != null) 93 | return models.get(data.breedId()).getQuads(state, side, rand, extraData, renderType); 94 | return FALLBACK.get().getQuads(state, side, rand, extraData, renderType); 95 | } 96 | 97 | @Override 98 | public boolean useAmbientOcclusion() 99 | { 100 | return true; 101 | } 102 | 103 | @Override 104 | public boolean isGui3d() 105 | { 106 | return true; 107 | } 108 | 109 | @Override 110 | public boolean usesBlockLight() 111 | { 112 | return true; 113 | } 114 | 115 | @Override 116 | public boolean isCustomRenderer() 117 | { 118 | return false; 119 | } 120 | 121 | @Override 122 | public TextureAtlasSprite getParticleIcon() 123 | { 124 | return FALLBACK.get().getParticleIcon(); 125 | } 126 | 127 | @Override 128 | public TextureAtlasSprite getParticleIcon(@NotNull ModelData modelData) 129 | { 130 | var data = modelData.get(Data.PROPERTY); 131 | if (data != null) 132 | return models.get(data.breedId()).getParticleIcon(modelData); 133 | 134 | return getParticleIcon(); 135 | } 136 | 137 | @Override 138 | public ItemOverrides getOverrides() 139 | { 140 | return overrides; 141 | } 142 | 143 | @Override 144 | public BakedModel applyTransform(ItemDisplayContext transformType, PoseStack poseStack, boolean applyLeftHandTransform) 145 | { 146 | return FALLBACK.get().applyTransform(transformType, poseStack, applyLeftHandTransform); 147 | } 148 | 149 | @Override 150 | public @NotNull ModelData getModelData(@NotNull BlockAndTintGetter level, @NotNull BlockPos pos, @NotNull BlockState state, @NotNull ModelData modelData) 151 | { 152 | if (level.getBlockEntity(pos) instanceof HatchableEggBlockEntity e && e.hasBreed()) 153 | return modelData.derive() 154 | .with(Data.PROPERTY, new Data(e.getBreedHolder().getRegisteredName())) 155 | .build(); 156 | 157 | return modelData; 158 | } 159 | } 160 | 161 | public static class ItemModelResolver extends ItemOverrides 162 | { 163 | private final Baked owner; 164 | private final ItemOverrides nested; 165 | 166 | public ItemModelResolver(Baked owner, ItemOverrides nested) 167 | { 168 | this.owner = owner; 169 | this.nested = nested; 170 | } 171 | 172 | @Nullable 173 | @Override 174 | public BakedModel resolve(BakedModel original, ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int pSeed) 175 | { 176 | var override = nested.resolve(original, stack, level, entity, pSeed); 177 | if (override != original) return override; 178 | 179 | var breed = stack.get(DMLRegistry.DRAGON_BREED_COMPONENT.get()); 180 | if (breed != null) 181 | { 182 | var model = owner.models.get(breed.getRegisteredName()); 183 | if (model != null) return model; 184 | } 185 | 186 | return original; 187 | } 188 | } 189 | 190 | public static class Loader implements IGeometryLoader 191 | { 192 | public static final Loader INSTANCE = new Loader(); 193 | 194 | private Loader() {} 195 | 196 | @Override 197 | public DragonEggModel read(JsonObject jsonObject, JsonDeserializationContext deserializer) throws JsonParseException 198 | { 199 | var models = ImmutableMap.builder(); 200 | var dir = "models/block/dragon_eggs"; 201 | var length = "models/".length(); 202 | var suffixLength = ".json".length(); 203 | for (var entry : Minecraft.getInstance().getResourceManager().listResources(dir, f -> f.getPath().endsWith(".json")).entrySet()) 204 | { 205 | var rl = entry.getKey(); 206 | var path = rl.getPath(); 207 | path = path.substring(length, path.length() - suffixLength); 208 | var id = String.format("%s:%s", rl.getNamespace(), path.substring("block/dragon_eggs/".length(), path.length() - "_dragon_egg".length())); 209 | 210 | try (var reader = entry.getValue().openAsReader()) 211 | { 212 | models.put(id, BlockModel.fromStream(reader)); 213 | } 214 | catch (IOException e) 215 | { 216 | throw new JsonParseException(e); 217 | } 218 | } 219 | 220 | return new DragonEggModel(models.build()); 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/client/DragonRenderer.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts.client; 2 | 3 | import com.github.kay9.dragonmounts.DragonMountsLegacy; 4 | import com.github.kay9.dragonmounts.data.model.DragonModelPropertiesListener; 5 | import com.github.kay9.dragonmounts.dragon.DragonBreed; 6 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 7 | import com.google.common.collect.ImmutableMap; 8 | import com.mojang.blaze3d.vertex.DefaultVertexFormat; 9 | import com.mojang.blaze3d.vertex.PoseStack; 10 | import com.mojang.blaze3d.vertex.VertexFormat; 11 | import com.mojang.math.Axis; 12 | import net.minecraft.Util; 13 | import net.minecraft.client.model.geom.ModelLayerLocation; 14 | import net.minecraft.client.renderer.MultiBufferSource; 15 | import net.minecraft.client.renderer.RenderType; 16 | import net.minecraft.client.renderer.culling.Frustum; 17 | import net.minecraft.client.renderer.entity.EntityRendererProvider; 18 | import net.minecraft.client.renderer.entity.MobRenderer; 19 | import net.minecraft.client.renderer.entity.layers.RenderLayer; 20 | import net.minecraft.client.renderer.texture.OverlayTexture; 21 | import net.minecraft.core.Holder; 22 | import net.minecraft.resources.ResourceLocation; 23 | import org.jetbrains.annotations.Nullable; 24 | 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | import java.util.function.Function; 28 | 29 | public class DragonRenderer extends MobRenderer 30 | { 31 | public static final ModelLayerLocation MODEL_LOCATION = new ModelLayerLocation(DragonMountsLegacy.id("dragon"), "main"); 32 | private static final ResourceLocation[] DEFAULT_TEXTURES = computeTextureCacheFor(DragonBreed.BuiltIn.END.location()); 33 | private static final ResourceLocation DISSOLVE_TEXTURE = DragonMountsLegacy.id("textures/entity/dragon/dissolve.png"); 34 | private static final int LAYER_BODY = 0; 35 | private static final int LAYER_GLOW = 1; 36 | private static final int LAYER_SADDLE = 2; 37 | 38 | private final DragonModel defaultModel; 39 | private final Map modelCache; 40 | private final Map textureCache = new HashMap<>(8); 41 | 42 | public DragonRenderer(EntityRendererProvider.Context modelBakery) 43 | { 44 | super(modelBakery, new DragonModel(modelBakery.bakeLayer(MODEL_LOCATION)), 2); 45 | 46 | this.defaultModel = model; 47 | this.modelCache = bakeModels(modelBakery); 48 | 49 | addLayer(GLOW_LAYER); 50 | addLayer(SADDLE_LAYER); 51 | addLayer(DEATH_LAYER); 52 | } 53 | 54 | @Override 55 | public boolean shouldRender(TameableDragon dragon, Frustum pCamera, double pCamX, double pCamY, double pCamZ) 56 | { 57 | return dragon.getBreed() != null && super.shouldRender(dragon, pCamera, pCamX, pCamY, pCamZ); 58 | } 59 | 60 | @Override 61 | public void render(TameableDragon dragon, float pEntityYaw, float pPartialTicks, PoseStack pMatrixStack, MultiBufferSource pBuffer, int pPackedLight) 62 | { 63 | this.model = getModel(dragon); 64 | super.render(dragon, pEntityYaw, pPartialTicks, pMatrixStack, pBuffer, pPackedLight); 65 | } 66 | 67 | @SuppressWarnings("ConstantConditions") 68 | private DragonModel getModel(TameableDragon dragon) 69 | { 70 | var breed = dragon.getBreedHolder(); 71 | if (breed == null) return defaultModel; 72 | 73 | var selected = modelCache.get(breed.unwrapKey().get().location()); 74 | if (selected == null) return defaultModel; 75 | 76 | return selected; 77 | } 78 | 79 | // During death, do not use the standard rendering and let the death layer handle it. Hacky, but better than mixins. 80 | @Nullable 81 | @Override 82 | protected RenderType getRenderType(TameableDragon entity, boolean visible, boolean invisToClient, boolean glowing) 83 | { 84 | return entity.deathTime > 0? null : super.getRenderType(entity, visible, invisToClient, glowing); 85 | } 86 | 87 | @Override 88 | public ResourceLocation getTextureLocation(TameableDragon dragon) 89 | { 90 | return getTextureForLayer(dragon.getBreedHolder(), LAYER_BODY); 91 | } 92 | 93 | @SuppressWarnings("ConstantConditions") 94 | public ResourceLocation getTextureForLayer(@Nullable Holder breed, int layer) 95 | { 96 | if (breed == null) return DEFAULT_TEXTURES[layer]; 97 | 98 | // we need to compute texture locations now rather than earlier due to the fact that breeds don't exist then. 99 | return textureCache.computeIfAbsent(breed.unwrapKey().get().location(), DragonRenderer::computeTextureCacheFor)[layer]; 100 | } 101 | 102 | @Override 103 | protected void setupRotations(TameableDragon dragon, PoseStack ps, float age, float yaw, float partials, float scale) 104 | { 105 | super.setupRotations(dragon, ps, age, yaw, partials, scale); 106 | var animator = dragon.getAnimator(); 107 | super.scale(dragon, ps, partials); 108 | scale = dragon.getAgeScale(); 109 | ps.scale(scale, scale, scale); 110 | ps.translate(animator.getModelOffsetX(), animator.getModelOffsetY(), animator.getModelOffsetZ()); 111 | ps.translate(0, 1.5, 0.5); // change rotation point 112 | ps.mulPose(Axis.XP.rotationDegrees(animator.getModelPitch(partials))); // rotate near the saddle so we can support the player 113 | ps.translate(0, -1.5, -0.5); // restore rotation point 114 | } 115 | 116 | // dragons dissolve during death, not flip. 117 | @Override 118 | protected float getFlipDegrees(TameableDragon pLivingEntity) 119 | { 120 | return 0; 121 | } 122 | 123 | private Map bakeModels(EntityRendererProvider.Context bakery) 124 | { 125 | var builder = ImmutableMap.builder(); 126 | for (var entry : DragonModelPropertiesListener.INSTANCE.pollDefinitions().entrySet()) 127 | builder.put(entry.getKey(), new DragonModel(bakery.bakeLayer(entry.getValue()))); 128 | return builder.build(); 129 | } 130 | 131 | private static ResourceLocation[] computeTextureCacheFor(ResourceLocation breedId) 132 | { 133 | final String[] TEXTURES = {"body", "glow", "saddle"}; // 0, 1, 2 134 | 135 | ResourceLocation[] cache = new ResourceLocation[TEXTURES.length]; 136 | for (int i = 0; i < TEXTURES.length; i++) 137 | cache[i] = ResourceLocation.tryBuild(breedId.getNamespace(), "textures/entity/dragon/" + breedId.getPath() + "/" + TEXTURES[i] + ".png"); 138 | return cache; 139 | } 140 | 141 | public final RenderLayer GLOW_LAYER = new RenderLayer<>(this) 142 | { 143 | @Override 144 | public void render(PoseStack pMatrixStack, MultiBufferSource buffer, int pPackedLight, TameableDragon dragon, float pLimbSwing, float pLimbSwingAmount, float pPartialTicks, float pAgeInTicks, float pNetHeadYaw, float pHeadPitch) 145 | { 146 | if (dragon.deathTime == 0) 147 | { 148 | var type = CustomRenderTypes.glow(getTextureForLayer(dragon.getBreedHolder(), LAYER_GLOW)); 149 | model.renderToBuffer(pMatrixStack, buffer.getBuffer(type), 0xffffff, OverlayTexture.NO_OVERLAY, -1); 150 | } 151 | } 152 | }; 153 | public final RenderLayer SADDLE_LAYER = new RenderLayer<>(this) 154 | { 155 | @Override 156 | public void render(PoseStack ps, MultiBufferSource buffer, int light, TameableDragon dragon, float pLimbSwing, float pLimbSwingAmount, float pPartialTicks, float pAgeInTicks, float pNetHeadYaw, float pHeadPitch) 157 | { 158 | if (dragon.isSaddled()) 159 | renderColoredCutoutModel(model, getTextureForLayer(dragon.getBreedHolder(), LAYER_SADDLE), ps, buffer, light, dragon, -1); 160 | } 161 | }; 162 | public final RenderLayer DEATH_LAYER = new RenderLayer<>(this) 163 | { 164 | @Override 165 | public void render(PoseStack ps, MultiBufferSource buffer, int light, TameableDragon dragon, float limbSwing, float limbSwingAmount, float partials, float age, float yaw, float pitch) 166 | { 167 | if (dragon.deathTime > 0) 168 | { 169 | int delta = (int) (255 * (dragon.deathTime / (float) dragon.getMaxDeathTime())); 170 | model.renderToBuffer(ps, buffer.getBuffer(CustomRenderTypes.DISSOLVE), light, OverlayTexture.NO_OVERLAY, 0xf | (delta << 24)); 171 | model.renderToBuffer(ps, buffer.getBuffer(RenderType.entityDecal(getTextureLocation(dragon))), light, OverlayTexture.pack(0, true), -1); 172 | } 173 | } 174 | }; 175 | 176 | private static class CustomRenderTypes extends RenderType 177 | { 178 | private static final RenderType DISSOLVE = RenderType.dragonExplosionAlpha(DISSOLVE_TEXTURE); 179 | private static final Function GLOW_FUNC = Util.memoize(texture -> create("eyes", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, false, true, CompositeState.builder() 180 | .setShaderState(RENDERTYPE_EYES_SHADER) 181 | .setTextureState(new TextureStateShard(texture, false, false)) 182 | .setTransparencyState(TRANSLUCENT_TRANSPARENCY) 183 | .setWriteMaskState(COLOR_WRITE) 184 | .createCompositeState(false))); 185 | 186 | private static RenderType glow(ResourceLocation texture) 187 | { 188 | return GLOW_FUNC.apply(texture); 189 | } 190 | 191 | @SuppressWarnings("DataFlowIssue") 192 | private CustomRenderTypes() 193 | { 194 | // dummy 195 | super(null, null, null, 0, false, true, null, null); 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/main/java/com/github/kay9/dragonmounts/DMLConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.kay9.dragonmounts; 2 | 3 | import com.github.kay9.dragonmounts.data.loot.DragonEggLootMod; 4 | import com.github.kay9.dragonmounts.dragon.DragonBreed; 5 | import com.github.kay9.dragonmounts.dragon.TameableDragon; 6 | import com.google.common.collect.ImmutableMap; 7 | import net.minecraft.resources.ResourceKey; 8 | import net.minecraft.world.level.storage.loot.LootTable; 9 | import net.minecraftforge.common.ForgeConfigSpec; 10 | 11 | import java.util.Map; 12 | 13 | import static com.github.kay9.dragonmounts.dragon.DragonBreed.BuiltIn.*; 14 | 15 | public class DMLConfig 16 | { 17 | public static final ForgeConfigSpec COMMON_SPEC; 18 | 19 | private static final ForgeConfigSpec.BooleanValue ALLOW_EGG_OVERRIDE; 20 | 21 | public static boolean allowEggOverride() 22 | { 23 | return ALLOW_EGG_OVERRIDE.get(); 24 | } 25 | 26 | private static final ForgeConfigSpec.BooleanValue REPLENISH_EGGS; 27 | 28 | public static boolean replenishEggs() 29 | { 30 | return REPLENISH_EGGS.get(); 31 | } 32 | 33 | private static final ForgeConfigSpec.BooleanValue USE_LOOT_TABLES; 34 | 35 | public static boolean useLootTables() 36 | { 37 | return USE_LOOT_TABLES.get(); 38 | } 39 | 40 | private static final ForgeConfigSpec.BooleanValue UPDATE_HABITATS; 41 | 42 | public static boolean updateHabitats() 43 | { 44 | return UPDATE_HABITATS.get(); 45 | } 46 | 47 | private static final Map EGG_CHANCES; 48 | 49 | public static float getEggChanceFor(String configTarget) 50 | { 51 | var chance = EGG_CHANCES.get(configTarget); 52 | if (chance == null) return -1f; 53 | return chance.get().floatValue(); 54 | } 55 | 56 | private static final Map REPRO_LIMITS; 57 | 58 | public static int getReproLimitFor(String configTarget) 59 | { 60 | var limit = REPRO_LIMITS.get(configTarget.replace("config:", "")); 61 | if (limit == null) return -1; 62 | return limit.get(); 63 | } 64 | 65 | static 66 | { 67 | var configurator = new ForgeConfigSpec.Builder() 68 | .push("config"); 69 | 70 | ALLOW_EGG_OVERRIDE = configurator.comment( 71 | "Allow the vanilla ender egg to be interacted with? (Hatchable)", 72 | "Useful to help with mod compatibility") 73 | .define("allow_egg_override", true); 74 | REPLENISH_EGGS = configurator.comment( 75 | "Should Ender Dragon Eggs replenish on the exit portal after a respawned dragon is defeated?", 76 | "Useful for multiplayer scenarios.", 77 | "NOTE: This may break with mods that add content in the end. (A big example is YUNG's better end islands)", 78 | "You should see if those mods have ways to replenish dragon eggs themselves.") 79 | .define("replenish_eggs", true); 80 | USE_LOOT_TABLES = configurator.comment( 81 | "Should dragon eggs generate in treasure chest loot tables?", 82 | "Useful for multiplayer scenarios and offering alternative ways to obtain eggs.", 83 | "Egg chance values can be modified below. If you'd like to change the loot tables the eggs appear", 84 | "in, consider using a datapack, since the static nature of configs complicates things.") 85 | .define("use_loot_tables", false); 86 | UPDATE_HABITATS = configurator.comment("Should Dragon Eggs adapt to their environments and change breeds?") 87 | .define("update_habitats", true); 88 | 89 | configurator.pop(); 90 | 91 | 92 | configurator.comment( 93 | "!!! THESE VALUES DO NOT TAKE EFFECT UNTIL `use_loot_tables` ABOVE IS SET TO `true` !!!", 94 | "These entries define the chance values of which a dragon egg can appear in its respective loot table.", 95 | "Due to the static nature of configs in general, DML cannot modify the chances of custom breed eggs", 96 | "outside the built-in defaults, so those should be configured to use minecraft's built in random chance conditions.", 97 | "(It is however, possible to point custom egg chances to the built-in values via the loot condition, therefore using a config chance)") 98 | .push("egg_loot_chances"); 99 | EGG_CHANCES = defineChanceEntries(configurator); 100 | configurator.pop(); 101 | 102 | 103 | configurator.comment( 104 | "These entries define the reproduction (breed) limits of each dragon breed.", 105 | "Due to the static nature of configs in general, DML cannot modify the reproduction limits of custom breeds", 106 | "outside the mod's built in breeds. Those should be configured in datapacks to use a direct number rather", 107 | "than point to an entry here (unless that's the goal.)") 108 | .push("reproduction_limits"); 109 | REPRO_LIMITS = defineReproLimEntries(configurator); 110 | configurator.pop(); 111 | 112 | 113 | COMMON_SPEC = configurator.build(); 114 | } 115 | 116 | public static final ForgeConfigSpec CLIENT_SPEC; 117 | 118 | public static final ForgeConfigSpec.BooleanValue CAMERA_DRIVEN_FLIGHT; 119 | 120 | public static boolean cameraDrivenFlight() 121 | { 122 | return CAMERA_DRIVEN_FLIGHT.get(); 123 | } 124 | 125 | public static final ForgeConfigSpec.BooleanValue THIRD_PERSON_ON_MOUNT; 126 | 127 | public static boolean thirdPersonOnMount() 128 | { 129 | return THIRD_PERSON_ON_MOUNT.get(); 130 | } 131 | 132 | // [0][0..2] = Back third person ; distance, vertical, horizontal 133 | // [1][0..2] = Front third person ; distance, vertical, horizontal 134 | private static final ForgeConfigSpec.DoubleValue[][] CAMERA_OFFSETS = new ForgeConfigSpec.DoubleValue[2][3]; 135 | 136 | public static ForgeConfigSpec.DoubleValue[] getCameraPerspectiveOffset(boolean back) 137 | { 138 | return CAMERA_OFFSETS[back? 0 : 1]; 139 | } 140 | 141 | static 142 | { 143 | var configurator = new ForgeConfigSpec.Builder() 144 | .push("client"); 145 | 146 | CAMERA_DRIVEN_FLIGHT = configurator.comment( 147 | "Is dragon flight vertical movement driven by the pitch of the game camera?", 148 | "This option can be toggled in-game via keybinding for quick switching.", 149 | "If you choose to disable this, vertical movement can still be achieved via dedicated keybindings.") 150 | .define("camera_driven_flight", true); 151 | 152 | THIRD_PERSON_ON_MOUNT = configurator.comment( 153 | "When mounting and dismounting a dragon, should the camera automatically switch third-person perspectives?") 154 | .define("third_person_on_mount", true); 155 | 156 | configurator.pop(); 157 | 158 | configurator.comment( 159 | "The values that define the offset of the camera position when riding a dragon in the third person camera.") 160 | .push("camera_offsets"); 161 | defineCameraOffsetEntries(configurator); 162 | configurator.pop(); 163 | 164 | CLIENT_SPEC = configurator.build(); 165 | } 166 | 167 | private static ImmutableMap defineChanceEntries(ForgeConfigSpec.Builder configurator) 168 | { 169 | var chances = ImmutableMap.builder(); 170 | for (var target : DragonEggLootMod.BUILT_IN_CHANCES) 171 | { 172 | var path = formatEggTargetAsPath(target.forBreed(), target.target()); 173 | var entry = configurator.comment( 174 | String.format("The chance that a %s egg appears in %s.", target.forBreed().location().getPath(), target.target().location().getPath()), 175 | "0 = Never Appears, 1 = Guaranteed") 176 | .defineInRange(path, target.chance(), 0, 1); 177 | 178 | chances.put(path, entry); 179 | } 180 | return chances.build(); 181 | } 182 | 183 | private static ImmutableMap defineReproLimEntries(ForgeConfigSpec.Builder configurator) 184 | { 185 | var lims = ImmutableMap.builder(); 186 | for (var type : new ResourceKey[]{AETHER, END, FIRE, FOREST, GHOST, ICE, NETHER, WATER}) 187 | { 188 | var path = type.location().getPath(); 189 | lims.put(path, configurator.comment( 190 | "-1 = No Limit, 0 = Cannot breed, 2 = Can breed only two times") 191 | .defineInRange(path, TameableDragon.BASE_REPRO_LIMIT, -1, Integer.MAX_VALUE)); 192 | } 193 | return lims.build(); 194 | } 195 | 196 | public static String formatEggTargetAsPath(ResourceKey forBreed, ResourceKey forTarget) 197 | { 198 | return String.format("%s_in_%s_chance", 199 | forBreed.location().getPath(), 200 | forTarget.location().getPath().substring(forTarget.location().getPath().lastIndexOf('/') + 1)); 201 | } 202 | 203 | private static void defineCameraOffsetEntries(ForgeConfigSpec.Builder configurator) 204 | { 205 | var perspectiveName = "back"; 206 | for (ForgeConfigSpec.DoubleValue[] perspective : CAMERA_OFFSETS) 207 | { 208 | configurator.push("third_person_" + perspectiveName); 209 | perspectiveName = "front"; 210 | 211 | perspective[0] = configurator.defineInRange("distance", 6.0, -3, 1000); 212 | perspective[1] = configurator.defineInRange("vertical", 4.0, -3, 1000); 213 | perspective[2] = configurator.defineInRange("horizontal", 0.0, -1000, 1000); 214 | 215 | configurator.pop(); 216 | } 217 | } 218 | } 219 | --------------------------------------------------------------------------------