diff --git a/assets/models/cloud_puff.bbmodel b/assets/models/cloud_puff.bbmodel new file mode 100644 index 00000000..7f045431 --- /dev/null +++ b/assets/models/cloud_puff.bbmodel @@ -0,0 +1 @@ +{"meta":{"format_version":"4.5","model_format":"modded_entity","box_uv":true},"name":"clouds","model_identifier":"","modded_entity_version":"1.17","modded_entity_flip_y":true,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":64,"height":128},"elements":[{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-12,-1,-8],"to":[6,4,1],"autouv":0,"color":0,"origin":[0,-1,-3],"uv_offset":[0,77],"faces":{"north":{"uv":[9,86,27,91],"texture":0},"east":{"uv":[0,86,9,91],"texture":0},"south":{"uv":[36,86,54,91],"texture":0},"west":{"uv":[27,86,36,91],"texture":0},"up":{"uv":[27,86,9,77],"texture":0},"down":{"uv":[45,77,27,86],"texture":0}},"type":"cube","uuid":"93c7fde5-1864-164c-f0cb-a52e32625a88"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,-1,1],"to":[8,3,7],"autouv":0,"color":0,"origin":[0,-1,-3],"uv_offset":[0,80],"faces":{"north":{"uv":[6,86,20,90],"texture":0},"east":{"uv":[0,86,6,90],"texture":0},"south":{"uv":[26,86,40,90],"texture":0},"west":{"uv":[20,86,26,90],"texture":0},"up":{"uv":[20,86,6,80],"texture":0},"down":{"uv":[34,80,20,86],"texture":0}},"type":"cube","uuid":"ef7ef34d-92b8-cf6a-935f-66890c620d75"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6,3,-3],"to":[2,6,3],"autouv":0,"color":0,"origin":[0,-1,-3],"uv_offset":[0,79],"faces":{"north":{"uv":[6,85,14,88],"texture":0},"east":{"uv":[0,85,6,88],"texture":0},"south":{"uv":[20,85,28,88],"texture":0},"west":{"uv":[14,85,20,88],"texture":0},"up":{"uv":[14,85,6,79],"texture":0},"down":{"uv":[22,79,14,85],"texture":0}},"type":"cube","uuid":"73c3ea63-aa41-3cb8-5a03-d62e6211cca3"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-10,-2,-14],"to":[2,3,-3],"autouv":0,"color":0,"origin":[0,-1,-3],"uv_offset":[0,74],"faces":{"north":{"uv":[11,85,23,90],"texture":0},"east":{"uv":[0,85,11,90],"texture":0},"south":{"uv":[34,85,46,90],"texture":0},"west":{"uv":[23,85,34,90],"texture":0},"up":{"uv":[23,85,11,74],"texture":0},"down":{"uv":[35,74,23,85],"texture":0}},"type":"cube","uuid":"9c951f25-ddf3-bf79-49c9-5f24c2cb86d8"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[2,-1,-13],"to":[14,4,-4],"autouv":0,"color":0,"origin":[0,-1,-3],"uv_offset":[7,77],"faces":{"north":{"uv":[16,86,28,91],"texture":0},"east":{"uv":[7,86,16,91],"texture":0},"south":{"uv":[37,86,49,91],"texture":0},"west":{"uv":[28,86,37,91],"texture":0},"up":{"uv":[28,86,16,77],"texture":0},"down":{"uv":[40,77,28,86],"texture":0}},"type":"cube","uuid":"e53523dd-2831-4c68-ea61-2cf296099a96"}],"outliner":[{"name":"puff","origin":[0,-1,-3],"rotation":[0,-87.5,0],"color":0,"uuid":"ddebc7b0-8a66-afe5-eccc-cbefbe7cbbe5","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["93c7fde5-1864-164c-f0cb-a52e32625a88","ef7ef34d-92b8-cf6a-935f-66890c620d75","9c951f25-ddf3-bf79-49c9-5f24c2cb86d8","e53523dd-2831-4c68-ea61-2cf296099a96","73c3ea63-aa41-3cb8-5a03-d62e6211cca3"]}],"textures":[{"path":"/home/sollace/Documents/GitRepos/minecraft_mods/Unicopia/assets/models/clouds.png","name":"clouds.png","folder":"block","namespace":"","id":"0","particle":false,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":true,"uuid":"18b207f2-8ac3-306a-05dd-6d554fa5ba9a","relative_path":"../../../../../../../../../../../home/sollace/Documents/GitRepos/minecraft_mods/Unicopia/assets/models/clouds.png","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAACACAYAAAC7gW9qAAAAAXNSR0IArs4c6QAACFtJREFUeF7tXM2P20QU/9mOHSfZbXdpKVCg2uVQcQMJBBIHhERVBEg9ASqCM3ckDhyQ4AA3/gwugODAmao3QEithERBLEj0S91t2W23TRMnjh2j582ks7P+GCeTNvbO9LBxPZmP33vzfu+9mYkBxeWTi1G0du36uNXjRx8Be+Y/UwX++ZuXHjUUD0WqOeWdMgA+On4QX67dHk+Snn+468ZgJL174XBD+VhkEFDeKQOApMsKTZo985/pPXv+fMVQPpYHDgAv7RNHl/HswnCPVpCW0LsPVurVBIBJgSb547Vb8aNoCyoHQJLqHYOPy3AStZLeVUoD9j0AmgZTqE7TIEd7IkVWhgZ/3fQi0QFiVLcvaPCdnzaiNKrTNKhpcJ/4AZoGNQ3ei/74iE/8XMloUNOgpsEo9gPEMrfRIHNcZLInWXVYjJ+V90tykKbpV0WO0SAA+PRV0QGdWujtyvKwQSXl/ZJyhEX7E2OIaXOMYwB47pYdFE02DYCknCDL/7FBs+hQtj9WT9S2pL5kg6tdAJDUZAuTpghAmuRZSozXED48LtpvmuSL5hhjAN5eWcK3F7fjdDUNKq/wkxYBEKUk5gTTACjSLxtvVl+yKbZEALKWg6j2SUsgjQUoGswCQLZfBkCa7SqSY0wFIGk5JKl9mg1Ik04eADL98gCkxR2yO017bAC/LsU1WgQAWRYQDSIDYNq+ZHeapADgVVpmCaTt/qSxAL9tpqqviVlA1ICk9ZxlBCdlgTQjOGlfhYzgJNSURoOTskAW80zS19QA5FEhc2tVsMAs+poKAJkBsTrTssCs+pqYBYoMiNXlffwiscAs+5qIBSYZUBH/nGeBWfdVmAWyBvSM0x+//s2vp1bN88+ZJ5jWgGw/9P28vgrZgGmkkfbdpDMAbGNEdX/TnDdQfipDPCTVXDyIsxdvYLMf4sPnV8cHpu4GEfyai99v3EY7GKLz1lPKxyIDtPJOxUNSjcUDMQBb/eEYAHKWPruwjb7t4kJVAWCR2tV2b48G0LubvQHWbnaqpwFiWvzVIy6++HM71oCzrzw2Tp+du7SB1VYN31/tVGsJiLvDNPF/7g5iG/Dm0Wa8LEkDzl/ewLoXYN0LqwUA2QDe+Fxpe/jl2jZoKZARZOWmN8Bftzr4+1YHW55fLSPIA0ATJwAIiF0AjGzAWhUB4LM0SSxAALUHEfwqswDLCyT5ATEN/rFdbT+A0SCpvugHxDQ4sgGV8wNEGjxxpBHTILEAT4PEAqvNGr6rOg3SxIkGiQ55GiQ/YL0XYKPqNLgvWWDf+wE8DepoMCUa/PTCdrX9AB0Njm6LpUWD5y9tYEVHg/s4GtzyBqBA6IFHg1kXHVnsnrYFLfOeZ4HozNeI6HYcRczsr0ziLqXOwmvvYnWphVrg46G6iaZ1L8MnvTGSddEx6dx/kY0P+j4fDQ4JAACqEpELJ0+PAfj46YP46konhoqCLemNkayLjvxGhszBJLE+DYbPCRIAY8mPNMAwDdA/0N8hEBnAMAyl9GL5jffxxKILO/Dx3LEj2NjcGmecpDdGso62Uh6fhbXskFPe9rf4nmeBGAChmKYJwzRhWxaGAOqmgV5ISABhGGA4pP9NLq2Tp/Hyw24cT5w6tozzW+24YpF7iPEBiaS1zF90ZI3yGxt5Gx/sPZ8TTATAMmE7dbTcOgKa60gzLAKi30cQhrFGRMPhHjDIBtDaP2RbOwBs3ikOgJjDY1jTQSN+K0t8FmWS9p7PCSYBYFkWGo0GGo6D0Kqh6e5svdGkB4MAvX4PQTBAMAgQBAEiLuW4/Pp78RJ4fNHFi4tWrAEEfKFDUveTBZIAsG0bjluH49RhOw5cAmK4wxJhECIMfHS7XfiDAQLf38F9xCK0BIgFyAaQJjTmkQXybEAMQN3FcqsO37Th1h2EYYSluon/uj5C34fnefB7vXg58ClnxgIEwNyywM//ro/3BRI1wLHhNhpoNpowrRocx0YYhrBMC36/D8/r7mgAkz5be4YBtgTIDyAWuD6PLCD6AaLtqJMGNJpoNeqAVcMBx8ZdsobREB5Jveeh3ekm0gBpALEAba7MLQvwnmCaBjSarZgFDMsGzIjmHlv9TqeDbqcTa0RSYSxweMQC58rIAmQDmq0W6o4Nw7RgGmZs/Hp+Hz1a+/69wxkiCEsjFiAmKC8LkA1wGyAgiBIjRAgGA/T7PrxusuozIEoZC4hSrNVqcF0Xds2O3WBSfaK8nteN2S6rlDMWEGYU06DjwLQsGIYROz1+30cQBrE7nFUqEQuQ2hMANHny8gbk8AwCREa0EyRllErEAlbNAi0DmuxwGMEf9HckT3PPWwIqYgH+1ljRG1959YkCu+3bYxnm1S/6Pqt96YQIASBztn+S5Ag5QV77TuIvR03SnpiMyWpfOiEiXpvLS4LI3AXgU+KkAXk3yKZJtqS1L50QYQCIv/qkKhly5kZv1z0hdqtr1u0XOimal9wQkyN59flkyKG6mfjLUXxyZRbtSwOQlBBRmQx5crGxh8juR/vSAGgWmCELUDKEbMCsWCar/blgAUqGMBvA1oFKlslqfy5YgPG0yN9iin3SlHtW+4VsQJ5Vn9RKM0/tQbQvDYBmgdHGSNLmSFHfXKyvYwEdC/TiaFDHAqPjMmkbrZOyAPMDklhG2gjywRDjapUbo3MfC2Rn3ar/VtVhjdIipQEoregUDVxrgCIgS9uM1oDSik7RwLUGKAKytM1oDSit6BQNXGuAIiBL24zWgNKKTtHAtQYoArK0zWgNKK3oFA1ca4AiIEvbjNaA0opO0cC1BigCsrTNaA0oregUDVxrgCIgS9uM1oDSik7RwLUGKAKytM1oDSit6BQNfN9rwP+iGi5TDQdxxwAAAABJRU5ErkJggg=="}],"fabricOptions":{"header":"package com.example.mod;","entity":"Entity","render":"","members":""}} \ No newline at end of file diff --git a/assets/models/clouds.bbmodel b/assets/models/clouds.bbmodel new file mode 100644 index 00000000..ac877f44 --- /dev/null +++ b/assets/models/clouds.bbmodel @@ -0,0 +1 @@ +{"meta":{"format_version":"4.5","model_format":"modded_entity","box_uv":true},"name":"cloud_puff","model_identifier":"","modded_entity_version":"1.17","modded_entity_flip_y":true,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":64,"height":128},"elements":[{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-7,16,-25],"to":[11,21,-16],"autouv":0,"color":0,"origin":[5,10,-20],"uv_offset":[0,77],"faces":{"north":{"uv":[9,86,27,91],"texture":0},"east":{"uv":[0,86,9,91],"texture":0},"south":{"uv":[36,86,54,91],"texture":0},"west":{"uv":[27,86,36,91],"texture":0},"up":{"uv":[27,86,9,77],"texture":0},"down":{"uv":[45,77,27,86],"texture":0}},"type":"cube","uuid":"93c7fde5-1864-164c-f0cb-a52e32625a88"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-1,16,-16],"to":[13,20,-10],"autouv":0,"color":0,"origin":[5,10,-20],"uv_offset":[0,80],"faces":{"north":{"uv":[6,86,20,90],"texture":0},"east":{"uv":[0,86,6,90],"texture":0},"south":{"uv":[26,86,40,90],"texture":0},"west":{"uv":[20,86,26,90],"texture":0},"up":{"uv":[20,86,6,80],"texture":0},"down":{"uv":[34,80,20,86],"texture":0}},"type":"cube","uuid":"ef7ef34d-92b8-cf6a-935f-66890c620d75"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-1,20,-20],"to":[7,23,-14],"autouv":0,"color":0,"origin":[5,10,-20],"uv_offset":[0,79],"faces":{"north":{"uv":[6,85,14,88],"texture":0},"east":{"uv":[0,85,6,88],"texture":0},"south":{"uv":[20,85,28,88],"texture":0},"west":{"uv":[14,85,20,88],"texture":0},"up":{"uv":[14,85,6,79],"texture":0},"down":{"uv":[22,79,14,85],"texture":0}},"type":"cube","uuid":"73c3ea63-aa41-3cb8-5a03-d62e6211cca3"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-5,15,-31],"to":[7,20,-20],"autouv":0,"color":0,"origin":[5,10,-20],"uv_offset":[0,74],"faces":{"north":{"uv":[11,85,23,90],"texture":0},"east":{"uv":[0,85,11,90],"texture":0},"south":{"uv":[34,85,46,90],"texture":0},"west":{"uv":[23,85,34,90],"texture":0},"up":{"uv":[23,85,11,74],"texture":0},"down":{"uv":[35,74,23,85],"texture":0}},"type":"cube","uuid":"9c951f25-ddf3-bf79-49c9-5f24c2cb86d8"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[7,16,-30],"to":[19,21,-21],"autouv":0,"color":0,"origin":[5,10,-20],"uv_offset":[7,77],"faces":{"north":{"uv":[16,86,28,91],"texture":0},"east":{"uv":[7,86,16,91],"texture":0},"south":{"uv":[37,86,49,91],"texture":0},"west":{"uv":[28,86,37,91],"texture":0},"up":{"uv":[28,86,16,77],"texture":0},"down":{"uv":[40,77,28,86],"texture":0}},"type":"cube","uuid":"e53523dd-2831-4c68-ea61-2cf296099a96"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-5,8,-6],"to":[2,25,14],"autouv":0,"color":9,"rotation":[17.437450715409224,0,90],"origin":[0,6,0],"uv_offset":[0,37],"faces":{"north":{"uv":[20,57,27,74],"texture":0},"east":{"uv":[0,57,20,74],"texture":0},"south":{"uv":[47,57,54,74],"texture":0},"west":{"uv":[27,57,47,74],"texture":0},"up":{"uv":[27,57,20,37],"texture":0},"down":{"uv":[34,37,27,57],"texture":0}},"type":"cube","uuid":"eeb1c62a-3056-5aae-3320-f67cf5849073"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-6.341997942912759,5.147570042731205,-30.708301350401655],"to":[0.6580020570872414,22.147570042731203,-10.708301350401655],"autouv":0,"color":9,"rotation":[-9.96271078726396,-0.8671724090795644,-2.4244151512138976],"origin":[-8.841997942912752,13.647570042731207,-20.70830135040165],"faces":{"north":{"uv":[20,20,27,37],"texture":0},"east":{"uv":[0,20,20,37],"texture":0},"south":{"uv":[47,20,54,37],"texture":0},"west":{"uv":[27,20,47,37],"texture":0},"up":{"uv":[27,20,20,0],"texture":0},"down":{"uv":[34,0,27,20],"texture":0}},"type":"cube","uuid":"941992de-24b9-66d8-0c80-2fe09aa7a35e"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-5,-7,-7],"to":[2,10,13],"autouv":0,"color":9,"rotation":[4.999999999999999,-1.987846675914698e-15,89.99999999999989],"origin":[0,6,0],"uv_offset":[0,37],"faces":{"north":{"uv":[20,57,27,74],"texture":0},"east":{"uv":[0,57,20,74],"texture":0},"south":{"uv":[47,57,54,74],"texture":0},"west":{"uv":[27,57,47,74],"texture":0},"up":{"uv":[27,57,20,37],"texture":0},"down":{"uv":[34,37,27,57],"texture":0}},"type":"cube","uuid":"2fed68af-3ea4-5686-b3de-5270750d0f2d"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[21,5,-14],"to":[28,22,6],"autouv":0,"color":9,"rotation":[0,0,90],"origin":[15,2,26],"faces":{"north":{"uv":[20,20,27,37],"texture":0},"east":{"uv":[0,20,20,37],"texture":0},"south":{"uv":[47,20,54,37],"texture":0},"west":{"uv":[27,20,47,37],"texture":0},"up":{"uv":[27,20,20,0],"texture":0},"down":{"uv":[34,0,27,20],"texture":0}},"type":"cube","uuid":"8f9d6a2a-286c-d397-eae2-a29b7a9b2b6c"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[15,-13,-10],"to":[22,4,10],"autouv":0,"color":9,"rotation":[0,0,90],"origin":[15,1,15],"uv_offset":[0,37],"faces":{"north":{"uv":[20,57,27,74],"texture":0},"east":{"uv":[0,57,20,74],"texture":0},"south":{"uv":[47,57,54,74],"texture":0},"west":{"uv":[27,57,47,74],"texture":0},"up":{"uv":[27,57,20,37],"texture":0},"down":{"uv":[34,37,27,57],"texture":0}},"type":"cube","uuid":"06198a92-c396-df48-54ce-bfa944e846b3"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[23,-3,-3],"to":[30,14,17],"autouv":0,"color":9,"rotation":[0,0,85],"origin":[15,1,15],"faces":{"north":{"uv":[20,20,27,37],"texture":0},"east":{"uv":[0,20,20,37],"texture":0},"south":{"uv":[47,20,54,37],"texture":0},"west":{"uv":[27,20,47,37],"texture":0},"up":{"uv":[27,20,20,0],"texture":0},"down":{"uv":[34,0,27,20],"texture":0}},"type":"cube","uuid":"845cf510-9043-98dd-1c5a-ef26745f6cab"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[14,5,5],"to":[21,22,25],"autouv":0,"color":9,"rotation":[0,0,90],"origin":[15,2,26],"uv_offset":[0,37],"faces":{"north":{"uv":[20,57,27,74],"texture":0},"east":{"uv":[0,57,20,74],"texture":0},"south":{"uv":[47,57,54,74],"texture":0},"west":{"uv":[27,57,47,74],"texture":0},"up":{"uv":[27,57,20,37],"texture":0},"down":{"uv":[34,37,27,57],"texture":0}},"type":"cube","uuid":"e7adf463-908e-b809-f11e-fca032850f22"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[14,2,14],"to":[21,19,34],"autouv":0,"color":9,"rotation":[47.5,1.2722218725854067e-14,89.9999999999999],"origin":[15,2,26],"uv_offset":[0,37],"faces":{"north":{"uv":[20,57,27,74],"texture":0},"east":{"uv":[0,57,20,74],"texture":0},"south":{"uv":[47,57,54,74],"texture":0},"west":{"uv":[27,57,47,74],"texture":0},"up":{"uv":[27,57,20,37],"texture":0},"down":{"uv":[34,37,27,57],"texture":0}},"type":"cube","uuid":"0ea49ced-856c-e60e-607f-937a478c0fb8"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-3.947114163928383,-0.3075192417859789,13],"to":[3.052885836071617,16.69248075821402,33],"autouv":0,"color":9,"rotation":[0,0,-180],"origin":[-0.4471141639283829,12.192480758214025,36],"faces":{"north":{"uv":[20,20,27,37],"texture":0},"east":{"uv":[0,20,20,37],"texture":0},"south":{"uv":[47,20,54,37],"texture":0},"west":{"uv":[27,20,47,37],"texture":0},"up":{"uv":[27,20,20,0],"texture":0},"down":{"uv":[34,0,27,20],"texture":0}},"type":"cube","uuid":"09755f9f-f000-8c7d-6237-0ba78092f202"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-3,2,13],"to":[4,19,33],"autouv":0,"color":9,"rotation":[19.999999999999996,1.5902773407317584e-15,89.99999999999986],"origin":[-2,2,19],"uv_offset":[0,37],"faces":{"north":{"uv":[20,57,27,74],"texture":0},"east":{"uv":[0,57,20,74],"texture":0},"south":{"uv":[47,57,54,74],"texture":0},"west":{"uv":[27,57,47,74],"texture":0},"up":{"uv":[27,57,20,37],"texture":0},"down":{"uv":[34,37,27,57],"texture":0}},"type":"cube","uuid":"57a12f61-d55c-8aa2-c482-8f5bb038d238"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[4,-1,-12],"to":[11,16,8],"autouv":0,"color":9,"rotation":[0,0,95],"origin":[-2,2,19],"faces":{"north":{"uv":[20,20,27,37],"texture":0},"east":{"uv":[0,20,20,37],"texture":0},"south":{"uv":[47,20,54,37],"texture":0},"west":{"uv":[27,20,47,37],"texture":0},"up":{"uv":[27,20,20,0],"texture":0},"down":{"uv":[34,0,27,20],"texture":0}},"type":"cube","uuid":"e2b25e48-e992-997c-653e-c64a96950c51"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[15,-9,8],"to":[22,8,28],"autouv":0,"color":9,"rotation":[-12.5,0,89.99999999999994],"origin":[15,1,15],"uv_offset":[0,37],"faces":{"north":{"uv":[20,57,27,74],"texture":0},"east":{"uv":[0,57,20,74],"texture":0},"south":{"uv":[47,57,54,74],"texture":0},"west":{"uv":[27,57,47,74],"texture":0},"up":{"uv":[27,57,20,37],"texture":0},"down":{"uv":[34,37,27,57],"texture":0}},"type":"cube","uuid":"014c486c-bbed-f77d-4306-3a39681c6f96"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-5,-4,-25],"to":[2,13,-5],"autouv":0,"color":9,"rotation":[-10.000000000000002,-7.951386703658792e-16,89.99999999999994],"origin":[0,6,0],"uv_offset":[0,37],"faces":{"north":{"uv":[20,57,27,74],"texture":0},"east":{"uv":[0,57,20,74],"texture":0},"south":{"uv":[47,57,54,74],"texture":0},"west":{"uv":[27,57,47,74],"texture":0},"up":{"uv":[27,57,20,37],"texture":0},"down":{"uv":[34,37,27,57],"texture":0}},"type":"cube","uuid":"0557bc25-4891-b53c-509c-f9a4976131fb"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-5,2,-40],"to":[2,19,-20],"autouv":0,"color":9,"rotation":[0,0,90],"origin":[0,6,0],"uv_offset":[0,37],"faces":{"north":{"uv":[20,57,27,74],"texture":0},"east":{"uv":[0,57,20,74],"texture":0},"south":{"uv":[47,57,54,74],"texture":0},"west":{"uv":[27,57,47,74],"texture":0},"up":{"uv":[27,57,20,37],"texture":0},"down":{"uv":[34,37,27,57],"texture":0}},"type":"cube","uuid":"5e5ae0e3-42e0-80f6-66b6-f36a10b94570"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-5,9,-20],"to":[2,26,0],"autouv":0,"color":9,"rotation":[0,0,90],"origin":[0,6,0],"uv_offset":[0,37],"faces":{"north":{"uv":[20,57,27,74],"texture":0},"east":{"uv":[0,57,20,74],"texture":0},"south":{"uv":[47,57,54,74],"texture":0},"west":{"uv":[27,57,47,74],"texture":0},"up":{"uv":[27,57,20,37],"texture":0},"down":{"uv":[34,37,27,57],"texture":0}},"type":"cube","uuid":"fba123fb-36b1-bde0-989e-cf43840b6594"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[7,9,-9],"to":[14,26,11],"autouv":0,"color":9,"inflate":2,"rotation":[-22.5,0,0],"origin":[2,29,0],"faces":{"north":{"uv":[20,20,27,37],"texture":0},"east":{"uv":[0,20,20,37],"texture":0},"south":{"uv":[47,20,54,37],"texture":0},"west":{"uv":[27,20,47,37],"texture":0},"up":{"uv":[27,20,20,0],"texture":0},"down":{"uv":[34,0,27,20],"texture":0}},"type":"cube","uuid":"4fbf977f-a50b-bfba-a5d8-650e450507db"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-12,3,0],"to":[-5,20,20],"autouv":0,"color":9,"inflate":2,"rotation":[-15,0,0],"origin":[2,29,0],"faces":{"north":{"uv":[20,20,27,37],"texture":0},"east":{"uv":[0,20,20,37],"texture":0},"south":{"uv":[47,20,54,37],"texture":0},"west":{"uv":[27,20,47,37],"texture":0},"up":{"uv":[27,20,20,0],"texture":0},"down":{"uv":[34,0,27,20],"texture":0}},"type":"cube","uuid":"a74b60c5-eed1-0270-166b-580c9e57f44e"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-5,13.5,-12.5],"to":[2,30.5,7.5],"autouv":0,"color":9,"inflate":5,"rotation":[-20,0,0],"origin":[2,29,0],"faces":{"north":{"uv":[20,20,27,37],"texture":0},"east":{"uv":[0,20,20,37],"texture":0},"south":{"uv":[47,20,54,37],"texture":0},"west":{"uv":[27,20,47,37],"texture":0},"up":{"uv":[27,20,20,0],"texture":0},"down":{"uv":[34,0,27,20],"texture":0}},"type":"cube","uuid":"13886e49-3168-7d3d-4e01-4c0ef7145c4f"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-3,30,4],"to":[4,47,24],"autouv":0,"color":9,"inflate":5,"rotation":[7.5,0,0],"origin":[2,29,0],"faces":{"north":{"uv":[20,20,27,37],"texture":0},"east":{"uv":[0,20,20,37],"texture":0},"south":{"uv":[47,20,54,37],"texture":0},"west":{"uv":[27,20,47,37],"texture":0},"up":{"uv":[27,20,20,0],"texture":0},"down":{"uv":[34,0,27,20],"texture":0}},"type":"cube","uuid":"b8b44b26-7bec-ec73-e9f4-e7da828bb082"},{"name":"cube","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[4,12,12],"to":[11,29,32],"autouv":0,"color":9,"inflate":2,"rotation":[22.5,0,0],"origin":[7.5,27.5,16],"faces":{"north":{"uv":[20,20,27,37],"texture":0},"east":{"uv":[0,20,20,37],"texture":0},"south":{"uv":[47,20,54,37],"texture":0},"west":{"uv":[27,20,47,37],"texture":0},"up":{"uv":[27,20,20,0],"texture":0},"down":{"uv":[34,0,27,20],"texture":0}},"type":"cube","uuid":"0e698f3b-0a9d-a66c-7391-25f1e35c70ce"}],"outliner":[{"name":"small_puffs","origin":[0,0,0],"color":0,"uuid":"85dcee7f-96ec-246b-e231-18bc402af68a","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["eeb1c62a-3056-5aae-3320-f67cf5849073","941992de-24b9-66d8-0c80-2fe09aa7a35e","2fed68af-3ea4-5686-b3de-5270750d0f2d","8f9d6a2a-286c-d397-eae2-a29b7a9b2b6c","06198a92-c396-df48-54ce-bfa944e846b3","845cf510-9043-98dd-1c5a-ef26745f6cab","e7adf463-908e-b809-f11e-fca032850f22","0ea49ced-856c-e60e-607f-937a478c0fb8","09755f9f-f000-8c7d-6237-0ba78092f202","57a12f61-d55c-8aa2-c482-8f5bb038d238","e2b25e48-e992-997c-653e-c64a96950c51","014c486c-bbed-f77d-4306-3a39681c6f96","0557bc25-4891-b53c-509c-f9a4976131fb","5e5ae0e3-42e0-80f6-66b6-f36a10b94570","fba123fb-36b1-bde0-989e-cf43840b6594"]},{"name":"anvil_heads","origin":[2,23,0],"color":0,"uuid":"bba2945e-7e02-c389-6b4c-84c0afcfcdb5","export":true,"mirror_uv":false,"isOpen":false,"locked":false,"visibility":true,"autouv":0,"children":["4fbf977f-a50b-bfba-a5d8-650e450507db","a74b60c5-eed1-0270-166b-580c9e57f44e","13886e49-3168-7d3d-4e01-4c0ef7145c4f","b8b44b26-7bec-ec73-e9f4-e7da828bb082","0e698f3b-0a9d-a66c-7391-25f1e35c70ce"]},{"name":"puff","origin":[5,10,-20],"rotation":[0,-87.5,0],"color":0,"uuid":"ddebc7b0-8a66-afe5-eccc-cbefbe7cbbe5","export":true,"mirror_uv":false,"isOpen":true,"locked":false,"visibility":true,"autouv":0,"children":["93c7fde5-1864-164c-f0cb-a52e32625a88","ef7ef34d-92b8-cf6a-935f-66890c620d75","9c951f25-ddf3-bf79-49c9-5f24c2cb86d8","e53523dd-2831-4c68-ea61-2cf296099a96","73c3ea63-aa41-3cb8-5a03-d62e6211cca3"]}],"textures":[{"path":"/home/sollace/Documents/GitRepos/minecraft_mods/Unicopia/assets/models/clouds.png","name":"clouds.png","folder":"block","namespace":"","id":"0","particle":false,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":true,"uuid":"18b207f2-8ac3-306a-05dd-6d554fa5ba9a","relative_path":"../../../../../../../../../../../home/sollace/Documents/GitRepos/minecraft_mods/Unicopia/assets/models/clouds.png","source":"data:image/png;base64,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"}],"fabricOptions":{"header":"package com.example.mod;","entity":"Entity","render":"","members":""}} \ No newline at end of file diff --git a/assets/models/clouds.png b/assets/models/clouds.png new file mode 100644 index 00000000..2d2021f3 Binary files /dev/null and b/assets/models/clouds.png differ diff --git a/assets/models/crystals.bbmodel b/assets/models/crystals.bbmodel index d899800b..55a13859 100644 --- a/assets/models/crystals.bbmodel +++ b/assets/models/crystals.bbmodel @@ -1 +1 @@ -{"meta":{"format_version":"4.0","model_format":"modded_entity","box_uv":true},"name":"crystals","model_identifier":"unicopia:crystals","modded_entity_version":"Fabric 1.17+","modded_entity_flip_y":true,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"resolution":{"width":32,"height":32},"elements":[{"name":"cube","rescale":false,"locked":false,"from":[-1,0,-2],"to":[2,20,1],"autouv":1,"color":2,"rotation":[-13.814616595160036,-19.12963698212366,10.422850350264296],"origin":[0,0,0],"faces":{"north":{"uv":[3,3,6,23],"texture":0},"east":{"uv":[0,3,3,23],"texture":0},"south":{"uv":[9,3,12,23],"texture":0},"west":{"uv":[6,3,9,23],"texture":0},"up":{"uv":[6,3,3,0],"texture":0},"down":{"uv":[9,0,6,3],"texture":0}},"type":"cube","uuid":"1b72dbc3-4f10-93d9-0485-9402ffb3a0e5"},{"name":"cube","rescale":false,"locked":false,"from":[-1,0,-2],"to":[2,8,1],"autouv":0,"color":2,"rotation":[6.592308260823488,-22.548293243863817,-43.23158512777915],"origin":[0,0,0],"uv_offset":[12,13],"faces":{"north":{"uv":[15,16,18,24],"texture":0},"east":{"uv":[12,16,15,24],"texture":0},"south":{"uv":[21,16,24,24],"texture":0},"west":{"uv":[18,16,21,24],"texture":0},"up":{"uv":[18,16,15,13],"texture":0},"down":{"uv":[21,13,18,16],"texture":0}},"type":"cube","uuid":"b73b73de-81e2-4c2e-79f4-03fa362f143d"},{"name":"cube","rescale":false,"locked":false,"from":[-1,-1,-2],"to":[2,9,1],"autouv":0,"color":2,"rotation":[104.58557576859963,-39.06922839127526,-79.9217924675026],"origin":[0,0,0],"uv_offset":[12,0],"faces":{"north":{"uv":[15,3,18,13],"texture":0},"east":{"uv":[12,3,15,13],"texture":0},"south":{"uv":[21,3,24,13],"texture":0},"west":{"uv":[18,3,21,13],"texture":0},"up":{"uv":[18,3,15,0],"texture":0},"down":{"uv":[21,0,18,3],"texture":0}},"type":"cube","uuid":"0370db0f-87c0-c37a-ee9f-ce55c015557b"},{"name":"cube","rescale":false,"locked":false,"from":[-2,0,-2],"to":[1,10,1],"autouv":0,"color":2,"rotation":[-104.10745659362532,-36.66539643660528,117.92676070126427],"origin":[0,0,0],"uv_offset":[12,0],"faces":{"north":{"uv":[15,3,18,13],"texture":0},"east":{"uv":[12,3,15,13],"texture":0},"south":{"uv":[21,3,24,13],"texture":0},"west":{"uv":[18,3,21,13],"texture":0},"up":{"uv":[18,3,15,0],"texture":0},"down":{"uv":[21,0,18,3],"texture":0}},"type":"cube","uuid":"07df3df9-bbb5-2d32-b4b0-12325fa4f7f7"},{"name":"cube","rescale":false,"locked":false,"from":[-2,0,-2],"to":[1,10,1],"autouv":0,"color":2,"rotation":[-23.349047789047876,-2.167989489936512,58.53200382014771],"origin":[0,0,0],"uv_offset":[12,0],"faces":{"north":{"uv":[15,3,18,13],"texture":0},"east":{"uv":[12,3,15,13],"texture":0},"south":{"uv":[21,3,24,13],"texture":0},"west":{"uv":[18,3,21,13],"texture":0},"up":{"uv":[18,3,15,0],"texture":0},"down":{"uv":[21,0,18,3],"texture":0}},"type":"cube","uuid":"21d3327d-d6c7-4165-ad04-ebe890d400ab"}],"outliner":["1b72dbc3-4f10-93d9-0485-9402ffb3a0e5","21d3327d-d6c7-4165-ad04-ebe890d400ab","b73b73de-81e2-4c2e-79f4-03fa362f143d","0370db0f-87c0-c37a-ee9f-ce55c015557b","07df3df9-bbb5-2d32-b4b0-12325fa4f7f7"],"textures":[{"path":"","name":"texture","folder":"block","namespace":"","id":"0","particle":false,"render_mode":"default","visible":true,"mode":"bitmap","saved":false,"uuid":"12d44612-f685-a543-420b-f8fb00e3dacf","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAABGZJREFUWEftlstvHEUQxr+Z7tmZfcy+/cJ2EgmFCIQSBQEXZAUExAhOcEACLii5wQGJCyCucOAh7pyiSLGR4A/gISF8ACIvSFaCBEIkkIjE66x37X3vzs50N+oe73gw9o4RFifXZXt6eqp+/XVV9WoI2fxCSdSqVTXz42tPa+F3u40/WKmItUYHv1U24LhMLZHf7+fbob+/BXn1mxtCvri2WsGXLz0cCfD+yrpwGEOj7+Knm+WDA1i++vO+dvHRlaroeww9l6F0/VYg0n7gAwXe+aEsCKEQQsCiOoQAmBBwBi4I0SElIboOZzCAgI7LoUBzx2cwcAdwXA9rtbry6XGO47MzoEQDJQRvPjA+Uknt9aU/RCpugXMB26TgQqDruNhstWGZpgKgREe72wUxTPxa9nNE2qnpAnrOANCBdtdBR46FwOTYGKiuwSAE780dGQ1w7vNfRMG2FUAmYYBxjvVGB+u1TdhpWykSowQu52BcYK3eCgCO5lLoOA56jgdNU+mjLJlM+QBUx8dnT4wGePazFTGRz4ILjmzcBOcct6qbWK2sI5/LKwVMSuExBo8LOJ63HcigWK1U1Dqqb9eJ0CkM3T+CT587ORrgqcWSmC7mAQjYljwKhmvlKiqVCgrFou9VC/mQkmyZLDm5Rv4aREMml4PgArWNzeDbqITUZO1P5TO+dJapfmUZbtRqyBcKSoHwFjzXhQYNAgKMSTX8Fc16A+lMGs1GE9B1ZHNZte7rlx8ZrcD8YklMZNMqsB23AoB6vY5sLucLEIQBOq2mem61WqBKGR/AExyJRALdbjdQSD5/98r8aICzCyUxnkkpN9lkIgBoNhtIpzMqCcMncOf2n6o6+o4D7g6CYDo1grHgHIQSMI/hylvPR+eApJ6ZGEMulQwAOu02krY93GDgfO3m78ERyEBD03SZhb4i4fmrb78QcQQLJdHv9TBZLKCYSQUAEkpKuNPKN67/Y27URDTAYkk4fQfFXBYTWTsAkHOWJRuRgKb5HVFu5YsXHwp2NLw7hok7BInK/DCw9tiFb4X0mEnbuGurGmQV9Lo9xGKGygFqUPWNhFo6P3ewAI9f/F4Vtp1MYLqQVYHkZWTGYqoFcwiYln8UnjvA0vkzBwvwxMXLCiBumTgyLhuSD6ALBl2T9Q7ErLjq8YwznL73RKCgbegwqQ7LMNQ6eSk5HoNtGaA6UWf2xumIy+jJS8tBa7tnekIFl8b6HVV+8gh8BYQaP3jq/iBPZvNpmAZBjFJVLQPmKYhsIh5AfnjmWEQfuLSsckBSyO4nTbbXcLaTrQDy3fjs0cC5aVCYhMCMSQB5TzB1VxSS2wAXnrkvugx3K6O9ym3q2N2RZRiPGWpH3XYDX5179GAB8pNTO5qzX6CmzJOQtesbaNU3Ed0HFkrb11vIwV4K6ESWpAZDleb2XeCD+VYp3wZz+mp84AB76R8+mjB8JEDkge5YcPLdT3ZV7H8D+LfAUesj//tHOfiv7w8BDhU4VOAvzYQXPySElm4AAAAASUVORK5CYII="}],"fabricOptions":{"header":"package com.example.mod;","entity":"Entity","render":"","members":""}} \ No newline at end of file +{"meta":{"format_version":"4.5","model_format":"modded_entity","box_uv":true},"name":"crystals","model_identifier":"unicopia:crystals","modded_entity_version":"Fabric 1.17+","modded_entity_flip_y":true,"visible_box":[1,1,0],"variable_placeholders":"","variable_placeholder_buttons":[],"unhandled_root_fields":{},"resolution":{"width":32,"height":32},"elements":[{"name":"primary","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-1,0,-2],"to":[2,20,1],"autouv":1,"color":2,"rotation":[-13.814616595160036,-19.12963698212366,10.422850350264296],"origin":[0,0,0],"faces":{"north":{"uv":[3,3,6,23],"texture":0},"east":{"uv":[0,3,3,23],"texture":0},"south":{"uv":[9,3,12,23],"texture":0},"west":{"uv":[6,3,9,23],"texture":0},"up":{"uv":[6,3,3,0],"texture":0},"down":{"uv":[9,0,6,3],"texture":0}},"type":"cube","uuid":"1b72dbc3-4f10-93d9-0485-9402ffb3a0e5"},{"name":"east","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-1,0,-2],"to":[2,8,1],"autouv":0,"color":2,"rotation":[6.592308260823488,-22.548293243863817,-43.23158512777915],"origin":[0,0,0],"uv_offset":[12,13],"faces":{"north":{"uv":[15,16,18,24],"texture":0},"east":{"uv":[12,16,15,24],"texture":0},"south":{"uv":[21,16,24,24],"texture":0},"west":{"uv":[18,16,21,24],"texture":0},"up":{"uv":[18,16,15,13],"texture":0},"down":{"uv":[21,13,18,16],"texture":0}},"type":"cube","uuid":"b73b73de-81e2-4c2e-79f4-03fa362f143d"},{"name":"south","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-1,-1,-2],"to":[2,9,1],"autouv":0,"color":2,"rotation":[104.58557576859963,-39.06922839127526,-79.9217924675026],"origin":[0,0,0],"uv_offset":[12,0],"faces":{"north":{"uv":[15,3,18,13],"texture":0},"east":{"uv":[12,3,15,13],"texture":0},"south":{"uv":[21,3,24,13],"texture":0},"west":{"uv":[18,3,21,13],"texture":0},"up":{"uv":[18,3,15,0],"texture":0},"down":{"uv":[21,0,18,3],"texture":0}},"type":"cube","uuid":"0370db0f-87c0-c37a-ee9f-ce55c015557b"},{"name":"north","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-2,0,-2],"to":[1,10,1],"autouv":0,"color":2,"rotation":[-104.10745659362532,-36.66539643660528,117.92676070126427],"origin":[0,0,0],"uv_offset":[12,0],"faces":{"north":{"uv":[15,3,18,13],"texture":0},"east":{"uv":[12,3,15,13],"texture":0},"south":{"uv":[21,3,24,13],"texture":0},"west":{"uv":[18,3,21,13],"texture":0},"up":{"uv":[18,3,15,0],"texture":0},"down":{"uv":[21,0,18,3],"texture":0}},"type":"cube","uuid":"07df3df9-bbb5-2d32-b4b0-12325fa4f7f7"},{"name":"west","box_uv":true,"rescale":false,"locked":false,"render_order":"default","allow_mirror_modeling":true,"from":[-2,0,-2],"to":[1,10,1],"autouv":0,"color":2,"rotation":[-23.349047789047876,-2.167989489936512,58.53200382014771],"origin":[0,0,0],"uv_offset":[12,0],"faces":{"north":{"uv":[15,3,18,13],"texture":0},"east":{"uv":[12,3,15,13],"texture":0},"south":{"uv":[21,3,24,13],"texture":0},"west":{"uv":[18,3,21,13],"texture":0},"up":{"uv":[18,3,15,0],"texture":0},"down":{"uv":[21,0,18,3],"texture":0}},"type":"cube","uuid":"21d3327d-d6c7-4165-ad04-ebe890d400ab"}],"outliner":["1b72dbc3-4f10-93d9-0485-9402ffb3a0e5","21d3327d-d6c7-4165-ad04-ebe890d400ab","b73b73de-81e2-4c2e-79f4-03fa362f143d","0370db0f-87c0-c37a-ee9f-ce55c015557b","07df3df9-bbb5-2d32-b4b0-12325fa4f7f7"],"textures":[{"path":"","name":"texture","folder":"block","namespace":"","id":"0","particle":false,"render_mode":"default","render_sides":"auto","frame_time":1,"frame_order_type":"loop","frame_order":"","frame_interpolate":false,"visible":true,"mode":"bitmap","saved":false,"uuid":"12d44612-f685-a543-420b-f8fb00e3dacf","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAABGZJREFUWEftlstvHEUQxr+Z7tmZfcy+/cJ2EgmFCIQSBQEXZAUExAhOcEACLii5wQGJCyCucOAh7pyiSLGR4A/gISF8ACIvSFaCBEIkkIjE66x37X3vzs50N+oe73gw9o4RFifXZXt6eqp+/XVV9WoI2fxCSdSqVTXz42tPa+F3u40/WKmItUYHv1U24LhMLZHf7+fbob+/BXn1mxtCvri2WsGXLz0cCfD+yrpwGEOj7+Knm+WDA1i++vO+dvHRlaroeww9l6F0/VYg0n7gAwXe+aEsCKEQQsCiOoQAmBBwBi4I0SElIboOZzCAgI7LoUBzx2cwcAdwXA9rtbry6XGO47MzoEQDJQRvPjA+Uknt9aU/RCpugXMB26TgQqDruNhstWGZpgKgREe72wUxTPxa9nNE2qnpAnrOANCBdtdBR46FwOTYGKiuwSAE780dGQ1w7vNfRMG2FUAmYYBxjvVGB+u1TdhpWykSowQu52BcYK3eCgCO5lLoOA56jgdNU+mjLJlM+QBUx8dnT4wGePazFTGRz4ILjmzcBOcct6qbWK2sI5/LKwVMSuExBo8LOJ63HcigWK1U1Dqqb9eJ0CkM3T+CT587ORrgqcWSmC7mAQjYljwKhmvlKiqVCgrFou9VC/mQkmyZLDm5Rv4aREMml4PgArWNzeDbqITUZO1P5TO+dJapfmUZbtRqyBcKSoHwFjzXhQYNAgKMSTX8Fc16A+lMGs1GE9B1ZHNZte7rlx8ZrcD8YklMZNMqsB23AoB6vY5sLucLEIQBOq2mem61WqBKGR/AExyJRALdbjdQSD5/98r8aICzCyUxnkkpN9lkIgBoNhtIpzMqCcMncOf2n6o6+o4D7g6CYDo1grHgHIQSMI/hylvPR+eApJ6ZGEMulQwAOu02krY93GDgfO3m78ERyEBD03SZhb4i4fmrb78QcQQLJdHv9TBZLKCYSQUAEkpKuNPKN67/Y27URDTAYkk4fQfFXBYTWTsAkHOWJRuRgKb5HVFu5YsXHwp2NLw7hok7BInK/DCw9tiFb4X0mEnbuGurGmQV9Lo9xGKGygFqUPWNhFo6P3ewAI9f/F4Vtp1MYLqQVYHkZWTGYqoFcwiYln8UnjvA0vkzBwvwxMXLCiBumTgyLhuSD6ALBl2T9Q7ErLjq8YwznL73RKCgbegwqQ7LMNQ6eSk5HoNtGaA6UWf2xumIy+jJS8tBa7tnekIFl8b6HVV+8gh8BYQaP3jq/iBPZvNpmAZBjFJVLQPmKYhsIh5AfnjmWEQfuLSsckBSyO4nTbbXcLaTrQDy3fjs0cC5aVCYhMCMSQB5TzB1VxSS2wAXnrkvugx3K6O9ym3q2N2RZRiPGWpH3XYDX5179GAB8pNTO5qzX6CmzJOQtesbaNU3Ed0HFkrb11vIwV4K6ESWpAZDleb2XeCD+VYp3wZz+mp84AB76R8+mjB8JEDkge5YcPLdT3ZV7H8D+LfAUesj//tHOfiv7w8BDhU4VOAvzYQXPySElm4AAAAASUVORK5CYII="}],"fabricOptions":{"header":"package com.example.mod;","entity":"Entity","render":"","members":""}} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/USounds.java b/src/main/java/com/minelittlepony/unicopia/USounds.java index fae2b782..c1022fd7 100644 --- a/src/main/java/com/minelittlepony/unicopia/USounds.java +++ b/src/main/java/com/minelittlepony/unicopia/USounds.java @@ -53,6 +53,10 @@ public interface USounds { SoundEvent ENTITY_HOT_AIR_BALLOON_EQUIP_CANOPY = ITEM_ARMOR_EQUIP_LEATHER; SoundEvent ENTITY_HOT_AIR_BALLOON_EQUIP_BURNER = ENTITY_IRON_GOLEM_DAMAGE; + SoundEvent ENTITY_SOMBRA_AMBIENT = register("entity.sombra.ambient"); + SoundEvent ENTITY_SOMBRA_LAUGH = register("entity.sombra.laugh"); + SoundEvent ENTITY_SOMBRA_SNICKER = register("entity.sombra.snicker"); + SoundEvent ITEM_AMULET_CHARGING = register("item.amulet.charging"); SoundEvent ITEM_AMULET_RECHARGE = register("item.amulet.recharge"); diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index 32cc4c41..acb70971 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -79,6 +79,8 @@ public interface URenderers { EntityRendererRegistry.register(UEntities.TWITTERMITE, FairyEntityRenderer::new); EntityRendererRegistry.register(UEntities.SPELLBOOK, SpellbookEntityRenderer::new); EntityRendererRegistry.register(UEntities.SOMBRA, SombraEntityRenderer::new); + EntityRendererRegistry.register(UEntities.CRYSTAL_SHARDS, CrystalShardsEntityRenderer::new); + EntityRendererRegistry.register(UEntities.STORM_CLOUD, StormCloudEntityRenderer::new); EntityRendererRegistry.register(UEntities.AIR_BALLOON, AirBalloonEntityRenderer::new); BlockEntityRendererFactories.register(UBlockEntities.WEATHER_VANE, WeatherVaneBlockEntityRenderer::new); diff --git a/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java b/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java index d2399a53..5d6681a4 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java +++ b/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java @@ -2,6 +2,8 @@ package com.minelittlepony.unicopia.client; import java.util.Optional; +import org.jetbrains.annotations.Nullable; + import com.minelittlepony.common.client.gui.element.Button; import com.minelittlepony.common.event.ScreenInitCallback; import com.minelittlepony.common.event.ScreenInitCallback.ButtonList; @@ -17,6 +19,7 @@ import com.minelittlepony.unicopia.container.*; import com.minelittlepony.unicopia.entity.player.PlayerCamera; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.network.handler.ClientNetworkHandlerImpl; +import com.minelittlepony.unicopia.server.world.WeatherConditions; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; @@ -26,9 +29,11 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.OpenToLanScreen; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.ingame.HandledScreens; +import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.resource.ResourceType; import net.minecraft.text.Text; +import net.minecraft.util.math.BlockPos; public class UnicopiaClient implements ClientModInitializer { @@ -59,6 +64,9 @@ public class UnicopiaClient implements ClientModInitializer { return 0.6F; } + @Nullable + private Float originalRainGradient; + @Override public void onInitializeClient() { InteractionManager.INSTANCE = new ClientInteractionManager(); @@ -70,6 +78,7 @@ public class UnicopiaClient implements ClientModInitializer { HandledScreens.register(UScreenHandlers.SPELL_BOOK, SpellbookScreen::new); ClientTickEvents.END_CLIENT_TICK.register(this::onTick); + ClientTickEvents.END_WORLD_TICK.register(this::onWorldTick); ScreenInitCallback.EVENT.register(this::onScreenInit); ItemTooltipCallback.EVENT.register(new ModifierTooltipRenderer()); @@ -83,6 +92,25 @@ public class UnicopiaClient implements ClientModInitializer { UHud.INSTANCE.tick(); } + private void onWorldTick(ClientWorld world) { + BlockPos pos = MinecraftClient.getInstance().getCameraEntity().getBlockPos(); + float tickDelta = MinecraftClient.getInstance().getTickDelta(); + + if (WeatherConditions.get(world).isInRangeOfStorm(pos)) { + if (originalRainGradient == null) { + originalRainGradient = world.getRainGradient(tickDelta); + } + world.setRainGradient(1); + world.setThunderGradient(1); + } else { + if (originalRainGradient != null) { + world.setRainGradient(originalRainGradient); + world.setThunderGradient(0); + originalRainGradient = null; + } + } + } + private void onScreenInit(Screen screen, ButtonList buttons) { if (screen instanceof OpenToLanScreen) { buttons.addButton(new Button(screen.width / 2 - 155, 130, 150, 20)) diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java index 559d16f1..aac4a49e 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java @@ -6,8 +6,8 @@ import java.util.Optional; import org.joml.Vector3f; +import com.minelittlepony.unicopia.EntityConvertable; import com.minelittlepony.unicopia.Unicopia; -import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; import com.minelittlepony.unicopia.particle.ParticleHandle.Link; @@ -79,8 +79,8 @@ public class RainbowTrailParticle extends AbstractBillboardParticle implements A } } - private void follow(Caster caster) { - Vec3d next = caster.getOriginVector(); + private void follow(EntityConvertable caster) { + Vec3d next = caster.asEntity().getPos(); if (segments.isEmpty()) { segments.add(new Segment(next)); diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java index aa785aa6..58a8d453 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/RunesParticle.java @@ -6,8 +6,8 @@ import org.joml.Quaternionf; import org.joml.Vector3f; import com.minelittlepony.common.util.Color; +import com.minelittlepony.unicopia.EntityConvertable; import com.minelittlepony.unicopia.Unicopia; -import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect; import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; import com.minelittlepony.unicopia.particle.ParticleHandle.Link; @@ -16,6 +16,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.Tessellator; import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; import net.minecraft.util.Identifier; import net.minecraft.util.math.*; @@ -62,7 +63,7 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme velocityX = 0; velocityY = 0; velocityZ = 0; - Vec3d pos = link.get().map(Caster::getOriginVector).orElse(Vec3d.ZERO); + Vec3d pos = link.get().map(EntityConvertable::asEntity).map(Entity::getPos).orElse(Vec3d.ZERO); setPos(pos.x, pos.y + 0.25, pos.z); } @@ -164,7 +165,7 @@ public class RunesParticle extends OrientedBillboardParticle implements Attachme public void tick() { super.tick(); - link.flatMap(Link::get).map(Caster::asEntity).ifPresentOrElse(e -> { + link.flatMap(Link::get).map(EntityConvertable::asEntity).ifPresentOrElse(e -> { if (getAlphaScale() >= 0.9F) { if (stasisAge < 0) { stasisAge = age; diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java index 29eb937c..7b0844e1 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/SphereParticle.java @@ -12,7 +12,7 @@ import net.minecraft.client.world.ClientWorld; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; -import com.minelittlepony.unicopia.ability.magic.Caster; +import com.minelittlepony.unicopia.EntityConvertable; import com.minelittlepony.unicopia.client.render.RenderLayers; import com.minelittlepony.unicopia.client.render.model.SphereModel; import com.minelittlepony.unicopia.particle.SphereParticleEffect; @@ -38,6 +38,8 @@ public class SphereParticle extends Particle implements Attachment { private final SphereParticleEffect parameters; + private boolean bound; + public SphereParticle(SphereParticleEffect parameters, ClientWorld w, double x, double y, double z, double vX, double vY, double vZ) { this(parameters, w, x, y, z); @@ -90,6 +92,9 @@ public class SphereParticle extends Particle implements Attachment { if (key == ATTR_OPACITY) { alpha = value.floatValue(); } + if (key == ATTR_BOUND) { + bound = value.intValue() == 1; + } } @Override @@ -102,13 +107,15 @@ public class SphereParticle extends Particle implements Attachment { super.tick(); if (link.isPresent()) { - link.flatMap(Link::get).map(Caster::asEntity).ifPresentOrElse(e -> { - Vec3d offset = parameters.getOffset(); - setPos(e.getX() + offset.getX(), e.getY() + offset.getY(), e.getZ() + offset.getZ()); + link.flatMap(Link::get).map(EntityConvertable::asEntity).ifPresentOrElse(e -> { + if (!bound) { + Vec3d offset = parameters.getOffset(); + setPos(e.getX() + offset.getX(), e.getY() + offset.getY(), e.getZ() + offset.getZ()); - prevPosX = e.lastRenderX + offset.getX(); - prevPosY = e.lastRenderY + offset.getY(); - prevPosZ = e.lastRenderZ + offset.getZ(); + prevPosX = e.lastRenderX + offset.getX(); + prevPosY = e.lastRenderY + offset.getY(); + prevPosZ = e.lastRenderZ + offset.getZ(); + } }, this::detach); if (steps-- > 0) { diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityModel.java new file mode 100644 index 00000000..78a19026 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityModel.java @@ -0,0 +1,42 @@ +package com.minelittlepony.unicopia.client.render.entity; + +import com.minelittlepony.unicopia.entity.CrystalShardsEntity; + +import net.minecraft.client.model.Dilation; +import net.minecraft.client.model.ModelData; +import net.minecraft.client.model.ModelPart; +import net.minecraft.client.model.ModelPartBuilder; +import net.minecraft.client.model.ModelPartData; +import net.minecraft.client.model.ModelTransform; +import net.minecraft.client.model.TexturedModelData; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.entity.model.EntityModel; +import net.minecraft.client.util.math.MatrixStack; + +public class CrystalShardsEntityModel extends EntityModel { + private final ModelPart part; + + public CrystalShardsEntityModel(ModelPart root) { + this.part = root; + } + + public static TexturedModelData getTexturedModelData() { + ModelData data = new ModelData(); + ModelPartData root = data.getRoot(); + root.addChild("west", ModelPartBuilder.create().uv(12, 0).cuboid(-1, -10, -2, 3, 10, 3, Dilation.NONE), ModelTransform.rotation(0.4075F, 0.0378F, 1.0216F)); + root.addChild("north", ModelPartBuilder.create().uv(12, 0).cuboid(-1, -10, -2, 3, 10, 3, Dilation.NONE), ModelTransform.rotation(1.817F, 0.6399F, 2.0582F)); + root.addChild("south", ModelPartBuilder.create().uv(12, 0).cuboid(-2, -9, -2, 3, 10, 3, Dilation.NONE), ModelTransform.rotation(-1.8254F, 0.6819F, -1.3949F)); + root.addChild("east", ModelPartBuilder.create().uv(12, 13).cuboid(-2, -8, -2, 3, 8, 3, Dilation.NONE), ModelTransform.rotation(-0.1151F, 0.3935F, -0.7545F)); + root.addChild("primary", ModelPartBuilder.create().uv(0, 0).cuboid(-2, -20, -2, 3, 20, 3, Dilation.NONE), ModelTransform.rotation(0.2411F, 0.3339F, 0.1819F)); + return TexturedModelData.of(data, 32, 32); + } + + @Override + public void setAngles(CrystalShardsEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + + @Override + public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) { + part.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityRenderer.java new file mode 100644 index 00000000..8f51da70 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/CrystalShardsEntityRenderer.java @@ -0,0 +1,51 @@ +package com.minelittlepony.unicopia.client.render.entity; + +import java.util.List; + +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.CrystalShardsEntity; +import net.minecraft.client.render.OverlayTexture; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.EntityRenderer; +import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.RotationAxis; + +public class CrystalShardsEntityRenderer extends EntityRenderer { + private static final Identifier TEXTURE = Unicopia.id("textures/entity/crystal_shards/normal.png"); + private static final Identifier[] CORRUPTED = List.of("corrupt", "dark", "darker").stream() + .map(name -> Unicopia.id("textures/entity/crystal_shards/" + name + ".png")) + .toArray(Identifier[]::new); + + private final CrystalShardsEntityModel model; + + public CrystalShardsEntityRenderer(EntityRendererFactory.Context context) { + super(context); + model = new CrystalShardsEntityModel(CrystalShardsEntityModel.getTexturedModelData().createModel()); + } + + @Override + public void render(CrystalShardsEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) { + matrices.push(); + matrices.multiply(entity.getAttachmentFace().getRotationQuaternion()); + matrices.scale(-1, -1, 1); + + float scale = entity.getGrowth(tickDelta); + matrices.scale(scale, scale, scale); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw)); + + model.setAngles(entity, 0, 0, 0, 0, 0); + model.render(matrices, vertices.getBuffer(model.getLayer(getTexture(entity))), light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1); + matrices.pop(); + super.render(entity, yaw, tickDelta, matrices, vertices, light); + } + + @Override + public Identifier getTexture(CrystalShardsEntity entity) { + if (entity.isCorrupt()) { + return CORRUPTED[(int)(Math.abs(entity.getUuid().getMostSignificantBits()) % CORRUPTED.length)]; + } + return TEXTURE; + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/SombraEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/SombraEntityModel.java index 9ad47e42..0802edca 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/SombraEntityModel.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/SombraEntityModel.java @@ -19,7 +19,6 @@ public class SombraEntityModel extends EntityModel { private final ModelPart part; private final ModelPart head; - private final ModelPart mane; private final ModelPart upperJaw; private final ModelPart lowerJaw; @@ -28,7 +27,6 @@ public class SombraEntityModel extends EntityModel { public SombraEntityModel(ModelPart root) { this.part = root; this.head = root.getChild("head"); - this.mane = head.getChild("mane"); this.upperJaw = head.getChild("upper_jaw"); this.lowerJaw = head.getChild("lower_jaw"); this.body = root.getChild("body"); @@ -84,27 +82,34 @@ public class SombraEntityModel extends EntityModel { } @Override - public void setAngles(SombraEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + public void animateModel(SombraEntity entity, float limbAngle, float limbDistance, float tickDelta) { + float jawsOpenAmount = entity.getBiteAmount(tickDelta); //(1 - Math.max(0, MathHelper.sin(ageInTicks * 0.1F))); + + lowerJaw.resetTransform(); + lowerJaw.pivotY -= jawsOpenAmount * 3; + lowerJaw.pivotX -= jawsOpenAmount * 3; + lowerJaw.roll += jawsOpenAmount - 0.9F; + + upperJaw.resetTransform(); + upperJaw.roll -= jawsOpenAmount * 0.2F; + } + + @Override + public void setAngles(SombraEntity entity, float limbAngle, float limbDistance, float animationProgress, float netHeadYaw, float headPitch) { + float scale = 1.6F; + + part.xScale = scale; + part.yScale = scale; + part.zScale = scale; part.yaw = -MathHelper.HALF_PI; - part.pivotY = MathHelper.sin(ageInTicks * 0.05F) - 3; - part.pivotZ = MathHelper.cos(ageInTicks * 0.045F); - - //part.yaw = (float)entity.getVelocity().getX(); + part.pivotY = MathHelper.sin(animationProgress * 0.05F) - 12; + part.pivotZ = MathHelper.cos(animationProgress * 0.045F); head.pitch = headPitch * MathHelper.RADIANS_PER_DEGREE; head.yaw = netHeadYaw * MathHelper.RADIANS_PER_DEGREE; - lowerJaw.resetTransform(); - float jawsOpenAmount = (1 - Math.max(0, MathHelper.sin(ageInTicks * 0.1F))); - lowerJaw.pivotY -= jawsOpenAmount * 3; - lowerJaw.pivotX -= jawsOpenAmount * 3; - lowerJaw.roll += jawsOpenAmount - 0.9F; - - upperJaw.resetTransform(); - upperJaw.roll -= jawsOpenAmount * 0.2F; - - body.roll = limbSwingAmount * 0.3F; + body.roll = limbDistance * 0.3F; } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/StormCloudEntityModel.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/StormCloudEntityModel.java new file mode 100644 index 00000000..8b3b25fe --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/StormCloudEntityModel.java @@ -0,0 +1,138 @@ +package com.minelittlepony.unicopia.client.render.entity; + +import java.util.List; + +import org.joml.Vector3f; + +import com.minelittlepony.unicopia.entity.StormCloudEntity; + +import net.minecraft.client.model.Dilation; +import net.minecraft.client.model.ModelData; +import net.minecraft.client.model.ModelPart; +import net.minecraft.client.model.ModelPartBuilder; +import net.minecraft.client.model.ModelPartData; +import net.minecraft.client.model.ModelTransform; +import net.minecraft.client.model.TexturedModelData; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.entity.model.EntityModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.random.Random; + +public class StormCloudEntityModel extends EntityModel { + private final ModelPart part; + + private final ModelPart smallPuffs; + private final List smallPuffCubes; + + private final ModelPart anvilHeads; + private final List anvilHeadCubes; + + private final ModelPart puff; + + private final Random rng = Random.create(0); + private final Vector3f puffLocation = new Vector3f(); + + public StormCloudEntityModel(ModelPart root) { + super(RenderLayer::getEntityTranslucent); + this.part = root; + this.smallPuffs = part.getChild("small_puffs"); + this.smallPuffCubes = smallPuffs.traverse().toList(); + this.anvilHeads = part.getChild("anvil_heads"); + this.anvilHeadCubes = anvilHeads.traverse().toList(); + this.puff = part.getChild("puff"); + } + + public static TexturedModelData getTexturedModelData() { + ModelData data = new ModelData(); + ModelPartData root = data.getRoot(); + ModelPartData small_puffs = root.addChild("small_puffs", ModelPartBuilder.create(), ModelTransform.pivot(0, 24, 0)); + + small_puffs.addChild("cube_r1", ModelPartBuilder.create() + .uv(0, 37).cuboid(-2, -20, -20, 7, 17, 20, Dilation.NONE) + .uv(0, 37).cuboid(-2, -13, -40, 7, 17, 20, Dilation.NONE), ModelTransform.of(0, -6, 0, 0, 0, 1.5708F)); + + small_puffs.addChild("cube_r2", ModelPartBuilder.create().uv(0, 37).cuboid(-2, -7, -25, 7, 17, 20, Dilation.NONE), ModelTransform.of(0, -6, 0, 0.1745F, 0, 1.5708F)); + small_puffs.addChild("cube_r3", ModelPartBuilder.create().uv(0, 37).cuboid(-7, -7, -7, 7, 17, 20, Dilation.NONE), ModelTransform.of(-15, -1, 15, 0.2182F, 0, 1.5708F)); + small_puffs.addChild("cube_r4", ModelPartBuilder.create().cuboid(-13, -14, -31, 7, 17, 20, Dilation.NONE), ModelTransform.of(2, -2, 19, 0, 0, 1.6581F)); + small_puffs.addChild("cube_r5", ModelPartBuilder.create().uv(0, 37).cuboid(-6, -17, -6, 7, 17, 20, Dilation.NONE), ModelTransform.of(2, -2, 19, -0.3491F, 0, 1.5708F)); + small_puffs.addChild("cube_r6", ModelPartBuilder.create().cuboid(-3.5F, -4.5F, -23, 7, 17, 20, Dilation.NONE), ModelTransform.of(0.4471F, -12.1925F, 36, 0, 0, -3.1416F)); + small_puffs.addChild("cube_r7", ModelPartBuilder.create().uv(0, 37).cuboid(-6, -17, -12, 7, 17, 20, Dilation.NONE), ModelTransform.of(-15, -2, 26, -0.829F, 0, 1.5708F)); + small_puffs.addChild("cube_r8", ModelPartBuilder.create() + .uv(0, 37).cuboid(-6, -20, -21, 7, 17, 20, Dilation.NONE) + .cuboid(-13, -20, -40, 7, 17, 20, Dilation.NONE), ModelTransform.of(-15, -2, 26, 0, 0, 1.5708F)); + + small_puffs.addChild("cube_r9", ModelPartBuilder.create().cuboid(-15, -13, -18, 7, 17, 20, Dilation.NONE), ModelTransform.of(-15, -1, 15, 0, 0, 1.4835F)); + small_puffs.addChild("cube_r10", ModelPartBuilder.create().uv(0, 37).cuboid(-7, -3, -25, 7, 17, 20, Dilation.NONE), ModelTransform.of(-15, -1, 15, 0, 0, 1.5708F)); + small_puffs.addChild("cube_r11", ModelPartBuilder.create().uv(0, 37).cuboid(-2, -4, -7, 7, 17, 20, Dilation.NONE), ModelTransform.of(0, -6, 0, -0.0873F, 0, 1.5708F)); + small_puffs.addChild("cube_r12", ModelPartBuilder.create().cuboid(-9.5F, -8.5F, -10, 7, 17, 20, Dilation.NONE), ModelTransform.of(8.842F, -13.6476F, -20.7083F, 0.1739F, 0.0151F, -0.0423F)); + small_puffs.addChild("cube_r13", ModelPartBuilder.create().uv(0, 37).cuboid(-2, -19, -6, 7, 17, 20, Dilation.NONE), ModelTransform.of(0, -6, 0, -0.3043F, 0, 1.5708F)); + + ModelPartData anvil_heads = root.addChild("anvil_heads", ModelPartBuilder.create(), ModelTransform.pivot(-2, 1, 0)); + anvil_heads.addChild("cube_r14", ModelPartBuilder.create().cuboid(-3.5F, -1.5F, -4, 7, 17, 20, new Dilation(2)), ModelTransform.of(-5.5F, -4.5F, 16, -0.3927F, 0, 0)); + anvil_heads.addChild("cube_r15", ModelPartBuilder.create().cuboid(-2, -18, 4, 7, 17, 20, new Dilation(5)), ModelTransform.of(0, -6, 0, -0.1309F, 0, 0)); + anvil_heads.addChild("cube_r16", ModelPartBuilder.create().cuboid(0, -1.5F, -12.5F, 7, 17, 20, new Dilation(5)), ModelTransform.of(0, -6, 0, 0.3491F, 0, 0)); + anvil_heads.addChild("cube_r17", ModelPartBuilder.create().cuboid(7, 9, 0, 7, 17, 20, new Dilation(2)), ModelTransform.of(0, -6, 0, 0.2618F, 0, 0)); + anvil_heads.addChild("cube_r18", ModelPartBuilder.create().cuboid(-12, 3, -9, 7, 17, 20, new Dilation(2)), ModelTransform.of(0, -6, 0, 0.3927F, 0, 0)); + + root.addChild("puff", ModelPartBuilder.create() + .uv(0, 77).cuboid(-6, -11, -5, 18, 5, 9, Dilation.NONE) + .uv(0, 80).cuboid(-8, -10, 4, 14, 4, 6, Dilation.NONE) + .uv(0, 74).cuboid(-2, -10, -11, 12, 5, 11, Dilation.NONE) + .uv(7, 77).cuboid(-14, -11, -10, 12, 5, 9, Dilation.NONE) + .uv(0, 79).cuboid(-2, -13, 0, 8, 3, 6, Dilation.NONE), ModelTransform.of(-5, 14, -20, 0, 1.5272F, 0)); + + return TexturedModelData.of(data, 64, 128); + } + + @Override + public void setAngles(StormCloudEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + float dir = 1; + float globalScale = 0.012F; + + float roll = MathHelper.cos(entity.age / 10F) * globalScale; + float flop = MathHelper.sin(entity.age / 30F) * 0.02F * globalScale; + + for (ModelPart swirl : smallPuffCubes) { + swirl.resetTransform(); + dir = -dir; + swirl.yaw += roll * 0.01F * dir; + } + + for (ModelPart swirl : anvilHeadCubes) { + swirl.resetTransform(); + dir = -dir; + swirl.pitch += flop + roll * -0.01F * dir; + swirl.yaw += roll * 0.01F * dir; + swirl.roll += -flop + roll * 0.01F * dir; + } + + part.pivotY = MathHelper.sin(entity.age * 0.25F) * 0.03F * globalScale; + part.pivotX = MathHelper.cos(entity.age * 0.05125F) * 0.7F * globalScale; + part.pivotZ = MathHelper.sin(entity.age * 0.05125F) * 0.7F * globalScale; + + rng.setSeed(entity.getId()); + } + + @Override + public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) { + matrices.push(); + part.rotate(matrices); + smallPuffs.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); + anvilHeads.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); + + + int puffCount = rng.nextInt(7); + for (int i = 0; i < puffCount; i++) { + puff.resetTransform(); + puff.translate(puffLocation.set( + rng.nextGaussian(), + rng.nextGaussian(), + rng.nextGaussian() + ).mul(16)); + puff.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha); + } + matrices.pop(); + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/StormCloudEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/StormCloudEntityRenderer.java new file mode 100644 index 00000000..467c64ed --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/StormCloudEntityRenderer.java @@ -0,0 +1,53 @@ +package com.minelittlepony.unicopia.client.render.entity; + +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.StormCloudEntity; + +import net.minecraft.client.render.Frustum; +import net.minecraft.client.render.OverlayTexture; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.EntityRenderer; +import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.RotationAxis; + +public class StormCloudEntityRenderer extends EntityRenderer { + private static final Identifier TEXTURE = Unicopia.id("textures/entity/storm_cloud.png"); + + private final StormCloudEntityModel model; + + public StormCloudEntityRenderer(EntityRendererFactory.Context context) { + super(context); + model = new StormCloudEntityModel(StormCloudEntityModel.getTexturedModelData().createModel()); + } + + @Override + public void render(StormCloudEntity entity, float yaw, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light) { + matrices.push(); + matrices.scale(-1, -1, 1); + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yaw)); + + float scale = entity.getSize(tickDelta); + + matrices.scale(scale, scale, scale); + matrices.translate(0, -1.45F, 0); + + model.setAngles(entity, 0, 0, 0, 0, 0); + model.render(matrices, vertices.getBuffer(model.getLayer(getTexture(entity))), + entity.isStormy() ? 0 : light, + OverlayTexture.DEFAULT_UV, 1, 1, 1, 0.9F); + matrices.pop(); + super.render(entity, yaw, tickDelta, matrices, vertices, light); + } + + @Override + public Identifier getTexture(StormCloudEntity entity) { + return TEXTURE; + } + + @Override + public boolean shouldRender(StormCloudEntity entity, Frustum frustum, double x, double y, double z) { + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/entity/ArenaCombatant.java b/src/main/java/com/minelittlepony/unicopia/entity/ArenaCombatant.java new file mode 100644 index 00000000..e8ba6e7e --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/ArenaCombatant.java @@ -0,0 +1,16 @@ +package com.minelittlepony.unicopia.entity; + +import java.util.Optional; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; + +public interface ArenaCombatant { + Optional getHomePos(); + + default float getAreaRadius() { + return 30; + } + + boolean teleportTo(Vec3d destination); +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/CrystalShardsEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/CrystalShardsEntity.java new file mode 100644 index 00000000..a9287462 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/CrystalShardsEntity.java @@ -0,0 +1,208 @@ +package com.minelittlepony.unicopia.entity; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.item.UItems; + +import net.minecraft.block.SideShapeType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.data.DataTracker; +import net.minecraft.entity.data.TrackedData; +import net.minecraft.entity.data.TrackedDataHandlerRegistry; +import net.minecraft.fluid.Fluids; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; + +public class CrystalShardsEntity extends Entity { + static final byte SHAKE = 1; + + static final int FULL_GROWTH_AGE = 25; + + private static final Set ALL_DIRECTIONS = Set.of(Direction.values()); + private static final TrackedData ATTACHMENT_FACE = DataTracker.registerData(SombraEntity.class, TrackedDataHandlerRegistry.FACING); + private static final TrackedData GROWTH = DataTracker.registerData(SombraEntity.class, TrackedDataHandlerRegistry.INTEGER); + private static final TrackedData DECAYING = DataTracker.registerData(SombraEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + private static final TrackedData CORRUPT = DataTracker.registerData(SombraEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + + public static boolean infestBlock(ServerWorld world, BlockPos pos) { + if (world.isAir(pos) || !world.getFluidState(pos).isOf(Fluids.EMPTY)) { + return false; + } + boolean success = false; + for (Direction face : getFreeFaces(world, pos)) { + CrystalShardsEntity shards = UEntities.CRYSTAL_SHARDS.create(world); + shards.setPosition(pos.offset(face).toCenterPos()); + shards.setAttachmentFace(face); + shards.setYaw(world.getRandom().nextFloat() * 360); + shards.setCorrupt(true); + + world.spawnEntity(shards); + success = true; + } + return success; + } + + public static Set getFreeFaces(World world, BlockPos pos) { + Set freeFaces = new HashSet<>(ALL_DIRECTIONS); + freeFaces.removeAll(getOccupiedFaces(world, pos)); + freeFaces.removeIf(face -> isInvalid(world, pos.offset(face), face)); + return freeFaces; + } + + public static Set getOccupiedFaces(World world, BlockPos pos) { + return world.getEntitiesByClass(CrystalShardsEntity.class, new Box(pos).expand(1), EntityPredicates.VALID_ENTITY) + .stream() + .map(e -> e.getAttachmentFace()) + .distinct() + .collect(Collectors.toSet()); + } + + static boolean isInvalid(World world, BlockPos crystalPos, Direction attachmentFace) { + if (!world.isAir(crystalPos)) { + return true; + } + BlockPos attachmentPos = crystalPos.offset(attachmentFace.getOpposite()); + return !world.getBlockState(attachmentPos).isSideSolid(world, attachmentPos, attachmentFace, SideShapeType.RIGID); + } + + private int prevAge; + + private int ticksShaking; + + public CrystalShardsEntity(EntityType type, World world) { + super(type, world); + } + + @Override + protected void initDataTracker() { + dataTracker.startTracking(ATTACHMENT_FACE, Direction.UP); + dataTracker.startTracking(GROWTH, 0); + dataTracker.startTracking(DECAYING, false); + dataTracker.startTracking(CORRUPT, false); + } + + public float getGrowth(float tickDelta) { + int age = getGrowth(); + float lerped = MathHelper.clamp(MathHelper.lerp(tickDelta, prevAge, age), 0, FULL_GROWTH_AGE) / (float)FULL_GROWTH_AGE; + return lerped * 2 * (1 + Math.abs(getUuid().getLeastSignificantBits() % 20) / 20F); + } + + public int getGrowth() { + return dataTracker.get(GROWTH); + } + + public void setGrowth(int growth) { + dataTracker.set(GROWTH, Math.max(0, growth)); + } + + public void setDecaying(boolean decaying) { + dataTracker.set(DECAYING, decaying); + } + + public boolean isDecaying() { + return dataTracker.get(DECAYING); + } + public void setCorrupt(boolean corrupted) { + dataTracker.set(CORRUPT, corrupted); + } + + public boolean isCorrupt() { + return dataTracker.get(CORRUPT); + } + + public boolean isShaking() { + return ticksShaking > 0; + } + + public Direction getAttachmentFace() { + return Objects.requireNonNullElse(dataTracker.get(ATTACHMENT_FACE), Direction.UP); + } + + public void setAttachmentFace(Direction face) { + dataTracker.set(ATTACHMENT_FACE, face); + } + + @Override + public void tick() { + prevAge = getGrowth(); + if (!getWorld().isClient) { + int growAmount = 1 + Math.abs((int)getUuid().getLeastSignificantBits() % 5); + setGrowth(prevAge + (isDecaying() ? -growAmount : growAmount)); + } + setFireTicks(0); + + if (ticksShaking > 0) { + ticksShaking--; + } + + setPosition(getBlockPos().toCenterPos()); + + super.tick(); + + if (getGrowth() < FULL_GROWTH_AGE) { + playSound(USounds.Vanilla.BLOCK_AMETHYST_BLOCK_CHIME, 1, 1); + } + + if (isInvalid(getWorld(), getBlockPos(), getAttachmentFace())) { + kill(); + } + } + + @Override + public boolean damage(DamageSource source, float amount) { + getWorld().sendEntityStatus(this, SHAKE); + return super.damage(source, amount); + } + + @Override + public void remove(RemovalReason reason) { + if (reason == RemovalReason.KILLED) { + playSound(USounds.Vanilla.BLOCK_AMETHYST_BLOCK_BREAK, 1, 1); + dropStack(new ItemStack(UItems.CRYSTAL_SHARD, 6)); + } + super.remove(reason); + } + + @Override + public void handleStatus(byte status) { + switch (status) { + case SHAKE: + ticksShaking = 30; + break; + default: + super.handleStatus(status); + } + } + + @Override + public void writeCustomDataToNbt(NbtCompound nbt) { + nbt.putFloat("yaw", this.getYaw()); + nbt.putInt("growth", getGrowth()); + nbt.putString("face", getAttachmentFace().getName()); + nbt.putBoolean("decaying", isDecaying()); + nbt.putBoolean("corrupt", isCorrupt()); + } + + @Override + public void readCustomDataFromNbt(NbtCompound nbt) { + setYaw(nbt.getFloat("yaw")); + setGrowth(nbt.getInt("growth")); + setAttachmentFace(Direction.byName(nbt.getString("face"))); + setDecaying(nbt.getBoolean("decaying")); + setCorrupt(nbt.getBoolean("corrupt")); + prevAge = getGrowth(); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/SombraEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/SombraEntity.java index dea032fc..a091b94f 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/SombraEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/SombraEntity.java @@ -1,28 +1,41 @@ package com.minelittlepony.unicopia.entity; import java.util.Optional; +import java.util.function.Predicate; +import java.util.stream.Stream; import org.jetbrains.annotations.Nullable; +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.entity.ai.ArenaAttackGoal; import com.minelittlepony.unicopia.item.AmuletItem; import com.minelittlepony.unicopia.item.UItems; +import com.minelittlepony.unicopia.particle.ParticleHandle; +import com.minelittlepony.unicopia.particle.ParticleHandle.Attachment; +import com.minelittlepony.unicopia.particle.ParticleSource; +import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.SphereParticleEffect; import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.util.VecHelper; +import com.minelittlepony.unicopia.util.shape.Sphere; import net.minecraft.block.BlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.ai.goal.ActiveTargetGoal; -import net.minecraft.entity.ai.goal.AttackGoal; +import net.minecraft.entity.ai.goal.FlyGoal; +import net.minecraft.entity.ai.goal.LongDoorInteractGoal; import net.minecraft.entity.ai.goal.LookAroundGoal; import net.minecraft.entity.ai.goal.LookAtEntityGoal; import net.minecraft.entity.ai.goal.PounceAtTargetGoal; import net.minecraft.entity.ai.goal.RevengeGoal; import net.minecraft.entity.ai.goal.WanderAroundGoal; +import net.minecraft.entity.ai.pathing.BirdPathNodeMaker; import net.minecraft.entity.ai.pathing.EntityNavigation; import net.minecraft.entity.ai.pathing.MobNavigation; +import net.minecraft.entity.ai.pathing.PathNodeNavigator; +import net.minecraft.entity.ai.pathing.PathNodeType; import net.minecraft.entity.attribute.DefaultAttributeContainer; import net.minecraft.entity.attribute.EntityAttributes; import net.minecraft.entity.boss.BossBar; @@ -41,20 +54,26 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtHelper; +import net.minecraft.particle.ParticleEffect; import net.minecraft.particle.ParticleTypes; import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.registry.tag.*; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundEvent; import net.minecraft.sound.SoundEvents; import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraft.world.event.GameEvent; -public class SombraEntity extends HostileEntity { +public class SombraEntity extends HostileEntity implements ArenaCombatant, ParticleSource { + static final byte BITE = 70; + static final int MAX_BITE_TIME = 20; + static final Predicate EFFECT_TARGET_PREDICATE = EntityPredicates.VALID_LIVING_ENTITY.and(EntityPredicates.EXCEPT_CREATIVE_OR_SPECTATOR); private static final TrackedData> HOME_POS = DataTracker.registerData(SombraEntity.class, TrackedDataHandlerRegistry.OPTIONAL_BLOCK_POS); @@ -62,6 +81,21 @@ public class SombraEntity extends HostileEntity { .setDarkenSky(true) .setThickenFog(true); + private final ParticleHandle shroud = new ParticleHandle(); + + final EntityReference stormCloud = new EntityReference<>(); + + private int prevBiteTime; + private int biteTime; + + public static void startEncounter(World world, BlockPos pos) { + StormCloudEntity cloud = UEntities.STORM_CLOUD.create(world); + cloud.setPosition(pos.up(10).toCenterPos()); + cloud.setSize(1); + cloud.cursed = true; + world.spawnEntity(cloud); + } + public SombraEntity(EntityType type, World world) { super(type, world); bossBar.setStyle(BossBar.Style.NOTCHED_10); @@ -70,7 +104,13 @@ public class SombraEntity extends HostileEntity { public static DefaultAttributeContainer.Builder createMobAttributes() { return HostileEntity.createMobAttributes() .add(EntityAttributes.GENERIC_MAX_HEALTH, 2000) - .add(EntityAttributes.GENERIC_ATTACK_DAMAGE, 102); + .add(EntityAttributes.GENERIC_ATTACK_KNOCKBACK, 1.5) + .add(EntityAttributes.GENERIC_ATTACK_DAMAGE, 22); + } + + @Override + public SombraEntity asEntity() { + return this; } @Override @@ -101,27 +141,37 @@ public class SombraEntity extends HostileEntity { @Override protected void initGoals() { - goalSelector.add(5, new WanderAroundGoal(this, 1)); - goalSelector.add(6, new LookAtEntityGoal(this, PlayerEntity.class, 8F)); + goalSelector.add(2, new LongDoorInteractGoal(this, true)); + goalSelector.add(5, new FlyGoal(this, 1)); + goalSelector.add(6, new LookAtEntityGoal(this, PlayerEntity.class, 18F)); goalSelector.add(7, new LookAroundGoal(this)); - goalSelector.add(8, new PounceAtTargetGoal(this, 0.3f)); - goalSelector.add(8, new AttackGoal(this)); + goalSelector.add(7, new WanderAroundGoal(this, 1)); + goalSelector.add(8, new PounceAtTargetGoal(this, 1.3F)); + goalSelector.add(8, new ArenaAttackGoal<>(this)); targetSelector.add(1, new RevengeGoal(this)); targetSelector.add(2, new ActiveTargetGoal<>(this, PlayerEntity.class, false)); targetSelector.add(3, new ActiveTargetGoal<>(this, MerchantEntity.class, false)); targetSelector.add(3, new ActiveTargetGoal<>(this, IronGolemEntity.class, true)); } - @Override protected EntityNavigation createNavigation(World world) { - MobNavigation nav = new MobNavigation(this, world); + MobNavigation nav = new MobNavigation(this, world) { + @Override + protected PathNodeNavigator createPathNodeNavigator(int range) { + nodeMaker = new BirdPathNodeMaker(); + nodeMaker.setCanEnterOpenDoors(true); + return new PathNodeNavigator(nodeMaker, range); + } + }; nav.setCanPathThroughDoors(true); nav.setCanSwim(true); nav.setCanEnterOpenDoors(true); + nav.canJumpToNext(PathNodeType.UNPASSABLE_RAIL); return nav; } + @Override public Optional getHomePos() { return dataTracker.get(HOME_POS); } @@ -130,9 +180,14 @@ public class SombraEntity extends HostileEntity { dataTracker.set(HOME_POS, Optional.of(pos)); } + public float getBiteAmount(float tickDelta) { + float progress = (MathHelper.lerp(tickDelta, prevBiteTime, biteTime) / (float)MAX_BITE_TIME); + return 1 - Math.abs(MathHelper.sin(progress * MathHelper.PI)); + } + @Override public void tick() { - + setPersistent(); Optional homePos = getHomePos(); if (homePos.isEmpty() && !isRemoved()) { @@ -140,59 +195,128 @@ public class SombraEntity extends HostileEntity { return; } - if (this.getBlockPos().getSquaredDistance(homePos.get()) > 16) { + if (getBlockPos().getSquaredDistance(homePos.get()) > MathHelper.square(getAreaRadius())) { teleportTo(Vec3d.ofCenter(homePos.get())); - setTarget(null); + getNavigation().stop(); + } + + prevBiteTime = biteTime; + if (biteTime > 0) { + biteTime--; } super.tick(); - addVelocity(0, 0.002F, 0); + if (getTarget() == null && getVelocity().y < 0) { + setVelocity(getVelocity().multiply(1, 0.4, 1)); + } + addVelocity(0, 0.0242F, 0); if (isSubmergedInWater()) { jump(); } - if (age % 50 == 0) { - playSound(SoundEvents.ENTITY_POLAR_BEAR_AMBIENT, 3, 0.3F); + if (random.nextInt(1200) == 0) { + playSound(USounds.ENTITY_SOMBRA_LAUGH, 1, 1); + getWorld().sendEntityStatus(this, BITE); } - if (age % 125 == 0) { + if (random.nextInt(340) == 0) { playSound(SoundEvents.AMBIENT_CAVE.value(), 1, 0.3F); + } else if (random.nextInt(1340) == 0) { + playSound(USounds.ENTITY_SOMBRA_AMBIENT, 1, 1); } if (getWorld().isClient) { - float range = 9; - for (int i = 0; i < 3; i++) { - var particle = new SphereParticleEffect(UParticles.SPHERE, - 0x222222, - 0.7F, - (float)getWorld().getRandom().nextTriangular(2.5F, 0.7F) - ); - getWorld().addParticle(particle, - getWorld().getRandom().nextTriangular(getX(), range), - getWorld().getRandom().nextTriangular(getY(), range), - getWorld().getRandom().nextTriangular(getZ(), range), - getWorld().getRandom().nextGaussian() / 6F, - getWorld().getRandom().nextGaussian() / 6F, - getWorld().getRandom().nextGaussian() / 6F - ); + generateBodyParticles(); + } else { + for (BlockPos p : BlockPos.iterateOutwards(getBlockPos(), 2, 1, 2)) { + if (getWorld().getBlockState(p).getLuminance() > 3) { + destroyLightSource(p); + } } - for (int i = 0; i < 13; i++) { - getWorld().addParticle(ParticleTypes.LARGE_SMOKE, - getWorld().getRandom().nextTriangular(getX(), 1), - getWorld().getRandom().nextTriangular(getY(), 1), - getWorld().getRandom().nextTriangular(getZ(), 1), - 0, - 0, - 0 - ); + for (BlockPos p : BlockPos.iterateRandomly(random, 3, getBlockPos(), 20)) { + CrystalShardsEntity.infestBlock((ServerWorld)getWorld(), p); + } + + if (getTarget() == null && getNavigation().isIdle()) { + getNavigation().startMovingTo(homePos.get().getX(), homePos.get().getY() + 10, homePos.get().getZ(), 2); } } - getWorld().getOtherEntities(this, this.getBoundingBox().expand(5), EntityPredicates.VALID_LIVING_ENTITY).forEach(target -> { - ((LivingEntity)target).addStatusEffect(new StatusEffectInstance(StatusEffects.BLINDNESS, 100, 1)); - }); + getHomePos().ifPresent(this::generateArenaEffects); + } + + protected void applyAreaEffects(Entity target) { + if (this.age % 150 == 0) { + target.playSound( + random.nextInt(30) == 0 ? USounds.ENTITY_SOMBRA_AMBIENT + : random.nextInt(10) == 0 ? USounds.Vanilla.ENTITY_GHAST_AMBIENT + : USounds.Vanilla.AMBIENT_CAVE.value(), + (float)random.nextTriangular(1, 0.2F), + (float)random.nextTriangular(0.3F, 0.2F) + ); + } + + ((LivingEntity)target).addStatusEffect(new StatusEffectInstance(StatusEffects.BLINDNESS, 26, 0, true, false)); + ((LivingEntity)target).addStatusEffect(new StatusEffectInstance(StatusEffects.DARKNESS, 26, 0, true, false)); + + if (getTarget() == null && target instanceof PlayerEntity player && player.distanceTo(this) < getAreaRadius() / 2F) { + setTarget(player); + if (teleportTo(target.getPos())) { + setPosition(getPos().add(0, 4, 0)); + } + } + } + + protected void destroyLightSource(BlockPos pos) { + getWorld().breakBlock(pos, true); + playSound(USounds.ENTITY_SOMBRA_SNICKER, 1, 1); + } + + protected void generateBodyParticles() { + for (int i = 0; i < 23; i++) { + getWorld().addParticle(ParticleTypes.LARGE_SMOKE, + random.nextTriangular(getX(), 8), + random.nextTriangular(getY(), 1), + random.nextTriangular(getZ(), 8), + 0, + 0, + 0 + ); + } + } + + private void generateArenaEffects(BlockPos home) { + if (getWorld().isClient()) { + Stream.concat( + new Sphere(false, getAreaRadius()).translate(home).randomPoints(random).filter(this::isSurfaceBlock).limit(80), + new Sphere(true, getAreaRadius()).translate(home).randomPoints(random).filter(this::isSurfaceBlock).limit(30)) + .forEach(pos -> { + ParticleEffect type = random.nextInt(3) < 1 ? ParticleTypes.LARGE_SMOKE : ParticleTypes.SOUL_FIRE_FLAME; + ParticleUtils.spawnParticle(getWorld(), type, pos, Vec3d.ZERO); + ParticleUtils.spawnParticle(getWorld(), type, pos, pos.subtract(getPos()).add(0, 0.1, 0).multiply(-0.013)); + }); + + shroud.update(getUuid(), this, spawner -> { + var radius = getAreaRadius(); + var center = home.toCenterPos(); + spawner.addParticle(new SphereParticleEffect(UParticles.SPHERE, 0xFF000000, 1, radius - 0.2F), center, Vec3d.ZERO); + }).ifPresent(attachment -> { + attachment.setAttribute(Attachment.ATTR_BOUND, 1); + attachment.setAttribute(Attachment.ATTR_COLOR, 0xFF000000); + attachment.setAttribute(Attachment.ATTR_OPACITY, 2.5F); + }); + } else { + for (Entity target : VecHelper.findInRange(this, getWorld(), home.toCenterPos(), getAreaRadius() - 0.2F, EFFECT_TARGET_PREDICATE)) { + applyAreaEffects(target); + } + } + } + + private boolean isSurfaceBlock(Vec3d pos) { + BlockPos bPos = BlockPos.ofFloored(pos); + return getWorld().isAir(bPos) && !getWorld().isAir(bPos.down()); } @Override @@ -207,21 +331,6 @@ public class SombraEntity extends HostileEntity { return distance < d * d; } - @Override - public boolean isPushable() { - return false; - } - - @Override - protected void pushAway(Entity entity) { - - } - - @Override - protected void tickCramming() { - - } - @Override public boolean handleFallDamage(float distance, float damageMultiplier, DamageSource cause) { return false; @@ -232,6 +341,7 @@ public class SombraEntity extends HostileEntity { if (source.getAttacker() instanceof PlayerEntity player) { if (AmuletSelectors.ALICORN_AMULET.test(player)) { if (!getWorld().isClient) { + playSound(USounds.ENTITY_SOMBRA_SNICKER, 1, 1); player.sendMessage(Text.translatable("entity.unicopia.sombra.taunt")); } } @@ -249,10 +359,47 @@ public class SombraEntity extends HostileEntity { return damaged; } + @Override + public void onDeath(DamageSource damageSource) { + if (!dead) { + stormCloud.ifPresent(getWorld(), cloud -> { + cloud.setStormTicks(0); + cloud.setDissipating(true); + }); + stormCloud.set(null); + getHomePos().ifPresent(home -> { + VecHelper.findInRange(this, getWorld(), home.toCenterPos(), getAreaRadius() - 0.2F, e -> e instanceof CrystalShardsEntity).forEach(e -> { + ((CrystalShardsEntity)e).setDecaying(true); + }); + }); + } + super.onDeath(damageSource); + } + @Override protected void fall(double y, boolean onGroundIn, BlockState state, BlockPos pos) { } + @Override + public boolean canTarget(LivingEntity target) { + return super.canTarget(target) && getHomePos().filter(home -> target.getPos().isInRange(home.toCenterPos(), getAreaRadius())).isPresent(); + } + + @Override + public boolean tryAttack(Entity target) { + getWorld().sendEntityStatus(this, BITE); + return super.tryAttack(target); + } + + @Override + public void handleStatus(byte status) { + if (status == BITE) { + biteTime = MAX_BITE_TIME; + } else { + super.handleStatus(status); + } + } + protected boolean teleportRandomly(int maxDistance) { if (getWorld().isClient() || !isAlive()) { return false; @@ -260,7 +407,8 @@ public class SombraEntity extends HostileEntity { return teleportTo(getPos().add(VecHelper.supply(() -> random.nextTriangular(0, maxDistance)))); } - private boolean teleportTo(Vec3d destination) { + @Override + public boolean teleportTo(Vec3d destination) { Vec3d oldPos = getPos(); if (canTeleportTo(destination) && teleport(destination.x, destination.y, destination.z, true)) { getWorld().emitGameEvent(GameEvent.TELEPORT, oldPos, GameEvent.Emitter.of(this)); @@ -309,6 +457,7 @@ public class SombraEntity extends HostileEntity { getHomePos().map(NbtHelper::fromBlockPos).ifPresent(pos -> { nbt.put("homePos", pos); }); + nbt.put("cloud", stormCloud.toNBT()); } @Override @@ -320,5 +469,6 @@ public class SombraEntity extends HostileEntity { if (hasCustomName()) { bossBar.setName(getDisplayName()); } + stormCloud.fromNBT(nbt.getCompound("cloud")); } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/StormCloudEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/StormCloudEntity.java new file mode 100644 index 00000000..4c19eaa7 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/StormCloudEntity.java @@ -0,0 +1,317 @@ +package com.minelittlepony.unicopia.entity; + +import java.util.function.Consumer; + +import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.particle.UParticles; +import com.minelittlepony.unicopia.server.world.WeatherConditions; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LightningEntity; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.data.DataTracker; +import net.minecraft.entity.data.TrackedData; +import net.minecraft.entity.data.TrackedDataHandlerRegistry; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.Heightmap.Type; +import net.minecraft.world.World; + +public class StormCloudEntity extends Entity { + private static final TrackedData CLEAR_TICKS = DataTracker.registerData(StormCloudEntity.class, TrackedDataHandlerRegistry.INTEGER); + private static final TrackedData STORM_TICKS = DataTracker.registerData(StormCloudEntity.class, TrackedDataHandlerRegistry.INTEGER); + private static final TrackedData TARGET_SIZE = DataTracker.registerData(StormCloudEntity.class, TrackedDataHandlerRegistry.FLOAT); + private static final TrackedData DISSIPATING = DataTracker.registerData(StormCloudEntity.class, TrackedDataHandlerRegistry.BOOLEAN); + + private float prevSize; + private float currentSize; + + public boolean cursed; + + private int phase; + private int nextPhase; + + public StormCloudEntity(EntityType type, World world) { + super(type, world); + setSize(1 + random.nextInt(4)); + } + + @Override + protected void initDataTracker() { + dataTracker.startTracking(STORM_TICKS, 0); + dataTracker.startTracking(CLEAR_TICKS, 0); + dataTracker.startTracking(TARGET_SIZE, 1F); + dataTracker.startTracking(DISSIPATING, false); + } + + public boolean isStormy() { + return getStormTicks() != 0; + } + + public int getStormTicks() { + return dataTracker.get(STORM_TICKS); + } + + public void setStormTicks(int stormTicks) { + dataTracker.set(STORM_TICKS, stormTicks); + setClearTicks(stormTicks); + } + + public void setDissipating(boolean dissipating) { + dataTracker.set(DISSIPATING, dissipating); + } + + public boolean isDissipating() { + return dataTracker.get(DISSIPATING); + } + + private void setClearTicks(int clearTicks) { + dataTracker.set(CLEAR_TICKS, clearTicks); + } + + private int getClearTicks() { + return dataTracker.get(CLEAR_TICKS); + } + + public boolean tickClear() { + int clearTicks = getClearTicks(); + if (clearTicks > 0) { + setClearTicks(--clearTicks); + } + return clearTicks > 0; + } + + public float getSize(float tickDelta) { + return MathHelper.clamp(MathHelper.lerp(tickDelta, prevSize, currentSize), 1, 30); + } + + public void setSize(float size) { + dataTracker.set(TARGET_SIZE, size); + } + + public int getSizeInBlocks() { + return (int)(getWidth() * (getSize(1) / 15F)); + } + + @Override + public void tick() { + setFireTicks(1); + + + prevSize = currentSize; + float targetSize = dataTracker.get(TARGET_SIZE); + if (currentSize != targetSize) { + float sizeDifference = (dataTracker.get(TARGET_SIZE) - currentSize); + currentSize = Math.abs(sizeDifference) < 0.01F ? targetSize : currentSize + (sizeDifference * 0.2F); + } + + if (isStormy()) { + int stormTicks = getStormTicks(); + if (stormTicks > 0) { + setStormTicks(stormTicks - 1); + } + } + + if (isLogicalSideForUpdatingMovement()) { + float groundY = getWorld().getTopY(Type.MOTION_BLOCKING_NO_LEAVES, (int)getX(), (int)getZ()); + float cloudY = (float)getY() - (isStormy() ? 20 : 90); + + addVelocity(0, 0.03F * (groundY - cloudY), 0); + + if (!cursed && !isStormy()) { + Vec3d wind = WeatherConditions.get(getWorld()).getWindDirection(); + addVelocity(wind.x * 0.001F, 0, wind.z * 0.001F); + } + + Vec3d velocity = getVelocity(); + setPosition(getPos().add(velocity.multiply(0.7F))); + setVelocity(velocity.multiply(0.9F)); + + float randomYaw = random.nextFloat() * 360; + Vec3d randomVelocity = Vec3d.fromPolar(0, randomYaw).multiply(0.002); + + for (var cloud : getWorld().getEntitiesByClass(StormCloudEntity.class, getBoundingBox(), EntityPredicates.VALID_ENTITY)) { + if (cloud != this) { + cloud.addVelocity(randomVelocity); + } + } + } + + super.tick(); + + float size = getSize(1); + + if (getWorld().isClient()) { + if (isStormy()) { + WeatherConditions.get(getWorld()).addStorm(this); + + float sizeInBlocks = getSizeInBlocks(); + int area = (int)MathHelper.square(sizeInBlocks); + sizeInBlocks /= getWidth(); + for (int i = 0; i < area; i++) { + float x = (float)getParticleX(sizeInBlocks); + float z = (float)getParticleZ(sizeInBlocks); + + getWorld().addParticle(UParticles.RAIN_DROPS, x, getY(), z, 0, -0.2F, 0); + } + } + } else { + if (!isStormy() && !cursed && size > 10) { + if (random.nextInt(1 + (int)(4000 / size)) == 0) { + split(random.nextBetween(1, 3)); + return; + } + } + + if (currentSize == targetSize) { + if (isDissipating()) { + if (size < 2) { + kill(); + } else { + if (random.nextInt(4) == 0) { + split(2); + } else { + setSize(size - 1); + } + } + } else { + if (size < 30) { + if (cursed) { + setSize(size + 1); + setStormTicks(1); + spawnLightningStrike(getBlockPos(), false, false); + playSound(USounds.AMBIENT_WIND_GUST, 1, 1); + } + } + } + } + + if (size >= 30) { + if (cursed) { + setStormTicks(-1); + + if (nextPhase-- == 0) { + phase++; + nextPhase = random.nextBetween(17, 120); + + if (phase == 1) { + playSound(USounds.ENTITY_SOMBRA_SNICKER, 10, 1); + } + + if (++phase >= 7) { + pickRandomPoints(13, pos -> spawnLightningStrike(pos, true, false)); + + cursed = false; + SombraEntity sombra = UEntities.SOMBRA.create(getWorld()); + sombra.setPosition(getPos()); + sombra.setHomePos(getWorld().getTopPosition(Type.MOTION_BLOCKING_NO_LEAVES, getBlockPos())); + sombra.stormCloud.set(this); + getWorld().spawnEntity(sombra); + + playSound(USounds.ENTITY_SOMBRA_LAUGH, 10, 1); + } else { + pickRandomPoints(3, pos -> spawnLightningStrike(pos, true, false)); + } + } + } else { + if (!isStormy() && !tickClear()) { + setStormTicks(random.nextBetween(30, 120)); + } + } + } + + if (isStormy() && age % 170 == 0) { + pickRandomPoints(3, pos -> spawnLightningStrike(pos, false, random.nextInt(30) == 0)); + } + } + } + + private void pickRandomPoints(int count, Consumer action) { + BlockPos.iterateRandomly(random, 3, getBlockPos(), getSizeInBlocks()).forEach(pos -> { + + BlockPos.Mutable mutable = new BlockPos.Mutable(); + mutable.set(pos); + while (getWorld().isInBuildLimit(mutable) && getWorld().isAir(mutable)) { + mutable.move(Direction.DOWN); + } + while (getWorld().isInBuildLimit(mutable) && !getWorld().isAir(mutable)) { + mutable.move(Direction.UP); + } + mutable.move(Direction.DOWN); + + action.accept(pos); + }); + } + + private void spawnLightningStrike(BlockPos pos, boolean cosmetic, boolean infect) { + if (infect) { + if (!CrystalShardsEntity.infestBlock((ServerWorld)getWorld(), pos)) { + return; + } + } + LightningEntity lightning = EntityType.LIGHTNING_BOLT.create(getWorld()); + lightning.refreshPositionAfterTeleport(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5); + lightning.setCosmetic(cosmetic); + getWorld().spawnEntity(lightning); + + } + + @Override + public void onDamaged(DamageSource damageSource) { + if (random.nextInt(35) == 0) { + split(2 + random.nextInt(4)); + } + } + + public void split(int splitCount) { + Vec3d center = getPos(); + + float totalAngle = (360F / splitCount) + ((float)random.nextGaussian() * 360F); + float size = getSize(1) / splitCount; + int stormTicks = getStormTicks() / splitCount; + + if (size < 1) { + return; + } + discard(); + + for (int i = 0; i < splitCount; i++) { + StormCloudEntity lump = (StormCloudEntity)getType().create(getWorld()); + lump.setSize(size); + lump.setStormTicks(stormTicks); + lump.setPosition(center); + lump.setDissipating(isDissipating()); + lump.setVelocity(Vec3d.fromPolar(0, totalAngle * i).normalize().multiply(1.8F)); + + getWorld().spawnEntity(lump); + } + + } + + @Override + public void handleStatus(byte status) { + super.handleStatus(status); + } + + @Override + public void writeCustomDataToNbt(NbtCompound nbt) { + nbt.putInt("stormTicks", getStormTicks()); + nbt.putInt("clearTicks", getClearTicks()); + nbt.putFloat("size", getSize(1)); + nbt.putBoolean("cursed", cursed); + } + + @Override + public void readCustomDataFromNbt(NbtCompound nbt) { + setStormTicks(nbt.getInt("stormTicks")); + setClearTicks(nbt.getInt("clearTicks")); + setSize(currentSize = nbt.getFloat("size")); + cursed = nbt.getBoolean("cursed"); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/UEntities.java b/src/main/java/com/minelittlepony/unicopia/entity/UEntities.java index b4f923a5..9c101076 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/UEntities.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/UEntities.java @@ -48,7 +48,13 @@ public interface UEntities { .dimensions(EntityDimensions.fixed(0.9F, 0.5F))); EntityType SOMBRA = register("sombra", FabricEntityTypeBuilder.create(SpawnGroup.MONSTER, SombraEntity::new) .trackRangeBlocks(200) + .dimensions(EntityDimensions.fixed(1F, 2F))); + EntityType CRYSTAL_SHARDS = register("crystal_shards", FabricEntityTypeBuilder.create(SpawnGroup.MISC, CrystalShardsEntity::new) + .trackRangeBlocks(100) .dimensions(EntityDimensions.fixed(1F, 1F))); + EntityType STORM_CLOUD = register("storm_cloud", FabricEntityTypeBuilder.create(SpawnGroup.MISC, StormCloudEntity::new) + .trackRangeBlocks(200) + .dimensions(EntityDimensions.fixed(20F, 20F))); EntityType AIR_BALLOON = register("air_balloon", FabricEntityTypeBuilder.create(SpawnGroup.MISC, AirBalloonEntity::new) .trackRangeBlocks(1000) .dimensions(EntityDimensions.changing(2.5F, 0.1F))); diff --git a/src/main/java/com/minelittlepony/unicopia/entity/ai/ArenaAttackGoal.java b/src/main/java/com/minelittlepony/unicopia/entity/ai/ArenaAttackGoal.java new file mode 100644 index 00000000..1650ef30 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/ai/ArenaAttackGoal.java @@ -0,0 +1,47 @@ +package com.minelittlepony.unicopia.entity.ai; + +import com.minelittlepony.unicopia.entity.ArenaCombatant; + +import net.minecraft.entity.ai.goal.AttackGoal; +import net.minecraft.entity.ai.pathing.Path; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.util.math.BlockPos; + +public class ArenaAttackGoal extends AttackGoal { + + private final E combatant; + + public ArenaAttackGoal(E mob) { + super(mob); + this.combatant = mob; + } + + @Override + public boolean canStart() { + return combatant.getHomePos().isPresent() && super.canStart(); + } + + @Override + public boolean shouldContinue() { + return combatant.getHomePos().filter(this::isInArena).isPresent() && super.shouldContinue(); + } + + private boolean isInArena(BlockPos homePos) { + return combatant.getBlockPos().isWithinDistance(homePos, combatant.getAreaRadius()); + } + + @Override + public void stop() { + super.stop(); + combatant.setTarget(null); + combatant.getHomePos().ifPresent(home -> { + + Path path = combatant.getNavigation().findPathTo(home, 2, (int)combatant.getAreaRadius() * 2); + if (path != null) { + combatant.getNavigation().startMovingAlong(path, combatant.getMovementSpeed() * 2F); + } else { + combatant.teleportTo(home.toCenterPos()); + } + }); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/MeteorlogicalUtil.java b/src/main/java/com/minelittlepony/unicopia/entity/player/MeteorlogicalUtil.java index 298e41e4..b5d028e2 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/MeteorlogicalUtil.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/MeteorlogicalUtil.java @@ -21,7 +21,6 @@ public interface MeteorlogicalUtil { return false; } - final float skyAngle = getSkyAngle(entity.getWorld()); float playerYaw = MathHelper.wrapDegrees(entity.getHeadYaw()); float playerAngle = (-entity.getPitch(1) / 90F) / 2F; diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index 08373931..bea56177 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -586,6 +586,10 @@ public class Pony extends Living implements Copyable, Update } sendCapabilities(); + + //if (!isClient()) { + // CrystalShardsEntity.infestBlock((ServerWorld)asWorld(), entity.getBlockPos().down()); + //} } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/item/AlicornAmuletItem.java b/src/main/java/com/minelittlepony/unicopia/item/AlicornAmuletItem.java index c42798b0..61c8dc68 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/AlicornAmuletItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/AlicornAmuletItem.java @@ -257,6 +257,11 @@ public class AlicornAmuletItem extends AmuletItem implements ItemTracker.Trackab player.getHungerManager().addExhaustion(90F); float healthDrop = MathHelper.clamp(player.getMaxHealth() - player.getHealth(), 2, 5); player.damage(pony.damageOf(UDamageTypes.ALICORN_AMULET), healthDrop); + + if (player.getHealth() < 2) { + stack.decrement(1); + SombraEntity.startEncounter(player.getWorld(), player.getBlockPos()); + } } return; diff --git a/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java b/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java index fa190703..1fc12c06 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/ParticleHandle.java @@ -8,6 +8,7 @@ import java.util.UUID; import java.util.WeakHashMap; import java.util.function.Consumer; +import com.minelittlepony.unicopia.EntityConvertable; import com.minelittlepony.unicopia.ability.magic.Caster; import net.fabricmc.api.EnvType; @@ -59,8 +60,8 @@ public class ParticleHandle { Entry p = SPAWNED_PARTICLES.computeIfAbsent(id, i -> new WeakHashMap<>()).computeIfAbsent(partName, i -> { constructor.accept((effect, pos, vel) -> { pp = MinecraftClient.getInstance().particleManager.addParticle(effect, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z); - if (pp instanceof Attachment && source instanceof Caster) { - ((Attachment) pp).attach(new Link(id, (Caster)source)); + if (pp instanceof Attachment) { + ((Attachment) pp).attach(new Link(id, source)); } }); return new Entry(new WeakReference<>(MinecraftClient.getInstance().world), new WeakReference<>(pp)); @@ -102,16 +103,16 @@ public class ParticleHandle { } public static final class Link { - private Optional>> caster = Optional.empty(); + private Optional>> caster = Optional.empty(); private UUID effect; - private Link(UUID effect, Caster caster) { + private Link(UUID effect, EntityConvertable caster) { this.caster = Optional.of(new WeakReference<>(caster)); this.effect = effect; } - public Optional> get() { - caster = caster.filter(r -> r.get() != null && r.get().getSpellSlot().contains(effect) && r.get().asEntity().isAlive()); + public Optional> get() { + caster = caster.filter(r -> r.get() != null && (!(r.get() instanceof Caster c) || c.getSpellSlot().contains(effect)) && r.get().asEntity().isAlive()); return caster.map(WeakReference::get); } } diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/WeatherConditions.java b/src/main/java/com/minelittlepony/unicopia/server/world/WeatherConditions.java index 535e3c66..4779e602 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/WeatherConditions.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/WeatherConditions.java @@ -1,6 +1,12 @@ package com.minelittlepony.unicopia.server.world; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.StormCloudEntity; import com.minelittlepony.unicopia.entity.player.MeteorlogicalUtil; import com.minelittlepony.unicopia.util.Tickable; @@ -55,6 +61,8 @@ public class WeatherConditions extends PersistentState implements Tickable { private boolean prevDayState; + private Map storms = new HashMap<>(); + private WeatherConditions(World world, NbtCompound compound) { this(world); windYaw = compound.getFloat("windYaw"); @@ -68,6 +76,19 @@ public class WeatherConditions extends PersistentState implements Tickable { this.world = world; } + public void addStorm(StormCloudEntity cloud) { + synchronized (storms) { + storms.computeIfAbsent(cloud.getUuid(), id -> new Storm(cloud)); + } + } + + public boolean isInRangeOfStorm(BlockPos pos) { + synchronized (storms) { + storms.values().removeIf(Storm::shouldRemove); + return storms.values().stream().anyMatch(storm -> storm.inRange(pos)); + } + } + @Override public void tick() { if (interpolation < maxInterpolation) { @@ -107,6 +128,24 @@ public class WeatherConditions extends PersistentState implements Tickable { return compound; } + private class Storm { + private final WeakReference cloud; + + public Storm(StormCloudEntity cloud) { + this.cloud = new WeakReference<>(cloud); + } + + public boolean inRange(BlockPos pos) { + final StormCloudEntity cloud = this.cloud.get(); + return cloud != null && cloud.getBlockPos().isWithinDistance(pos, cloud.getSizeInBlocks()); + } + + public boolean shouldRemove() { + final StormCloudEntity cloud = this.cloud.get(); + return cloud == null || cloud.isRemoved() || !cloud.isStormy(); + } + } + public static Vec3d getAirflow(BlockPos pos, World world) { BlockPos.Mutable probedPosition = new BlockPos.Mutable(); diff --git a/src/main/resources/assets/unicopia/sounds.json b/src/main/resources/assets/unicopia/sounds.json index 99a11584..bd8cfd55 100644 --- a/src/main/resources/assets/unicopia/sounds.json +++ b/src/main/resources/assets/unicopia/sounds.json @@ -228,6 +228,24 @@ ], "subtitle": "unicopia.subtitle.entity.twittermite.hurt" }, + "entity.sombra.ambient": { + "subtitle": "unicopia.subtitle.sombra.ambient", + "sounds": [ + "unicopia:entity/sombra/sombra_idle" + ] + }, + "entity.sombra.laugh": { + "subtitle": "unicopia.subtitle.sombra.laugh", + "sounds": [ + "unicopia:entity/sombra/sombra_laugh" + ] + }, + "entity.sombra.snicker": { + "subtitle": "unicopia.subtitle.sombra.snicker", + "sounds": [ + "unicopia:entity/sombra/sombra_snicker" + ] + }, "entity.jar.throw": { "sounds": [ "random/bow" diff --git a/src/main/resources/assets/unicopia/sounds/entity/sombra/sombra_idle.ogg b/src/main/resources/assets/unicopia/sounds/entity/sombra/sombra_idle.ogg new file mode 100644 index 00000000..c3f93d0e Binary files /dev/null and b/src/main/resources/assets/unicopia/sounds/entity/sombra/sombra_idle.ogg differ diff --git a/src/main/resources/assets/unicopia/sounds/entity/sombra/sombra_laugh.ogg b/src/main/resources/assets/unicopia/sounds/entity/sombra/sombra_laugh.ogg new file mode 100644 index 00000000..57ec9909 Binary files /dev/null and b/src/main/resources/assets/unicopia/sounds/entity/sombra/sombra_laugh.ogg differ diff --git a/src/main/resources/assets/unicopia/sounds/entity/sombra/sombra_snicker.ogg b/src/main/resources/assets/unicopia/sounds/entity/sombra/sombra_snicker.ogg new file mode 100644 index 00000000..80fbcaf2 Binary files /dev/null and b/src/main/resources/assets/unicopia/sounds/entity/sombra/sombra_snicker.ogg differ diff --git a/src/main/resources/assets/unicopia/textures/entity/crystal_shards/corrupt.png b/src/main/resources/assets/unicopia/textures/entity/crystal_shards/corrupt.png new file mode 100644 index 00000000..c0de4e4b Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/crystal_shards/corrupt.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/crystal_shards/dark.png b/src/main/resources/assets/unicopia/textures/entity/crystal_shards/dark.png new file mode 100644 index 00000000..823984ed Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/crystal_shards/dark.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/crystal_shards/darker.png b/src/main/resources/assets/unicopia/textures/entity/crystal_shards/darker.png new file mode 100644 index 00000000..468cf1ea Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/crystal_shards/darker.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/crystal_shards/normal.png b/src/main/resources/assets/unicopia/textures/entity/crystal_shards/normal.png new file mode 100644 index 00000000..bc34b627 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/crystal_shards/normal.png differ diff --git a/src/main/resources/assets/unicopia/textures/entity/storm_cloud.png b/src/main/resources/assets/unicopia/textures/entity/storm_cloud.png new file mode 100644 index 00000000..2d2021f3 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/entity/storm_cloud.png differ diff --git a/tom.png b/tom.png new file mode 100644 index 00000000..c4e9c580 Binary files /dev/null and b/tom.png differ